MongoDB聚合框架如何实现$lookup返回字段的嵌套关联填充
MongoDB嵌套关联填充聚合实现
首先先修正你现有代码的一个小错误:answer集合里关联问题的字段是questionId(小驼峰,Id结尾),你当前写的foreignField: "questionID"是大写D的ID,会匹配不到数据,先把这个字段名改对。
要实现usersAnswered数组里每条answer的userID自动关联填充profile数据,有两种成熟写法,优先推荐第一种,性能更好:
写法1:嵌套管道$lookup(推荐,MongoDB 3.6+支持)
这种写法不需要把关联到的answer数组拆了重组,直接在关联answer的管道内完成第二层用户关联,步骤更少、性能更高,代码如下:
question.aggregate([ { $match: { category: "gaming", }, }, { $lookup: { from: "answers", let: { qId: "$_id" }, // 把当前question的_id声明为管道内可用变量 pipeline: [ // 第一步:筛选出属于当前问题的所有answer { $match: { $expr: { $eq: ["$questionId", "$$qId"] } } }, // 第二步:关联profile集合,填充用户信息 { $lookup: { from: "profiles", localField: "userID", foreignField: "_id", as: "userID" // 直接覆盖原answer里存的字符串用户ID } }, // $lookup默认返回数组,因为用户是1对1匹配,直接取第一个元素转成对象 { $unwind: "$userID" } ], as: "usersAnswered", }, }, ])
运行后返回的结构和你预期完全一致,不需要额外做字段映射。
写法2:拆分重组兼容写法(适配3.6以下旧版本MongoDB)
如果你的MongoDB版本低于3.6不支持lookup内写pipeline,可以用先拆数组、关联后重组的方式实现,代码如下:
question.aggregate([ { $match: { category: "gaming", }, }, { $lookup: { from: "answers", localField: "_id", foreignField: "questionId", as: "usersAnswered", }, }, // 把usersAnswered数组拆成单条answer文档 { $unwind: "$usersAnswered" }, // 关联每条answer对应的用户信息 { $lookup: { from: "profiles", localField: "usersAnswered.userID", foreignField: "_id", as: "usersAnswered.userID" } }, // 把用户信息的单元素数组转成对象 { $unwind: "$usersAnswered.userID" }, // 按question ID分组,把拆分的answer重新拼回数组 { $group: { _id: "$_id", question: { $first: "$question" }, category: { $first: "$category" }, usersAnswered: { $push: "$usersAnswered" } } } ])
注意事项
- 如果你用Mongoose写代码,
from字段要填MongoDB中实际的集合名,Mongoose默认会把模型名转成小写复数形式,比如Profile模型对应集合是profiles,Answer模型对应集合是answers,不要写错。 - 第一种写法少了一次$unwind和$group操作,数据量越大性能优势越明显,版本支持的话优先用。
内容的提问来源于stack exchange,提问作者Ali Abyer
相关产品推荐
相关产品推荐

