MongoDB多集合嵌套关联查询:distributors关联fruits与tools
解决方案:MongoDB深层嵌套集合链式关联聚合查询
针对你需要将distributors、fruits、tools三个集合做深层链式关联的需求,这里提供一个一次性聚合查询方案,完美处理嵌套字段的替换:
db.distributors.aggregate([ // 第一步:关联fruits集合,同时在内部管道中先完成与tools的关联替换 { $lookup: { from: "fruits", let: { distributorChildren: "$categories.children" }, pipeline: [ // 先把fruits关联到tools集合 { $lookup: { from: "tools", localField: "variants.tools", foreignField: "uid", as: "toolsDocs" } }, // 替换fruits里variants的tools字段为对应工具文档 { $addFields: { variants: { $map: { input: "$variants", as: "variant", in: { $mergeObjects: [ "$$variant", { tools: { $map: { input: "$$variant.tools", as: "toolUid", in: { $first: { $filter: { input: "$toolsDocs", cond: { $eq: ["$$this.uid", "$$toolUid"] } } } } } } } ] } } }, fruitUid: "$uid" // 临时存uid用于后续匹配 } }, // 过滤掉distributors没用到的fruits文档 { $match: { $expr: { $in: [ "$fruitUid", { $reduce: { input: "$$distributorChildren", initialValue: [], in: { $concatArrays: [ "$$value", { $reduce: { input: "$$this.children", initialValue: [], in: { $concatArrays: ["$$value", ["$$this.child"]] } } } ] } } } ] } } }, // 清理临时字段 { $project: { uid: 0, toolsDocs: 0 } } ], as: "mappedFruits" } }, // 第二步:替换distributors里深层嵌套的child字段为处理后的fruits文档 { $addFields: { categories: { $map: { input: "$categories", as: "category", in: { $mergeObjects: [ "$$category", { children: { $map: { input: "$$category.children", as: "childItem", in: { $mergeObjects: [ "$$childItem", { child: { $first: { $filter: { input: "$mappedFruits", cond: { $eq: ["$$this.fruitUid", "$$childItem.child"] } } } } } ] } } } } ] } } } } }, // 移除临时的mappedFruits字段 { $project: { mappedFruits: 0 } } ])
核心思路说明
- 嵌套$lookup搞定链式关联:在关联
fruits的内部管道里先完成fruits与tools的替换,不用分开写多次聚合,一次搞定两层关联。 - 深层数组处理:用
$reduce+$concatArrays把distributors里所有深层的child值提取出来,确保能精准匹配到对应的fruits文档;再用$map+$filter逐个替换嵌套字段,保留原文档的其他结构不变。 - 适配小数据量场景:因为
fruits和tools最多只有10条数据,用$filter遍历完全没性能问题,不用搞复杂的索引优化。
关键步骤拆解
- 预处理fruits文档:先把
fruits里的variants.tools替换成完整的tools文档,同时只保留distributors实际用到的那些fruits,减少后续处理的数据量。 - 替换distributors的child字段:遍历
distributors的嵌套数组,把每个child的UID替换成预处理好的完整fruits文档,同时保留原有的分类结构。
内容的提问来源于stack exchange,提问作者Lucien Chardon
相关产品推荐
相关产品推荐

