【问题标题】:How can I flatten a deep aggregation in ElasticSearch?如何在 ElasticSearch 中展平深度聚合?
【发布时间】:2021-07-01 11:43:49
【问题描述】:

我的索引中的文档如下所示:

{
  "foo": null,
  "bars": [
    {
      "baz": "BAZ",
      "qux": null,
      "bears": [
        {
          "fruit": "banana"
        }
      ]
    }
  ]
}

我想汇总 .bars[].bears[].fruit 的唯一值以及每个找到值的计数。但是,我也只想计算与foo 上的某些条件匹配的文档的这些深度值,以及与bazqux 上的某些条件匹配的bars[] 值。我还想聚合所有文档,忽略搜索查询的内容。

以下查询完成了我想做的所有事情:

{
  "aggs": {
    "global": {
      "global": {},
      "aggs": {
        "notFoo": {
          "filter": {
            "bool": {
              "must_not": [
                {
                  "exists": {
                    "field": "foo"
                  }
                }
              ]
            }
          },
          "aggs": {
            "bars": {
              "nested": {
                "path": "bars"
              },
              "aggs": {
                "notValueN": {
                  "filter": {
                    "bool": {
                      "filter": [
                        {
                          "bool": {
                            "should": [
                              {
                                "terms": {
                                  "bars.baz": [
                                    "value1",
                                    "value2",
                                    "value3"
                                  ]
                                }
                              },
                              {
                                "terms": {
                                  "bars.qux": [
                                    "value4",
                                    "value5",
                                    "value6"
                                  ]
                                }
                              }
                            ],
                            "minimum_should_match": 1
                          }
                        }
                      ]
                    }
                  },
                  "aggs": {
                    "bears": {
                      "nested": {
                        "path": "bars.bears"
                      },
                      "aggs": {
                        "rules": {
                          "terms": {
                            "field": "bars.bears.fruit"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

此查询有效,但感觉相当庞大和繁重。为了得到我正在寻找的结果,我必须访问.aggregations.global.bars.notValueN.bears.fruit.buckets。有没有办法展平这个大查询?就目前而言,如果以后需要引入任何其他条件,则此查询非常难以维护。

【问题讨论】:

    标签: elasticsearch elasticsearch-aggregation


    【解决方案1】:

    ES 唯一支持object key flattening 的地方是集群设置API。遗憾的是,此策略不能用于 API 的其他部分,包括聚合。

    不过,还有一些其他技巧值得一提。

    1。首先是聚合元数据。

    负责对高度嵌套的聚合结果进行后处理的任何人都会喜欢知道目标存储桶路径。您可以通过aggregation metadata 子句提供它:

    POST your-index/_search
    {
      "aggs": {
        "global": {
          "global": {},
          "meta": {
            "accessor_path": "aggs.global.Foo...."  <---
          },
          ...
    

    将返回

    {
      "aggregations" : {
        "global" : {
          "meta" : {
            "accessor_path" : "aggs.global.Foo..."  <---
          },
          "Foo" : {
    

    2。然后是响应过滤

    如果您在同一个请求正文中包含多个(子)聚合,您可以通过filter_path URI parameter 减少响应“庞大”:

    POST your-index/_search/template?filter_path=aggregations.global.meta,aggregations.global.*.*.*.*.*.buckets
    {
      "aggs": {
        "global": {
          ...
    

    这可能会或可能不会真正帮助您,因为您的 agg 查询看起来很简单且没有太多子条款。

    3。最后说一下可维护性

    在处理可重用查询时,Elasticsearch 提供了Search template API。您将构建一个包含参数化 mustache 模板的脚本,然后在查询时提供参数。

    在您的特定用例中,我提出以下建议:

    1. 存储小胡子模板脚本:
    POST _scripts/nested_bars_query
    {
      "script": {
        "lang": "mustache",
        "source": """
          {
            "query": {{#toJson}}raw_search_query{{/toJson}},
            "aggs": {
              "global": {
                "global": {},
                "meta": {
                  "accessor_path": "{{accessor_path}}"
                },
                "aggs": {
                  "{{notXYZ.agg_name}}": {
                    "filter": {
                      "bool": {
                        "must_not": [
                          {
                            "exists": {
                              "field": "{{notXYZ.field_name}}"
                            }
                          }
                        ]
                      }
                    },
                    "aggs": {
                      "bars": {
                        "nested": {
                          "path": "bars"
                        },
                        "aggs": {
                          "{{notValueN.agg_name}}": {
                            "filter": {
                              "bool": {
                                "filter": [
                                  {
                                    "bool": {
                                      "should":  {{#toJson}}notValueN.raw_should_clauses{{/toJson}},
                                      "minimum_should_match": 1
                                    }
                                  }
                                ]
                              }
                            },
                            "aggs": {
                              "bears": {
                                "nested": {
                                  "path": "bars.bears"
                                },
                                "aggs": {
                                  "rules": {
                                    "terms": {
                                      "field": "bars.bears.fruit"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        """
      }
    }
    
    1. 使用搜索模板 ID (nested_bars_query) 定位 /_search/template/ 端点。另外,指定上面讨论的filter_path 和元数据accessor_path
    POST your-index-name/_search/template?filter_path=aggregations.global.meta,aggregations.global.*.*.*.*.*.buckets
    {
      "id": "nested_bars_query",
      "params": {
        "raw_search_query": {
          "match_all": {}
        },
        "accessor_path": "aggs.global.Foo.bars.notValueN.bears.rules.buckets",
        "notXYZ": {
          "agg_name": "Foo",
          "field_name": "foo"
        },
        "notValueN": {
          "agg_name": "notValueN",
          "raw_should_clauses": [
            {
              "terms": {
                "bars.baz": [
                  "BAZ",
                  "value2",
                  "value3"
                ]
              }
            },
            {
              "terms": {
                "bars.qux": [
                  "value4",
                  "value5",
                  "value6"
                ]
              }
            }
          ]
        }
      }
    }
    

    您当然可以通过消除定义自定义agg_names 等的可能性来标准化上述内容。

    如果后面需要引入额外的条件,可以修改params里面的raw_should_clauses列表。

    【讨论】:

    • accessor_path 是否有可用的文档?我似乎在任何地方都找不到任何东西。
    • 哦,accessor_path 就是我给它取的名字。这是一个任意的元数据键?
    • 啊,明白了。谢谢!
    • 没有问题!嘿,顺便说一句,在我的recently released ES handbook 中有很多这样的简洁提示。一章专注于聚合,可能与您特别相关!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 2023-03-28
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多