MongoDB向文档数组添加元素时如何仅满足指定条件才执行更新
解决方案
你可以通过$expr操作符在查询层实现同文档字段比较校验,再配合聚合更新管道实现状态的条件修改,所有逻辑都在数据库层面执行,天然避免并发问题,完整实现代码如下:
Game.findOneAndUpdate( // 过滤条件:只有游戏ID匹配且当前玩家数小于最大玩家数的文档才会被更新 { _id: 'randomObjectId', $expr: { $lt: [{ $size: '$players' }, '$maxPlayers'] } }, // 聚合更新管道,按需求修改数据 [ // 第一步:添加新玩家到数组,自动去重 { $set: { players: { $addToSet: { input: '$players', elements: ['playerObjId'] } } } }, // 第二步:仅当玩家数满时修改状态为已满员 { $set: { status: { $cond: { if: { $eq: [{ $size: '$players' }, '$maxPlayers'] }, then: 'fully-booked', else: '$status' } } } } ], { new: true, // 返回更新后的文档 runValidators: true // 可选,开启你的Schema字段校验 } ).then(updatedGame => { if (!updatedGame) { // 没有匹配到可更新的文档,说明游戏已经满员 console.log('游戏已满员,无法加入') // 这里可以返回对应的错误响应 return } // 加入成功,updatedGame为更新后的游戏文档 console.log('玩家加入成功', updatedGame) })
关键逻辑说明
- 查询层的
$expr操作符支持在查询条件中比较同一个文档的两个字段值,直接在数据库层面就过滤掉了已经满员的游戏,不需要先查再判断再更新,避免了并发场景下的超员问题。 - 用数组包裹的更新参数是MongoDB 4.2及以上版本支持的聚合更新管道,通过
$cond条件判断符实现了「仅当玩家数满时修改状态」的需求,不需要额外的业务层判断。 - 如果返回的
updatedGame为空,说明当前游戏已经满员,直接返回错误即可。
内容的提问来源于stack exchange,提问作者Gabriel Lupu
相关产品推荐
相关产品推荐

