ElasticSearch如何单查询实现基于首次结果的链式关联查询
单次ElasticSearch查询实现方案
完全可以通过单次ES查询拿到目标结果,无需两次链式调用,根据是否允许调整索引结构,可选择不同落地方案:
方案1:不改动现有索引结构,直接查询
核心思路是用bool查询组合两类匹配规则,通过脚本动态关联familyId匹配关联家庭成员,以搜索姓名关键词abc为例,查询DSL如下:
GET person_index/_search { "query": { "bool": { "should": [ // 匹配姓名符合搜索条件的人员 {"match": {"name": "abc"}}, // 匹配和命中人员同familyId的所有家庭成员 { "bool": { "must": [ {"exists": {"field": "familyId"}}, { "script": { "script": { "source": """ def matchedFids = []; // 检索所有姓名命中的文档,提取对应familyId def nameHits = _index.search( [ "query": {"match": {"name": params.keyword}}, "_source": ["familyId"] ], ["size": 1000] ).hits.hits; for (def hit : nameHits) { if (hit._source.familyId != null) matchedFids.add(hit._source.familyId); } // 判断当前文档是否属于命中的家庭 return matchedFids.contains(doc['familyId'].value); """, "params": {"keyword": "abc"} } } } ] } } ], "minimum_should_match": 1 } } }
- 注意:该方案需要提前开启ES脚本的
_index.search执行权限,默认配置下未开放该权限会报错。脚本查询存在一定性能损耗,数据量超过百万级时不推荐使用。 - 结果去重可通过外层加
collapse: {"field": "id"}参数实现,避免重复返回同一条人员数据。
方案2:调整索引结构,高性能实现
如果允许调整索引mapping,可通过预关联的方式彻底避免运行时关联的性能损耗,两种常用调整方式:
- 采用
join字段类型建立家庭-成员父子文档关系:家庭作为父文档,人员作为子文档。查询时先匹配姓名命中的子文档,再通过父子文档查询配合inner_hits一次性拉取同家庭所有成员,全程走倒排索引检索,性能远高于脚本方案。 - 冗余家庭成员字段:在人员文档中新增
familyMembers数组字段,写入数据时就把同家庭所有成员的核心信息(id、姓名等)同步到该字段。查询时只要匹配到姓名,直接就能从当前文档拿到所有家庭成员信息,不需要运行时做关联,是读多写少场景下性能最优的方案。
低成本优化方案
如果既不想改索引结构,也不想开脚本权限,可以把现有两次链式调用合并为单次HTTP请求,用ES的_msearch批量查询接口同时发送「姓名匹配查询」「关联家庭成员查询」两个请求,相比原有方案减少一次网络往返开销,改造成本极低,性能提升明显。
内容的提问来源于stack exchange,提问作者special3220
相关产品推荐
相关产品推荐

