Elasticsearch多索引查询:筛选用户未达完成上限的课程
如何过滤出用户未达课程完成上限的Elasticsearch查询
嘿,我懂你要啥了——你想从course索引里挑出指定用户还能继续刷的课程,也就是用户在course_events里的完成次数current_count还没到课程的max_per_user上限,同时还要保留你原来的那些过滤条件对吧?
因为要关联两个独立索引做字段比较,直接单查询跨索引取值在Elasticsearch里是受限的(脚本没法跨索引访问字段),所以我给你整理了一套可行的方案,包含优化后的查询和前置准备步骤:
前置准备:同步课程上限字段到事件索引
首先得把course里的max_per_user同步到course_events中,这样我们能在事件索引里直接做次数比较。这里用Elasticsearch的Enrich功能来实现自动同步:
1. 创建Enrich策略
PUT _enrich/policy/course_max_per_user_policy { "match": { "indices": "course", "match_field": "id", "enrich_fields": ["max_per_user"] } }
2. 执行策略生成关联数据
POST _enrich/policy/course_max_per_user_policy/_execute
3. 创建Ingest管道,写入事件时自动关联字段
PUT _ingest/pipeline/course_enrich_pipeline { "description": "自动给course_events添加上限字段", "processors": [ { "enrich": { "policy_name": "course_max_per_user_policy", "field": "course_id", "target_field": "course_meta", "max_matches": 1 } }, { "remove": { "field": "course_meta.id" // 移除冗余字段 } } ] }
之后写入course_events时,加上pipeline=course_enrich_pipeline参数,新的事件文档就会自动带上course_meta.max_per_user字段了。
最终查询代码
现在就能构造出符合你需求的查询了——在原有过滤条件基础上,排除掉用户已达到上限的课程:
{ "sort": [{"id": "desc"}], "query": { "bool": { "filter": [ // 保留你原来的日期过滤条件 {"range": {"end_date": {"gte": "2020-09-28T12:27:55.884Z"}}}, {"range": {"start_date": {"lte": "2020-09-28T12:27:55.884Z"}}}, // 新增:排除用户已完成次数达上限的课程 {"bool": { "must_not": [ {"terms": { "id": { "index": "course_events", "path": "course_id", "query": { "bool": { "filter": [ {"term": {"user_progress.user_id": 123}}, // 替换为实际传入的user_id {"script": { "source": "doc['user_progress.current_count'].value >= doc['course_meta.max_per_user'].value" }} ] } } } }} ] }} ], "must": [{"term": {"is_active": true}}] // 保留原来的激活状态过滤 } } }
关键部分解释
terms查询的跨索引能力:从course_events索引中筛选出用户已达上限的课程ID,然后在course查询中排除这些ID- 脚本比较:在事件索引里直接对比用户的已完成次数和课程上限,确保只排除真正达到上限的课程
- 兼容原有逻辑:完全保留了你原来的日期、激活状态过滤和排序规则
无脚本的替代方案(如果集群禁用脚本)
如果你的Elasticsearch集群不允许使用脚本,可以分两步走:
- 先从
course_events和course索引联合查询,获取用户已达上限的课程ID列表 - 在
course查询中用must_not + terms排除这些ID
举个第一步的查询例子:
{ "size": 10000, "_source": ["course_id"], "query": { "bool": { "filter": [ {"term": {"user_progress.user_id": 123}}, // 这里需要先通过Enrich或者关联查询拿到max_per_user,再做范围比较 {"range": {"user_progress.current_count": {"gte": "{{max_per_user}}"}}} ] } } }
内容的提问来源于stack exchange,提问作者Prim
相关产品推荐
相关产品推荐

