MongoDB聚合:如何将关联数据转为根文档并保留父文档数据?
MongoDB聚合管道实现:将评论、点赞与对应帖子关联为独立文档
需求说明
现有posts、comments、likes三个集合,需构建聚合管道,让每条评论、每条点赞都作为独立文档返回,且每个文档包含对应的完整帖子数据,同时关联评论/点赞发起者的用户信息。
集合结构示例
posts集合
[ { "_id":"62a25c932c38e92bdcd32094", "text": "Text to be written by the creator of the post.", "userId": "621b814eed5b141c19c2d34b", "createdAt": { "$date": "2022-06-09T20:48:19.528Z" } } ]
comments集合
[ { "_id": { "$oid": "62a3677843c638676e126815" }, "postId": { "$oid": "62a25c932c38e92bdcd32094" }, "text": "Text to be written by the creator of the comment.", "userId": { "$oid": "6255d5d7eb15644e44b84240" }, "createdAt": { "$date": "2022-06-10T15:47:04.429Z" } } ]
likes集合
[ { "_id": { "$oid": "62a30565f8ded22defc7bb78" }, "postId": { "$oid": "62a25c932c38e92bdcd32094" }, "userId": { "$oid": "6255d5d7eb15644e44b84240" }, "createdAt": { "$date": "2022-06-10T08:48:37.203Z" } } ]
聚合管道解决方案
假设存在users集合存储用户信息(包含_id和name字段),以下是完整的聚合管道:
db.posts.aggregate([ // 关联当前帖子的所有评论 { $lookup: { from: "comments", localField: "_id", foreignField: "postId", as: "comments" } }, // 关联当前帖子的所有点赞 { $lookup: { from: "likes", localField: "_id", foreignField: "postId", as: "likes" } }, // 为评论、点赞标记类型,方便后续合并拆分 { $project: { _id: 1, text: 1, userId: 1, createdAt: 1, comments: { $map: { input: "$comments", as: "c", in: { type: "comment", data: "$$c" } } }, likes: { $map: { input: "$likes", as: "l", in: { type: "like", data: "$$l" } } } } }, // 合并评论和点赞数组 { $project: { postData: { _id: "$_id", text: "$text", userId: "$userId", createdAt: "$createdAt" }, activities: { $concatArrays: ["$comments", "$likes"] } } }, // 将合并后的数组拆分为独立文档 { $unwind: "$activities" }, // 关联用户信息 { $lookup: { from: "users", localField: "activities.data.userId", foreignField: "_id", as: "userInfo" } }, // 提取用户信息(取第一个匹配项) { $addFields: { user: { $arrayElemAt: ["$userInfo", 0] } } }, // 整理最终文档结构 { $project: { _id: "$postData._id", text: "$postData.text", userId: "$postData.userId", createdAt: "$postData.createdAt", comment: { $cond: { if: { $eq: ["$activities.type", "comment"] }, then: { _id: "$activities.data._id", user: { name: "$user.name" } }, else: "$$REMOVE" } }, like: { $cond: { if: { $eq: ["$activities.type", "like"] }, then: { _id: "$activities.data._id", user: { name: "$user.name" } }, else: "$$REMOVE" } } } } ])
管道阶段解释
- $lookup(关联评论/点赞):分别从
comments和likes集合中获取当前帖子对应的所有评论、点赞数据。 - $map(标记类型):为每条评论、点赞添加类型标识,方便后续合并后区分类型。
- $concatArrays(合并数组):将评论数组和点赞数组合并为一个统一的活动数组。
- $unwind(拆分文档):把活动数组拆分成独立文档,每条评论/点赞对应一个文档。
- $lookup(关联用户):根据评论/点赞的
userId从users集合获取用户信息。 - $addFields(提取用户):从用户信息数组中取出第一个匹配的用户数据。
- $project(整理结构):根据活动类型,动态生成
comment或like字段,同时保留帖子的核心数据,最终输出符合需求的文档结构。
预期输出
[ { "_id":"62a25c932c38e92bdcd32094", "text": "Text to be written by the creator of the post.", "userId": "621b814eed5b141c19c2d34b", "comment":{ "_id": { "$oid": "62a3677843c638676e126815" }, "user":{ "name": "John" } }, "createdAt": { "$date": "2022-06-09T20:48:19.528Z" } }, { "_id":"62a25c932c38e92bdcd32094", "text": "Text to be written by the creator of the post.", "userId": "621b814eed5b141c19c2d34b", "like":{ "_id": { "$oid": "62a30565f8ded22defc7bb78" }, "user":{ "name": "John" } }, "createdAt": { "$date": "2022-06-09T20:48:19.528Z" } } ]
内容的提问来源于stack exchange,提问作者YulePale
相关产品推荐
相关产品推荐

