如何编写MongoDB查询关联嵌套数据并生成父子关系列表
MongoDB集合关联与父子关系列表生成方案
问题背景
现有两个MongoDB集合:
- 集合A(蓝色):内部节点(非外层)携带
_parentNode属性;外层节点携带_organisationId,需关联集合B的某一层级(层级未知),自带kind属性。 - 集合B(黑色):结构与A类似,内部节点携带
_parentOrg属性,外层节点无关联属性,无kind属性(需默认设为Organisation)。
需求:生成包含完整父子关系的列表,格式示例如下:
[ { "_id": { "$oid": "1" }, "name": "A Inc", "children": [{"$oid": "2"}, {"$oid": "3"}], "kind": "Organisation" }, ... ]
现有代码的问题
当前代码仅分别对两个集合执行graphLookup后拼接,存在两个核心问题:
- 未过滤根节点,导致每个节点都被作为根节点生成树结构,产生大量重复数据;
- 未处理集合A外层节点与集合B的关联逻辑,不符合需求中“关联B某一层级”的要求。
修正后的解决方案
步骤1:处理集合B,生成完整的组织树
先筛选B的根节点(无_parentOrg的节点),再通过graphLookup获取其完整子树,并添加默认kind属性:
// 处理集合B:获取根节点及其完整子树 let orgs = db.B.aggregate([ // 仅保留B的根节点(无_parentOrg属性) { $match: { _parentOrg: { $exists: false } } }, { $graphLookup: { from: "B", startWith: "$_id", connectFromField: "_id", connectToField: "_parentOrg", as: "children", }, }, { $project: { name: 1, children: "$children._id", // 仅保留子节点的ObjectID kind: { $literal: "Organisation" } // 为B添加默认kind属性 } } ]).toArray();
步骤2:处理集合A,生成节点树并关联到B
筛选A的根节点(带_organisationId的节点),获取其完整子树,再将这些根节点关联到B中对应的节点下:
// 处理集合A:获取根节点及其子树,保留关联ID let nodes = db.A.aggregate([ // 仅保留A的外层根节点(带_organisationId属性) { $match: { _organisationId: { $exists: true } } }, { $graphLookup: { from: "A", startWith: "$_id", connectFromField: "_id", connectToField: "_parentNode", as: "children", }, }, { $project: { name: 1, kind: 1, // 保留A自带的kind属性 children: "$children._id", _organisationId: 1 // 保留关联B的ID } } ]).toArray(); // 将A的根节点挂载到B的对应节点下 nodes.forEach(node => { const targetOrg = orgs.find(org => org._id.equals(node._organisationId)); if (targetOrg) { // 确保children数组存在 targetOrg.children = targetOrg.children || []; targetOrg.children.push(node._id); } }); // 合并结果:包含B的完整树,以及未找到对应B节点的A根节点 const finalResult = [...orgs, ...nodes.filter(node => !orgs.some(org => org._id.equals(node._organisationId)))];
关键说明
- 通过
$match过滤根节点,避免生成重复的树结构; - 利用
_id.equals()方法对比MongoDB的ObjectID(直接===无法正确匹配); - 对未找到对应B节点的A根节点,直接加入最终结果,避免数据丢失。
内容的提问来源于stack exchange,提问作者Yiffany
相关产品推荐
相关产品推荐

