Elasticsearch如何实现collapse折叠结果的过滤与排序
问题原因
你当前的查询执行逻辑和需求顺序完全相反:
- 现有查询的执行流程是:先过滤出
discount_pct > 2的全量文档 → 全局按discount_pct降序排序 → 按product_id折叠,每个商品取排序后的第一条记录 - 这个逻辑天然会返回每个商品下折扣最高的文档,完全不会校验该文档是否为商品的最低价记录,自然不符合预期。
实现方案
方案1:Terms聚合 + Top_hits子聚合(逻辑最严谨)
这个方案严格按照「先按商品分组取最低价记录→再按折扣排序」的流程执行,不会出现逻辑偏差,查询示例如下:
{ "size": 0, "query": { "bool": { "filter": [ { "range": { "discount_pct": { "gt": 2 } } } ] } }, "aggs": { "group_by_product": { "terms": { "field": "product_id", "size": 10000 }, "aggs": { "lowest_price_doc": { "top_hits": { "sort": [ {"price": {"order": "asc"}}, {"discount_pct": {"order": "desc"}} ], "size": 1 } }, "get_first_page": { "bucket_sort": { "size": 100 } } } } } }
参数说明:
terms.size设置为大于符合筛选条件的商品总数量的值即可,确保所有商品都被分组top_hits内先按price升序、再按discount_pct降序排序,每个分组取1条,就是对应商品最低售价、且同价下折扣最高的记录- 最终返回结果在
aggregations.group_by_product.buckets路径下,每个桶的lowest_price_doc.hits.hits就是目标文档,拿到结果后在业务侧按discount_pct降序取前12条即可,百级数据量下内存排序几乎无性能损耗。如果需要完全在ES侧完成排序,可以新增脚本聚合提取最低价文档的折扣值做桶排序,会有一定性能损耗,非必要不推荐。
方案2:Collapse + Inner_hits(性能更高,适合大数据量场景)
如果不想用聚合,可优化原collapse写法,通过inner_hits指定组内取数规则,查询示例如下:
{ "query": { "bool": { "filter": [ { "range": { "discount_pct": { "gt": 2 } } } ] } }, "collapse": { "field": "product_id", "inner_hits": { "name": "lowest_price_record", "sort": [ {"price": {"order": "asc"}}, {"discount_pct": {"order": "desc"}} ], "size": 1 } }, "sort": [ {"price": {"order": "asc"}} ], "size": 100 }
注意:collapse的外层排序作用于折叠前的全量文档,无法直接决定组内返回的文档,真正的最低价记录存储在每条返回结果的inner_hits.lowest_price_record.hits.hits中,拿到结果后同样在业务侧按discount_pct降序排序取前12条即可。这个方案比聚合查询性能更好,适合千万级以上数据量的场景。
内容的提问来源于stack exchange,提问作者Jerodev
相关产品推荐
相关产品推荐

