如何使用Spring Data Elasticsearch实现层级位置对象的递归查询
Elasticsearch 自关联层级结构查询实现方案
Elasticsearch 本身不支持原生递归关联查询,针对你的地点层级数据场景,以下是3种可落地的实现方案,可根据业务场景选型:
方案1:冗余存储全量祖先路径(优先推荐,适合地点类低变动场景)
这是生产环境最常用的树形结构存储方案,你只需在现有EsLocation实体类中新增冗余字段,写入数据时提前计算好当前节点的所有上级信息:
- 新增
ancestorIds字段,类型为Keyword数组,存储当前节点所有上级节点的ID,顺序从直接父级到顶级节点,例如下属美国得克萨斯州的奥斯汀,该字段值为["得克萨斯州ID", "美国ID", "北美洲ID"] - 可选新增
fullPath字段,存储完整层级路径用于前端展示,例如北美洲/美国/得克萨斯州/奥斯汀
实体类修改示例
@Document(indexName = "locations", shards = 1, replicas = 0) @Setting(settingPath = "analyzer.json") public class EsLocation { @Id private String id; @Field(type = FieldType.Text, analyzer = "index", searchAnalyzer = "search", fielddata = true) private String name; @Field(type = FieldType.Text, analyzer = "index", searchAnalyzer = "search", fielddata = true) private String displayName; @Field(type = FieldType.Keyword) private String type; @Transient private EsLocation parent; @Field(type = FieldType.Keyword) private String parentId; // 新增字段:所有祖先ID数组 @Field(type = FieldType.Keyword) private List<String> ancestorIds; // 可选新增字段:完整层级路径 @Field(type = FieldType.Text, analyzer = "index", searchAnalyzer = "search") private String fullPath; }
适配查询场景
- 查询某个节点下的所有子节点:直接用
terms查询匹配ancestorIds包含当前节点ID即可 - 查询某个节点的完整层级链路:直接读取返回结果的
ancestorIds/fullPath字段即可,无需额外查询
优缺点
- 优点:查询性能最高,实现逻辑简单,无额外语法限制
- 缺点:节点父级变动时需要同步更新所有子节点的冗余字段,适合地点这类层级变动频率极低的业务场景
方案2:使用ES原生Join类型实现父子关联
ES提供的join字段类型专门用于处理同索引内的父子关联关系,你可以把不同层级的地点映射为父子文档,父文档为上级地点,子文档为下级地点。
适配场景
适合层级变动频繁,不想批量更新大量子节点冗余数据的场景,查询性能低于方案1,且聚合、排序有较多语法限制,另外要求父子文档必须存储在同一个分片,你当前索引分片数为1刚好适配该要求。
方案3:应用层递归查询(仅适合小数据量测试场景)
如果你的数据量极小,也可以在应用层做多轮循环查询:
- 查向上层级:先查当前节点获取
parentId,再用parentId查询父节点,循环直到parentId为null - 查向下层级:先拿当前节点ID查询所有
parentId等于该ID的子节点,再循环递归查询每个子节点的下级节点
优缺点
- 优点:无需修改现有数据结构
- 缺点:查询次数和层级深度成正比,层级较深时延迟极高,不适合线上生产环境使用
内容的提问来源于stack exchange,提问作者Matej Martincevic
相关产品推荐
相关产品推荐

