Elasticsearch查询嵌套文档最小值且满足其他过滤条件的方法
Elasticsearch 嵌套订单匹配最早订单查询方案
可行性结论
该需求完全可以实现,无需重构现有存储结构,可根据Elasticsearch版本、数据量级、查询场景选择对应实现方式。
前提说明
默认索引中orders为nested类型,包含3个核心字段:
order_id:订单ID,keyword类型order_status:订单状态,keyword类型order_time:下单时间,date类型
基础mapping参考:
PUT /user_idx { "mappings": { "properties": { "user_id": {"type": "keyword"}, "orders": { "type": "nested", "properties": { "order_id": {"type": "keyword"}, "order_status": {"type": "keyword"}, "order_time": {"type": "date"} } } } } }
方案1:实时查询(无需预处理,适合中小数据量低并发场景)
通过nested查询配合脚本评分,查询时实时校验订单是否为用户最早订单,同时匹配状态、ID前缀规则,可直接返回命中的用户文档和对应匹配的订单详情。
示例DSL(以查询订单状态为paid、订单ID前缀为100的用户为例):
GET /user_idx/_search { "query": { "nested": { "path": "orders", "query": { "bool": { "must": [ // 匹配指定订单状态 {"term": {"orders.order_status": "paid"}}, // 匹配指定订单ID前缀 {"prefix": {"orders.order_id": "100"}}, // 校验当前订单为用户最早下单的订单 { "function_score": { "script_score": { "script": { "source": """ long minOrderTime = params._source.orders .stream() .mapToLong(order -> order.order_time.toInstant().toEpochMilli()) .min() .getAsLong(); return doc['orders.order_time'].value.toInstant().toEpochMilli() == minOrderTime ? 1 : 0; """ } } } } ] } }, // 返回命中的符合条件的订单详情,方便结果校验 "inner_hits": {} } } }
注意事项
- 单用户嵌套订单量超过1000条时,脚本遍历会有明显性能损耗,不适合高并发查询场景
- 7.x以下版本默认禁止脚本访问
_source,需要调整对应配置,或者改用聚合方案 order_id如果存为数值类型无法直接使用prefix查询,建议提前存为keyword类型
方案2:聚合筛选(适合统计场景,无_source访问权限时可用)
如果不需要直接返回全量用户文档,只需要筛选出符合条件的用户ID,可以用嵌套聚合+top_hits+桶选择器实现,不需要访问文档_source。
示例DSL:
GET /user_idx/_search { "size": 0, "aggs": { "user_group": { "terms": { "field": "user_id", "size": 10000 }, "aggs": { "user_orders": { "nested": {"path": "orders"}, "aggs": { "earliest_order": { "top_hits": { "sort": [{"orders.order_time": "asc"}], "size": 1, "_source": ["orders.order_id", "orders.order_status"] } } } }, "valid_user_filter": { "bucket_selector": { "buckets_path": {"topOrder": "user_orders>earliest_order"}, "script": { "source": """ def order = params.topOrder.hits.hits[0]._source.orders; return order.order_status == 'paid' && order.order_id.startsWith('100'); """ } } } } } } }
方案3:预处理优化(适合生产高并发场景)
如果查询QPS较高,建议在数据写入阶段提前给每个订单新增is_earliest_order布尔字段,标记该订单是否为对应用户的最早订单。后续查询只需要在nested查询中新增一个term过滤条件"term": {"orders.is_earliest_order": true}即可,完全不需要脚本,查询性能和普通嵌套查询一致。
内容的提问来源于stack exchange,提问作者NNN123
相关产品推荐
相关产品推荐

