Elasticsearch聚合过滤疑问:如何让聚合仅基于搜索结果计算?
问题原因
你遇到的核心问题是**post_filter的执行时机在聚合之后**:Elasticsearch会先执行query获取匹配的文档,基于这些文档计算聚合结果,最后才用post_filter过滤搜索命中的结果。所以你的聚合是基于所有匹配query(标题/内容含"klavi")的文档,而非post_filter过滤后的size=large文档。
解决方案
方案1:将过滤条件合并到query中
如果希望搜索结果和聚合都基于同一套过滤规则,直接把post_filter中的条件移到query的bool.must里即可。这样Elasticsearch会先过滤出符合所有条件的文档,再基于这些文档计算聚合。
修改后的请求:
{ "aggs":{ "price":{ "terms":{ "field":"price.keyword" } }, "size":{ "terms":{ "field":"size.keyword" } } }, "from":0, "query":{ "bool":{ "must":[ { "bool":{ "should":[ { "term":{ "language":{ "value":"en" } } }, { "term":{ "language":{ "value":"nullvalue" } } } ] } }, { "term":{ "size":{ "value":"large" } } } ], "should":[ { "match_phrase_prefix":{ "title":{ "query":"klavi" } } }, { "match_phrase_prefix":{ "content":{ "query":"klavi" } } } ] } } }
方案2:用filters聚合实现固定桶的统计
如果需要保留post_filter,同时让聚合返回所有固定桶(包括doc_count为0的),可以使用filters聚合替代terms聚合,明确指定每个需要统计的键:
{ "aggs":{ "price":{ "filters":{ "filters":{ "100": {"term": {"price.keyword": "100"}}, "120": {"term": {"price.keyword": "120"}}, "60": {"term": {"price.keyword": "60"}} } } }, "size":{ "filters":{ "filters":{ "large": {"term": {"size.keyword": "large"}}, "medium": {"term": {"size.keyword": "medium"}}, "small": {"term": {"size.keyword": "small"}} } } } }, "from":0, "post_filter":{ "bool":{ "must":[ { "bool":{ "should":[ { "term":{ "language":{ "value":"en" } } }, { "term":{ "language":{ "value":"nullvalue" } } } ] } }, { "term":{ "size":{ "value":"large" } } } ] } }, "query":{ "bool":{ "should":[ { "match_phrase_prefix":{ "title":{ "query":"klavi" } } }, { "match_phrase_prefix":{ "content":{ "query":"klavi" } } } ] } } }
这个请求的聚合会返回你期望的结果:每个指定的键都会显示对应的文档数,没有匹配的则为0。
内容的提问来源于stack exchange,提问作者Ali Hasan
相关产品推荐
相关产品推荐

