如何过滤Elasticsearch全局聚合?

15

我想要实现的目标:我希望我的“年龄”聚合不会受到查询过滤器的影响,并且我希望能够对其应用筛选器。

因此,如果我从这个查询开始:

{
    "query":{
        "filtered":{
            "filter":{ "terms":{ "family_name":"Brown" } } //filter_1
        }
    },
    "aggs":{
        "young_age":{
            "filter":{
                "range":{ "lt":40, "gt":18 } //filter_2
            },
            "aggs":{
                "age":{
                    "terms":{
                        "field":"age"
                    }
                }
            }
        }
    }
}

我的聚合“young_age”将被filter_1和filter_2过滤,我不想让我的聚合被filter_1过滤。

当我查阅文档时,我认为全局聚合可以解决我的问题,于是我编写了以下查询:

{
    "query":{
        "filtered":{
            "filter":{ "terms":{ "family_name":"Brown" } } //filter_1
        }
    },
    "aggs":{
        "young_age":{
            "global":{}, //<----------- add global
            "filter":{
                "range":{ "lt":40, "gt":18 } //filter_2
            },
            "aggs":{
                "age":{
                    "terms":{
                        "field":"age"
                    }
                }
            }
        }
    }
}

但是,Elasticsearch对我的filter_2进行抱怨:

""" 在[global]和[filter]中发现两个聚合类型定义[age] """

当然,如果我删除filter_2:

{
    "query":{
        "filtered":{
            "filter":{
                "terms":{
                    "family_name":"Brown"
                }
            }
        }
    },
    "aggs":{
        "young_age":{
            "global":{},
            "aggs":{
                "age":{
                    "terms":{
                        "field":"age"
                    }
                }
            }
        }
    }
}

那么我的聚合将不会被filter_1过滤(正如预期的那样)。

那么我应该如何对我的全局聚合应用filter_2呢?或者说我该如何实现这一目标呢?我记得之前使用面向文档的筛选器时写过类似的东西...

2个回答

10

在我看来,这是post_filter的典型用法。正如文档所说:

post_filter会在搜索请求的最后一步应用于搜索命中结果,此时聚合已经被计算完毕。

你的查询将类似于:

{
    "post_filter":{
       "terms":{
            "family_name":"Brown" //filter_1
        }
    },
   "aggs":{
        "young_age":{
            "filter":{
                "range":{ "lt":40, "gt":18 } //filter_2
            },
            "aggs":{
                "age":{
                    "terms":{
                        "field":"age"
                    }
                }
            }
        }
    }
}
在这种情况下,搜索命中的是索引中的所有文档。然后计算聚合(在filter_1之前)。之后将执行带有filter_1的post_filter。 编辑:正如您在评论中所说,您有许多聚合,只有一个不应受到filter_1的影响,我使用全局聚合修复了您的查询。
{
  "query": {
    "filtered": {
      "filter": {
        "term": {
          "family_name": "Brown"
        }
      }
    }
  },
  "aggs": {
    "young_age": {
      "global": {},
      "aggs": {
        "filter2": {
          "filter": {
            "range": {
              "lt": 40,
              "gt": 18
            }
          },
          "aggs": {
            "age": {
              "terms": {
                "field": "age"
              }
            }
          }
        }
      }
    }
  }
}

2
这个方案可以行得通,但实际上我有很多聚合,只有其中一个不应该被 filter_1 过滤。所以按照你的解决方案,我需要为那些聚合复制 post_filter,这让我感到有点烦人(而且从性能角度来看也可能不是那么好)。 - adrienbourgeois
1
@adrienbourgeois 我使用全局聚合修复了查询,如果可以,请告诉我它是否有效 :) - moliware
谢谢伙计,它像魔法一样工作!我已经接受了你的答案。干杯! - adrienbourgeois
感谢您提供第二个(编辑过的)答案。在找到您的过滤子聚合想法之前,我一度感到很困扰。 - maricn

1

全局变量和过滤器不能在同一级别使用。您需要将过滤器放置在全局聚合内的一个级别。

类似这样的做法应该适合您。

{
    "query":{
        "filtered":{
            "filter":{
                "terms":{
                    "family_name":"Brown"
                }
            }
        }
    },
    "aggs":{
        "young_age":{
            "global":{},
            "aggs":{
                "filter": {"term": {"family_name": "Brown"}}, #or {"bool": {"filter": {"term": {"family_name": "Brown"}}}}
                "aggs": {
                    "age":{
                        "terms":{
                            "field":"age"
                        }
                    }
                }
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接