使用spring-data-mongo计算MongoDB 3.2版本中文档树结构的深度
MongoDB 3.2版本树形结构深度统计实现方案
因为MongoDB 3.2不支持$graphLookup递归聚合算子,所以只能结合业务逻辑实现需求,以下是两种可行方案:
方案一:分批次按根节点查询(推荐,内存占用低)
适合数据量较大、树深度较低的场景,不需要全量拉取所有数据:
- 先查询所有
parent_id为null的根节点,获取每个根节点的name、rootId和id - 对每个根节点单独计算对应树的深度:
- 初始化深度为1,当前层节点ID列表为根节点ID
- 循环查询
rootId匹配、parent_id属于当前层ID列表的所有子节点ID,作为下一层节点列表 - 若下一层列表不为空,深度+1,将当前层替换为下一层后继续循环;若为空则终止循环,当前深度就是该树的总深度
- 汇总所有根节点的统计结果生成报表
核心代码示例(spring-data-mongo):
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; // 树节点实体类示例 class TreeNode { private String id; private String name; private String rootId; private String parent_id; // 省略getter、setter } // 统计结果实体类示例 class TreeDepthStat { private String rootName; private String rootId; private Integer totalDepth; // 省略getter、setter } public List<TreeDepthStat> statTreeDepth(MongoTemplate mongoTemplate) { // 查询所有根节点 Query rootQuery = Query.query(Criteria.where("parent_id").is(null)); List<TreeNode> rootNodes = mongoTemplate.find(rootQuery, TreeNode.class); List<TreeDepthStat> result = new ArrayList<>(); for (TreeNode root : rootNodes) { int depth = 1; List<String> currentLevelIds = Collections.singletonList(root.getId()); while (true) { // 只查子节点ID,减少数据传输 Query childQuery = Query.query(Criteria.where("rootId").is(root.getRootId()) .and("parent_id").in(currentLevelIds)) .fields().include("id"); List<String> nextLevelIds = mongoTemplate.find(childQuery, TreeNode.class) .stream() .map(TreeNode::getId) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(nextLevelIds)) { break; } depth++; currentLevelIds = nextLevelIds; } // 封装统计结果 TreeDepthStat stat = new TreeDepthStat(); stat.setRootName(root.getName()); stat.setRootId(root.getRootId()); stat.setTotalDepth(depth); result.add(stat); } return result; }
优化建议:给rootId和parent_id加联合索引,加快子节点查询速度,索引创建语句:db.tree.createIndex({rootId:1, parent_id:1})
方案二:全量拉取内存计算
适合数据量较小的场景,实现更简单,查询次数少:
- 全量拉取tree集合所有节点数据,按
rootId分组 - 对每个分组的节点列表,用BFS广度优先遍历或者递归计算最大深度
- 关联对应根节点的名称后汇总生成统计结果
内容的提问来源于stack exchange,提问作者aditya tolety
相关产品推荐
相关产品推荐

