如何在Mongoose聚合管道中填充ratings.postedBy字段?
问题描述
我有如下结构的Product模型:
const product = { ...other fields, ratings: [ { star: Number, postedBy: { type: ObjectId, ref: "User", }, }, ], }
使用普通find查询时,通过populate方法可以正确填充ratings.postedBy字段,代码如下:
const product = Product .find({}) .populate({ path: "ratings.postedBy", select: "_id name email picture", });
能得到符合预期的嵌套结构结果。但尝试用聚合管道的$lookup实现时,返回的ratings结构被改变,无法得到和find查询一致的嵌套结果,我的尝试代码如下:
const product = await Product.aggregate([ ...other pipeline stages { //populate ratings.postedBy $lookup: { from: "users", //table name localField: "ratings.postedBy", foreignField: "_id", as: "ratings.postedBy", pipeline: [{ $project: { _id: 1, name: 1, star: 1 } }], }, }, ]);
需要正确的$lookup实现方案。
解决方案
直接用ratings.postedBy作为as字段会把原数组里的每个postedBy替换成匹配到的用户数组,而非单个对象。要实现和populate一致的嵌套结构,需要先拆解ratings数组,逐个处理后重组,具体代码如下:
const product = await Product.aggregate([ // 保留你的其他管道阶段 { $unwind: "$ratings" }, // 拆分ratings数组,将每个rating转为独立文档 { $lookup: { from: "users", localField: "ratings.postedBy", foreignField: "_id", as: "ratings.postedBy", pipeline: [{ $project: { _id: 1, name: 1, email: 1, picture: 1 } }] // 对应populate的select } }, { $unwind: "$ratings.postedBy" }, // 将lookup返回的用户数组转为单个对象 { $group: { _id: "$_id", // 按产品ID聚合 productFields: { $first: "$$ROOT" }, // 保留产品的其他所有字段 ratings: { $push: "$ratings" } // 重组ratings数组 } }, { $replaceRoot: { newRoot: { $mergeObjects: [ "$productFields", { ratings: "$ratings" } ] } } } ]);
核心逻辑说明
$unwind: "$ratings":把嵌套的ratings数组拆分成独立文档,让$lookup能精准关联每个rating的postedBy$lookup:针对每个拆分后的rating,查询对应的用户信息,用pipeline筛选需要的字段,等价于populate的select$unwind: "$ratings.postedBy":因为$lookup默认返回数组,这里把它转为单个对象,和原rating的结构匹配$group:按产品ID重新聚合,把处理后的rating对象重组回数组,同时保留产品的其他原始字段$replaceRoot:合并字段,恢复产品原本的结构
内容的提问来源于stack exchange,提问作者desh
相关产品推荐
相关产品推荐

