MongoDB原子操作:数组子文档字段递减/新增及返回更新文档
MongoDB 原子更新实现方案
完全可以通过findOneAndUpdate结合聚合更新管道实现需求,MongoDB的单文档更新操作本身具备原子性,能完美应对并发请求场景,同时可配置返回更新后的文档用于配额验证。
实现代码示例
假设集合名为quotas,传入operatorId(目标文档_id)和userId参数,代码如下:
const updatedDocument = await db.collection('quotas').findOneAndUpdate( { _id: operatorId }, // 匹配指定operatorId的文档 [ // 第一步:将monthlyQuotas字段值减1 { $set: { monthlyQuotas: { $subtract: ['$monthlyQuotas', 1] } } }, // 第二步:条件处理userUsages数组 { $set: { userUsages: { $cond: { // 判断数组中是否存在匹配userId的子文档 if: { $in: [userId, '$userUsages.userId'] }, then: { // 存在则更新对应子文档的dailyQuotas减1 $map: { input: '$userUsages', as: 'usage', in: { $cond: { if: { $eq: ['$$usage.userId', userId] }, then: { userId: '$$usage.userId', dailyQuotas: { $subtract: ['$$usage.dailyQuotas', 1] } }, else: '$$usage' } } } }, else: { // 不存在则向数组添加新子文档 $concatArrays: ['$userUsages', [{ userId: userId, dailyQuotas: 9 }]] } } } } } ], { returnDocument: 'after' } // 返回更新后的完整文档 );
关键说明
- 原子性保障:整个更新管道作为单原子操作执行,并发请求下不会出现中间状态的读写冲突,确保数据一致性。
- 返回更新后文档:通过
returnDocument: 'after'参数(MongoDB 4.2+支持;旧版本可使用returnNewDocument: true)直接获取更新后的文档,方便验证配额变化。 - 逻辑拆解:
- 用
$subtract对monthlyQuotas执行减1操作; - 用
$in判断数组中是否存在目标userId; - 匹配到目标用户时,用
$map遍历数组,仅更新对应子文档的dailyQuotas; - 未匹配到目标用户时,用
$concatArrays向数组追加新的配额记录。
- 用
版本兼容提示
如果使用MongoDB 4.2之前的版本,无法直接使用聚合更新管道,需通过findAndModify结合更复杂的条件判断实现,但推荐升级至4.2+版本以获得更简洁、可靠的实现方式。
内容的提问来源于stack exchange,提问作者msalafia
相关产品推荐
相关产品推荐

