如何使用$graphLookup查询子引用模式集合并生成树形视图
子引用模式MongoDB集合树形结构查询方案
实现思路
你使用的子引用模式(父文档存储子文档引用)可以通过$graphLookup递归遍历所有层级子节点,再将扁平化的查询结果组装为嵌套树形结构即可。
完整聚合查询语句
假设你的集合名为products,以下是查询指定根节点(示例中为prod03)的完整语句:
db.products.aggregate([ // 匹配查询根节点,可根据需求调整匹配条件 { $match: { id: "3" } }, // 递归查询所有后代节点 { $graphLookup: { from: "products", startWith: "$children._id", connectFromField: "children._id", connectToField: "_id", as: "allDescendants", depthField: "nodeLevel" } }, // 组装嵌套树形结构 { $addFields: { children: { $function: { body: function(rootChildren, descendants) { // 构建节点映射表提升查找效率 const nodeMap = {}; descendants.forEach(item => { nodeMap[item._id.toString()] = { id: item.id, name: item.name, totalQty: item.totalQty, children: item.children } }); // 递归构建子树 const buildSubTree = (childList) => { return childList.map(child => { const nodeInfo = nodeMap[child._id]; if (!nodeInfo) return { id: child._id, qty: child.qty }; return { id: nodeInfo.id, name: nodeInfo.name, qty: child.qty, totalQty: nodeInfo.totalQty, children: nodeInfo.children.length ? buildSubTree(nodeInfo.children) : [] } }) } return buildSubTree(rootChildren); }, args: ["$children", "$allDescendants"], lang: "js" } } } }, // 过滤输出指定字段 { $project: { _id: 0, id: 1, name: 1, totalQty: 1, children: 1 } } ])
适配场景说明
- 查询所有根节点:如果需要查询所有没有父级的根节点,将第一阶段的
$match替换为以下逻辑即可:
// 收集所有被引用的子节点ID { $group: { _id: null, allReferencedIds: { $addToSet: "$children._id" } } }, // 匹配未被引用的文档作为根节点 { $lookup: { from: "products", pipeline: [ { $match: { $expr: { $not: { $in: ["$_id", { $concatArrays: "$allReferencedIds" }] } } } } ], as: "rootList" } }, { $unwind: "$rootList" }, { $replaceRoot: { newRoot: "$rootList" } }
- 低版本MongoDB兼容:如果你的MongoDB版本低于4.4不支持
$function,可以保留allDescendants字段,在应用层按照上述函数逻辑组装树形结构即可。 - 深层级适配:默认
$graphLookup最大递归深度为100,若你的数据层级超过100,执行聚合时添加allowDiskUse: true参数即可突破限制。
内容的提问来源于stack exchange,提问作者Henrique
相关产品推荐
相关产品推荐

