如何用聚合查询结果更新MongoDB集合文档?投票场景技术咨询
投票网站Polls与Votes集合同步问题
问题背景
开发投票网站时,已通过聚合管道统计出指定Poll下各电影的得票数,需要将结果同步更新到Polls集合的对应文档中。相关Schema与现有聚合代码如下:
Vote Schema
poll: ObjectId votedMovies: Array 0: Object id: ObjectId title: string
Poll Schema
_id: ObjectID pollType: String totalVotes: Number movies: Array 0: Object id: ObjectID title: String votes: Number
现有聚合管道代码
let voteCollection = await db.collection('votes').aggregate([ { $match: {poll: id} }, { $unwind: "$votedMovies" }, { $group: {_id: "$votedMovies.id", totalVotes: {$sum: 1}} } ]).toArray()
聚合输出示例
[{"_id":10674,"totalVotes":2}, {"_id":99861,"totalVotes":1}, {"_id":299534,"totalVotes":4}, {"_id":637157,"totalVotes":3}, {"_id":24428,"totalVotes":5}]
问题解答
1. 聚合管道方向是否正确?
你的聚合管道方向完全正确:
$match精准过滤指定Poll的投票记录,避免全表扫描,提升统计效率;$unwind拆分votedMovies数组为单条文档,为后续分组统计提供基础;$group按电影ID分组统计得票总数,完全匹配需求。
如果需要同时统计Poll的总票数,可在管道末尾添加额外计算逻辑,但当前逻辑已满足单电影得票统计的核心需求。
2. 如何更新Poll文档同步得票数?
提供两种可行方案,可根据数据规模选择:
方案一:循环更新单部电影(适合小规模数据)
遍历聚合结果,逐个更新Poll文档中的对应电影得票数:
const pollId = id; // 目标Poll的ID const voteStats = await db.collection('votes').aggregate([ { $match: { poll: pollId } }, { $unwind: "$votedMovies" }, { $group: { _id: "$votedMovies.id", totalVotes: { $sum: 1 } } } ]).toArray(); // 计算该Poll的总票数 const totalVotes = voteStats.reduce((sum, item) => sum + item.totalVotes, 0); // 先更新总票数 await db.collection('polls').updateOne( { _id: pollId }, { $set: { totalVotes: totalVotes } } ); // 循环更新每部电影的得票数 for (const stat of voteStats) { await db.collection('polls').updateOne( { _id: pollId, "movies.id": stat._id }, { $set: { "movies.$.votes": stat.totalVotes } } ); }
注:若Poll中存在未被投票的电影,其
votes字段会保留原值;如需重置为0,可先执行一次批量设0操作,再执行上述更新。
方案二:聚合+原子更新(适合大规模数据,效率更高)
通过聚合生成更新映射表,再用原子操作一次性更新所有数据:
const pollId = id; // 聚合整理更新所需的结构 const updateData = await db.collection('votes').aggregate([ { $match: { poll: pollId } }, { $unwind: "$votedMovies" }, { $group: { _id: "$votedMovies.id", totalVotes: { $sum: 1 } } }, { $group: { _id: null, movieVotes: { $push: { k: { $toString: "$_id" }, v: "$totalVotes" } }, totalVotes: { $sum: "$totalVotes" } } }, { $project: { _id: 0, totalVotes: 1, movieVoteMap: { $arrayToObject: "$movieVotes" } } } ]).toArray(); if (updateData.length > 0) { const { totalVotes, movieVoteMap } = updateData[0]; // 一次性更新总票数与所有电影的得票数 await db.collection('polls').updateOne( { _id: pollId }, [ { $set: { totalVotes: totalVotes } }, { $set: { movies: { $map: { input: "$movies", as: "movie", in: { id: "$$movie.id", title: "$$movie.title", votes: { $ifNull: [ movieVoteMap[{$toString: "$$movie.id"}], 0 // 未被投票的电影设为0 ] } } } } } } ] ); }
该方案通过聚合直接生成更新映射,利用$map和$ifNull实现批量更新,原子性更强,适合投票数据较多的场景。
内容的提问来源于stack exchange,提问作者Jacob Edie
相关产品推荐
相关产品推荐

