如何在ElasticSearch中实现同类型/索引的Join查询(类Solr)
这问题我太熟了!你要实现的是先通过p1、p2、p3筛选出对应的category,再拉取这些分类下的所有产品,在Elasticsearch里主要有几种靠谱的实现方式,我给你拆解清楚:
方法一:分步查询(最直观易控)
这种方式和你MySQL里的子查询逻辑思路一致,分两步走:
第一步:获取符合条件的category列表
先执行一个查询,把满足p1=$p1、p2=$p2、p3=$p3的文档的category字段提取出来:
GET /product/_search { "size": 10000, // 如果category数量可能超过1万,建议用scroll或者search_after分页获取 "_source": ["category"], // 只返回category字段,减少数据传输 "query": { "bool": { "filter": [ {"term": {"p1": "$p1"}}, {"term": {"p2": "$p2"}}, {"term": {"p3": "$p3"}} ] } } }
从返回结果的hits.hits里把所有_source.category的值提取出来,去重后得到目标category列表。
第二步:查询这些category下的所有产品
用第一步得到的category列表,执行terms查询:
GET /product/_search { "query": { "terms": { "category": ["cat1", "cat2", "cat3"] // 替换成第一步拿到的category列表 } } }
这种方式的优势是逻辑清晰,对结果的可控性强,适合大部分场景。
方法二:单请求实现(用Terms Lookup功能)
Elasticsearch支持在terms查询里直接引用另一个查询的结果,把两步合并成一个请求,非常方便:
GET /product/_search { "query": { "terms": { "category": { "index": "product", // 目标索引名 "path": "category", // 要提取的字段名 "query": { // 这里就是用来筛选category的子查询 "bool": { "filter": [ {"term": {"p1": "$p1"}}, {"term": {"p2": "$p2"}}, {"term": {"p3": "$p3"}} ] } } } } } }
这个查询会自动先执行内部的子查询,提取符合条件的category值,再用这些值去匹配所有产品。需要注意的是,如果子查询返回的category数量超过Elasticsearch默认的index.max_terms_count(默认1024),需要调整这个参数或者用方法一处理。
方法三:Script实现(不推荐,性能拉胯)
如果上面两种方法都不适用,也可以用脚本实现,但非常不推荐在生产环境用——因为每个文档都要执行一次子查询,性能极差,只适合极小数据集的场景:
GET /product/_search { "query": { "bool": { "filter": { "script": { "script": { "source": """ def categories = []; // 内部执行子查询获取category列表 def searchResponse = ctx._index.search({ "size": 10000, "_source": ["category"], "query": { "bool": { "filter": [ {"term": {"p1": params.p1}}, {"term": {"p2": params.p2}}, {"term": {"p3": params.p3}} ] } } }); // 收集category值 for (hit in searchResponse.hits.hits) { categories.add(hit._source.category); } // 判断当前文档的category是否在集合中 categories.contains(doc['category'].value) """, "params": { "p1": "$p1", "p2": "$p2", "p3": "$p3" } } } } } } }
总结
日常开发优先选方法一(可控性强)或者方法二(单请求便捷),方法三只作为极端情况下的备选方案。
内容的提问来源于stack exchange,提问作者yang
相关产品推荐
相关产品推荐

