如何在Elasticsearch结果过滤前按timestamp对聚合桶进行排序
问题根因
你遇到的问题是composite聚合的固有执行逻辑导致的:
composite聚合会先按照sources中定义的字段顺序做字典序排序生成全部分组桶,先取size参数指定的前N个桶,之后才会执行桶内的子聚合(包括你写的bucket_sort)。你设置size=1的时候,取到的第一个桶本身就是按sub_type字典序排第一的zzzzzmysubtype222对应的桶,后续的bucket_sort只会对这1个桶排序,自然不可能得到时间最新的桶。
解决方案
方案1:小数据量场景(分组总数≤1万)
把composite的size设置为大于等于你的分组总数量的数值,然后在bucket_sort中设置你需要的返回size即可。该方案会先拉取全部分组,再按时间排序取前N个,修改示例如下:
"composite": { "size": 10000, // 调整为大于等于你的总分组数 "sources": [ {"name": {"terms": {"field": "name.keyword"}}}, {"type": {"terms": {"field": "type.keyword"}}}, {"sub_type": {"terms": {"field": "sub_type.keyword"}}} ] }, // 子聚合中的bucket_sort修改为 "events_bucket_sort": {"bucket_sort": {"sort": [{"top_hit": {"order": "desc"}}], "size": 1}} // 这里的size是你需要返回的数量
方案2:大数据量场景(ES版本≥7.12)
使用multi_terms聚合替代composite聚合,multi_terms支持直接按子聚合的结果对分组桶排序,不需要先拉取全部分组,性能更优,完整查询示例如下:
{ "query": { "bool": { "filter": [{"range": {"timestamp": {"lte": "now", "gte": "2020-06-05"}}}] } }, "aggs": { "events": { "multi_terms": { "terms": [ {"field": "name.keyword"}, {"field": "type.keyword"}, {"field": "sub_type.keyword"} ], "order": {"top_hit": "desc"}, // 直接按每个桶的最大时间排序 "size": 1 // 这里直接填你需要的返回数量即可 }, "aggs": { "events_count": {"sum": {"field": "count"}}, "latest_event": {"top_hits": {"sort": [{"timestamp": {"order": "desc"}}], "size": 1}}, "top_hit": {"max": {"field": "timestamp"}} } } }, "size": 0 }
该方案不管设置的返回size是多少,都会优先返回最新时间对应的分组桶,完全符合需求。
兼容低版本ES方案
如果你的ES版本低于7.12不支持multi_terms,可以在写入文档时新增一个group_key字段,值为name#type#sub_type的拼接字符串,之后用普通terms聚合对group_key字段分组,同样配置order按top_hit降序排序即可。
内容的提问来源于stack exchange,提问作者ArielB
相关产品推荐
相关产品推荐

