Elasticsearch - 如何找出嵌套对象的字段值大于 X 且最大日期的所有文档

Elasticsearch - How to find out all the documents where a field value of a nested object is greater than X with the max date

提问人:gron 提问时间:10/25/2023 更新时间:10/25/2023 访问量:39

问:

我有一些公司模型的文件。每家公司都有一些财务报告。它们被设计为 Elasticsearch 的嵌套对象。例如:

[
  {
    "id": 1,
    "financials": [
      {
        "periodEndDate": "2022-06-30",
        "cash": 10000,
        "inventory": 24000
      },
      {
        "periodEndDate": "2022-12-31",
        "cash": 12000,
        "inventory": 19000
      }
    ]
  },
  {
    "id": 2,
    "financials": [
      {
        "periodEndDate": "2022-12-31",
        "cash": 200000,
        "inventory": 1590000
      },
      {
        "periodEndDate": "2023-03-31",
        "cash": 210000,
        "inventory": 1430000
      }
    ]
  }
]

我想在最近的期间结束日期报告中找出所有现金> 200000 的公司。期望的结果是公司 2,因为它的最新报告是 periodEndDate 为 2023-03-31 的报告,报告的现金是 210000

如何编写查询?

此外,如果我想使用第二个到最近的日期筛选报告,查询是什么?

我找到了这篇文章: ElasticSearch - 获取字段值为 x 且类型的最大日期低于 y 日期的文档 但它需要“否定”过滤器,这对我不起作用,因为财务报告嵌套对象有太多字段。

Java Elasticsearch 嵌套

评论


答:

0赞 imotov 10/25/2023 #1

如果公司数量足够少,您不需要对它们进行分页,则可以使用组合和聚合来完成。基本上,它将找到最新报告,并将删除所有没有足够现金的公司。top_hitsbucket_selectortop_hitsbucket_selector

PUT test
{
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      },
      "financials": {
        "type": "nested",
        "properties": {
          "periodEndDate": {
            "type": "date"
          },
          "cash": {
            "type":"long"
          },
          "inventory": {
            "type": "long"
          }
        }
      }
    }
  }
}

POST test/_bulk?refresh
{"index":{"_id":1}}
{"id":1,"financials":[{"periodEndDate":"2022-06-30","cash":10000,"inventory":24000},{"periodEndDate":"2022-12-31","cash":12000,"inventory":19000}]}
{"index":{"_id":2}}
{"id":2,"financials":[{"periodEndDate":"2022-12-31","cash":200000,"inventory":1590000},{"periodEndDate":"2023-03-31","cash":210000,"inventory":1430000}]}

POST test/_search
{
  "size": 0,
  "aggs": {
    "companies": {
      "terms": {
        "field": "id"
      },
      "aggs": {
        "nested": {
          "nested": {
            "path": "financials"
          },
          "aggs": {
            "last_cash": {
              "top_hits": {
                "sort": [
                  {
                    "financials.periodEndDate": {
                      "order": "desc"
                    }
                  }
                ],
                "_source": {
                  "includes": [
                    "financials.cash",
                    "financials.periodEndDate"
                  ]
                },
                "size": 1
              }
            }
          }
        },
        "having.top_cash": {
          "bucket_selector": {
            "buckets_path": {
              "last_cash": "nested>last_cash[_source.cash]"
            },
            "script": "params.last_cash > 200000"
          }
        }
      }
    }
  }
}

此方法有几个局限性,包括可伸缩性差、可以处理的公司数量有限以及不支持第二旧的报告。如果您还需要按相关性对公司进行排序或浏览它们,这也不是最好的解决方案。

为了实现所有这些功能并构建可扩展的解决方案,我建议在索引期间添加额外的处理,并将最新报表和第二个报表索引为主对象中的专用字段,而不是将它们存储为嵌套并尝试在搜索期间查找最新和第二个最新。因此,基本上您的记录应如下所示:

  {
    "id": 1,
    "latest_finanicals": {
            "periodEndDate": "2022-12-31",
            "cash": 12000,
            "inventory": 19000
        }
    },
    "previous_finanicals": {
            "periodEndDate": "2022-06-30",
            "cash": 10000,
            "inventory": 24000
        }
    }
    "financials": [
      {
        "periodEndDate": "2022-06-30",
        "cash": 10000,
        "inventory": 24000
      },
      {
        "periodEndDate": "2022-12-31",
        "cash": 12000,
        "inventory": 19000
      }
    ]
  }

with 和 having type 而不是 type。然后,您的所有查询都将变得非常简单。latest_finanicalsprevious_finanicalsobjectnested

评论

0赞 gron 10/25/2023
感谢您的精彩回答!我们有数以百万计的公司,因此分页和排序是强制性的。第二个解决方案是我一开始尝试过的。但是,财务报告有两种时间类型:年度和季度。PM 要求查找“最新”、“第二晚”、“最新年度”、“第二晚年度”、“最新季度”和“第二最新季度”。这些是 6 个额外的对象。我们还有更多模型,如财务报告。如果没有别的办法,我会采用它。
0赞 imotov 10/26/2023
嗯,我不确定您多久会在索引中收到相对于期间结束日期的报告,但我想知道 - 您能否将这些条件表示为日期范围,而不是排序列表中的最后一个和倒数第二个?如果可以的话,我想我可以提供更好的解决方案。
0赞 imotov 10/26/2023
在这种情况下,您还可以在嵌套对象上添加一个标记字段,指示该报表的状态,而不是创建所有这些附加字段。然后,您只需要在标记字段上添加一个术语查询作为条件之一。这里的主要问题是您要根据与其他记录的关系选择一条记录。如果您可以将相同的条件表达为不涉及记录之间关系的内容,那将会容易得多。例如,从现在到一个季度前的任何报告,而不是上一份报告,都会简化这一点。
0赞 gron 10/26/2023
不幸的是,我无法将日期条件表示为范围。例如,有两家公司 A 和 B。A的最新报告日期为2015-12-31。B的最新一次是在2023-06-30。虽然 A 报告的日期看起来很古老,但它是最新的并且与条件相符。