如何在Mongoose聚合中检测用户投票的Poll选项并返回其ID
解决Mongoose聚合中嵌套数组的用户投票检测问题
我用Mongoose编写了一个聚合管道,用于获取帖子及其关联的点赞、投票数据,通过lookup实现关联。已经通过$in完成了用户是否点赞的检测,但在检测用户投票选项时遇到嵌套对象数组的问题,嵌套$in未生效,希望通过$addFields添加pollVote字段存储用户所选投票选项的ID,避免在控制器中处理逻辑。
现有聚合管道代码
return Post.aggregate([ {"$match": { "issuer.companyId": new mongoose.Types.ObjectId(startupId) } }, {"$sort": { "createdAt": -1 } }, // 点赞检测 {"$lookup":{ "from":"postlikes", "localField":"_id", "foreignField":"_id", "as":"likes" }}, {"$addFields": { "likes": {"$first": "$likes.likes._id"}, } }, {"$addFields": { "liked": {"$in": [ new mongoose.Types.ObjectId(userId), "$likes" ] } } }, {"$unset": "likes"}, // 投票检测(待完善) {"$lookup":{ "from":"polls", "localField":"_id", "foreignField":"_id", "as":"polls" }}, {"$addFields": { "polls": {"$first": "$polls.options"}, } }, // todo: 检测用户ID是否在某个投票数组中,返回选中的选项ID ])
当前输出结果
[ { "_id": "6414698183b285f6f16a17be", "poll": [ { "text": "Facebook", "votes": 0, "_id": "6414698183b285f6f16a17c0" }, { "text": "TikTok", "votes": 2, "_id": "6414698183b285f6f16a17c1" } ], "liked": true, "polls": [ { "_id": "6414698183b285f6f16a17c0", "voters": [] }, { "_id": "6414698183b285f6f16a17c1", "voters": [ { "name": "XX", "timestamp": 1679218784, "_id": "63d3892fd1cc6b5711c67799" } ] } ] }, ]
预期输出结果
[ { "_id": "6414698183b285f6f16a17be", "poll": [ { "text": "Facebook", "votes": 0, "_id": "6414698183b285f6f16a17c0" }, { "text": "TikTok", "votes": 2, "_id": "6414698183b285f6f16a17c1" } ], "liked": true, "pollVote": "6414698183b285f6f16a17c1" // 用户投了TikTok选项 }, ]
解决方案
通过$filter筛选包含当前用户的投票选项,结合$map提取投票者ID数组,再用$arrayElemAt获取匹配选项的ID,最后清理临时字段。修改后的聚合管道如下:
return Post.aggregate([ {"$match": { "issuer.companyId": new mongoose.Types.ObjectId(startupId) } }, {"$sort": { "createdAt": -1 } }, // 点赞检测部分保持不变 {"$lookup":{ "from":"postlikes", "localField":"_id", "foreignField":"_id", "as":"likes" }}, {"$addFields": { "likes": {"$first": "$likes.likes._id"}, } }, {"$addFields": { "liked": {"$in": [ new mongoose.Types.ObjectId(userId), "$likes" ] } } }, {"$unset": "likes"}, // 投票检测修改部分 {"$lookup":{ "from":"polls", "localField":"_id", "foreignField":"_id", "as":"polls" }}, {"$addFields": { "polls": {"$first": "$polls.options"}, } }, // 筛选包含当前用户的投票选项 {"$addFields": { "pollVote": { "$arrayElemAt": [ {"$filter": { "input": "$polls", "as": "option", "cond": { "$in": [ new mongoose.Types.ObjectId(userId), {"$map": { "input": "$$option.voters", "as": "voter", "in": "$$voter._id" }} ] } }}, 0 ] } }}, // 提取选项ID,未投票则返回null {"$addFields": { "pollVote": {"$ifNull": ["$pollVote._id", null]} }}, // 移除临时polls字段 {"$unset": "polls"} ])
关键逻辑说明
$map+$in:将每个投票选项的voters数组转换为仅包含用户ID的数组,再用$in判断当前用户是否在其中。$filter:筛选出符合条件的投票选项(一个用户仅能投一个选项)。$arrayElemAt:取出筛选结果的第一个元素,确保只获取用户的投票选项。$ifNull:处理用户未投票的场景,此时pollVote设为null。
内容的提问来源于stack exchange,提问作者Jakob
相关产品推荐
相关产品推荐

