使用Aggregate管道更新数组内对象并计算总和的实现方案
MongoDB聚合管道实现玩家生命值更新与总生命值计算
现有文档结构
// Game 集合文档 { number: "001", players: [ { _id: ObjectId("123"), lives: 0 }, { _id: ObjectId("124"), lives: 2 }, { _id: ObjectId("125"), lives: 3 } ], totalLives: 5 }
需求
- 根据
number定位目标游戏,更新指定玩家(示例中_id=123)的lives字段 - 同步更新该游戏的
totalLives为所有玩家lives的总和
尝试的代码(存在问题)
原尝试用updateOne直接更新,但$sum无法在普通更新的$set中使用,代码如下:
await Games.updateOne( { number: game.number, "player._id": new ObjectId(newPlayer._id), }, { $set: { "players.$[player].lives": newPlayer.lives, totalLives: { $sum: "$players.lives" }, }, }, { arrayFilters: [{ "player._id": new ObjectId(newPlayer._id) }], } )
正确实现方案(聚合管道式更新)
MongoDB 4.2及以上版本支持用聚合管道作为更新操作的内容,可先修改玩家生命值,再计算总生命值:
await Games.updateOne( // 匹配条件:定位指定number的游戏 { number: game.number }, // 聚合管道更新逻辑 [ // 第一步:更新指定玩家的lives字段 { $set: { players: { $map: { input: "$players", as: "p", in: { $cond: [ { $eq: ["$$p._id", new ObjectId(newPlayer._id)] }, { ..."$$p", lives: newPlayer.lives }, // 替换目标玩家的lives "$$p" // 其他玩家保持原样 ] } } } } }, // 第二步:计算所有玩家lives的总和,更新totalLives { $set: { totalLives: { $sum: "$players.lives" } } } ] )
代码说明
- 匹配阶段:仅通过
number定位目标游戏即可,玩家匹配逻辑放在聚合管道内处理 - $map更新玩家:遍历
players数组,通过$cond判断当前玩家ID是否为目标ID,是则替换lives值,否则保留原数据 - 计算总生命值:利用
$sum直接对更新后的players.lives数组求和,赋值给totalLives
内容的提问来源于stack exchange,提问作者AES
相关产品推荐
相关产品推荐

