Elasticsearch如何计算全部分桶平均值同时仅返回前N个terms分桶
问题核心
avg_bucket管道聚合的计算范围默认与父级terms聚合返回的分桶范围完全一致:terms的size参数设置为多少,就只会基于这部分返回的分桶计算平均值,因此你之前的写法无法拿到全部门店的平均销量。
解决方案
提供两种适配不同场景的实现方式:
方案一:全局指标直接计算(性能最优)
无需遍历全部门店分桶,通过两个全局指标直接计算平均销量,适合门店数量多、允许极小统计误差的场景:
GET hat-sales/_search { "size": 0, "query": { "match_all": {} }, "aggs": { "total_sales": { "value_count": { "field": "_id" } }, "total_stores": { "cardinality": { "field": "store.keyword" } }, "avg_all_store_sales": { "bucket_script": { "buckets_path": { "total_sales": "total_sales", "total_stores": "total_stores" }, "script": "params.total_sales / params.total_stores" } }, "top3_stores": { "terms": { "field": "store.keyword", "size": 3, "order": { "_count": "desc" } } } } }
说明:
total_sales统计全量销售记录总数,total_stores统计全部门店数量,两者的商就是全部门店的平均销量top3_stores单独返回销量最高的前3家门店,不涉及全部分桶计算,性能极高- 如果需要100%精确的门店数量,可选择方案二
方案二:全部分桶计算后截断返回(结果100%精确)
适合门店总数不超过65536、需要完全精确的统计结果、或是有复杂分桶计算逻辑的场景:
GET hat-sales/_search { "size": 0, "query": { "match_all": {} }, "aggs": { "by_store": { "terms": { "field": "store.keyword", "size": 65536 }, "aggs": { "sales_count": { "cardinality": { "field": "_id" } }, "only_return_top3": { "bucket_sort": { "sort": [ {"sales_count": {"order": "desc"}} ], "size": 3 } } } }, "avg_all_store_sales": { "avg_bucket": { "buckets_path": "by_store>sales_count" } } } }
说明:
- 先将
terms的size设置为足够覆盖所有门店的阈值(ES默认最大terms分桶数为65536),确保所有门店都参与聚合计算 bucket_sort是在全部分桶计算完成后、返回结果前执行的截断逻辑,仅返回前3个分桶,不会影响全部分桶的计算结果avg_bucket基于by_store生成的全部分桶计算平均值,结果完全精确
内容的提问来源于stack exchange,提问作者Sean Letendre
相关产品推荐
相关产品推荐

