如何在 ElasticSearch 中展平深度聚合?

How can I flatten a deep aggregation in ElasticSearch?

我的索引中有这样的文档:

{
  "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。有没有办法展平这个大查询?就目前而言,如果以后需要引入任何其他条件,则此查询很难维护。

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

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

1。首先是聚合元数据。

任何负责 post 处理大量嵌套聚合结果的人都会很高兴知道目标存储桶路径。您可以通过 aggregation metadata 子句提供它:

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

这将 return

{
  "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": {
      ...

这可能真的对你有帮助,也可能没有帮助,因为你的聚合查询看起来很简单而且没有太多子句。

3。最后说一下可维护性

使用可重用查询时,Elasticsearch 提供 Search template API. You'd construct a script containing a parametrized 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列表。