You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用聚合查询结果更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 21:20:19