MongoDB同表左连接与organisations集合递归左连接技术需求
MongoDB 同表关联与递归层级查询实现方案
一、核心需求拆解
- nodes集合同表左连接:A类型文档通过
_b字段关联B类型文档的_id,需保留所有字段并区分重复属性(如name) - organisations集合递归查询:文档通过
_parentOrg递归关联父级,层级未知,需获取所有父级并处理重复字段
二、示例数据准备
nodes集合数据
db.nodes.insertMany([ { _id: ObjectId("60d21b4667d0d8992e610c85"), type: "A", name: "Node A1", _b: ObjectId("60d21b4667d0d8992e610c86"), org: ObjectId("60d21b4667d0d8992e610c8a") }, { _id: ObjectId("60d21b4667d0d8992e610c86"), type: "B", name: "Node B1", org: ObjectId("60d21b4667d0d8992e610c8a") }, { _id: ObjectId("60d21b4667d0d8992e610c87"), type: "A", name: "Node A2", _b: ObjectId("60d21b4667d0d8992e610c88"), org: ObjectId("60d21b4667d0d8992e610c8b") }, { _id: ObjectId("60d21b4667d0d8992e610c88"), type: "B", name: "Node B2", org: ObjectId("60d21b4667d0d8992e610c8b") } ])
organisations集合数据
db.organisations.insertMany([ { _id: ObjectId("60d21b4667d0d8992e610c8a"), name: "Org C1", _parentOrg: ObjectId("60d21b4667d0d8992e610c8c") }, { _id: ObjectId("60d21b4667d0d8992e610c8b"), name: "Org C2", _parentOrg: null }, { _id: ObjectId("60d21b4667d0d8992e610c8c"), name: "Parent Org 1", _parentOrg: ObjectId("60d21b4667d0d8992e610c8d") }, { _id: ObjectId("60d21b4667d0d8992e610c8d"), name: "Top Parent Org", _parentOrg: null } ])
三、完整聚合查询实现
db.nodes.aggregate([ // 1. 同表左连接A与关联的B类型文档 { $lookup: { from: "nodes", localField: "_b", foreignField: "_id", as: "b_node" } }, // 将关联的B文档数组转为单个对象(保留无关联的A文档) { $unwind: { path: "$b_node", preserveNullAndEmptyArrays: true } }, // 2. 关联对应的组织(C)文档 { $lookup: { from: "organisations", localField: "org", foreignField: "_id", as: "org_info" } }, { $unwind: { path: "$org_info", preserveNullAndEmptyArrays: true } }, // 3. 递归查询组织的所有父级 { $graphLookup: { from: "organisations", startWith: "$org_info._parentOrg", connectFromField: "_parentOrg", connectToField: "_id", as: "parent_orgs" } }, // 4. 重命名字段,区分A、B、C及父级的重复属性 { $project: { // A类型节点字段 a_id: "$_id", a_name: "$name", a_type: "$type", // B类型节点字段 b_id: "$b_node._id", b_name: "$b_node.name", b_type: "$b_node.type", // 当前组织(C)字段 c_id: "$org_info._id", c_name: "$org_info.name", // 父级组织列表,重命名重复字段 parent_orgs: { $map: { input: "$parent_orgs", as: "parent", in: { parent_id: "$$parent._id", parent_name: "$$parent.name", parent_parentOrg: "$$parent._parentOrg" } } }, // 按需保留其他原始字段 original_fields: "$$ROOT" } } ])
四、关键步骤说明
- 同表左连接:使用
$lookup时指定from: "nodes"实现同表关联,通过localField: "_b"和foreignField: "_id"匹配A与B类型文档,$unwind将关联结果数组转为单个对象,确保无关联的A文档不被过滤。 - 递归层级查询:使用MongoDB专属的
$graphLookup处理递归关联,startWith指定递归起始点(当前组织的_parentOrg),connectFromField和connectToField定义递归关联规则,自动获取所有层级的父级组织。 - 重复字段区分:通过
$project给不同来源的重复字段(如name)添加前缀(a_、b_、c_、parent_),避免字段覆盖冲突。
内容的提问来源于stack exchange,提问作者Yiffany
相关产品推荐
相关产品推荐

