如何利用$cond将两段MongoDB用户更新查询合并为一个?
合并MongoDB数组更新与新增操作
原始代码逻辑
用户现有两段查询,逻辑如下:
- 当
override为true且userUpdate.user存在于users数组中时,更新该用户的role - 无论
override是否为true,只要userUpdate.user不在users数组中,就将该用户对象添加到数组
原始代码:
if (override) { await ProjectModel.findOneAndUpdate( { _id: projectId, users: { $elemMatch: { user: userUpdate.user } } }, { $set: { 'users.$.role': userUpdate.role } } ); } await ProjectModel.findOneAndUpdate( { _id: projectId, users: { $not: { $elemMatch: { user: userUpdate.user } } } }, { $addToSet: { users: userUpdate } } );
其中userUpdate是包含user(ObjectId类型)和role(字符串类型)的对象。
合并后的解决方案
可以利用MongoDB 4.2+支持的聚合管道更新,通过$cond条件判断实现逻辑合并,只需要一次findOneAndUpdate调用:
await ProjectModel.findOneAndUpdate( { _id: projectId }, [ { $set: { users: { $cond: [ // 条件:用户已存在且override为true { $and: [ { $in: [userUpdate.user, '$users.user'] }, override ] }, // 满足条件时:更新对应用户的role { $map: { input: '$users', as: 'item', in: { $cond: [ { $eq: ['$$item.user', userUpdate.user] }, { user: userUpdate.user, role: userUpdate.role }, '$$item' ] } } }, // 不满足条件时:判断是否需要新增用户 { $cond: [ { $in: [userUpdate.user, '$users.user'] }, // 用户已存在但override为false,保留原数组 '$users', // 用户不存在,添加新用户 { $concatArrays: ['$users', [userUpdate]] } ] } ] } } } ], { new: true } // 可选:返回更新后的文档 );
逻辑说明
- 外层$cond判断:先检查目标用户是否存在且
override为true- 满足时:用
$map遍历users数组,匹配到目标用户就替换其role,其他用户保持不变 - 不满足时:进入内层
$cond判断
- 满足时:用
- 内层$cond判断:
- 如果用户已存在但
override为false,直接返回原users数组,不做修改 - 如果用户不存在,用
$concatArrays将新用户对象追加到数组末尾(效果等同于$addToSet,因为user是唯一的ObjectId)
- 如果用户已存在但
注意事项
- 该方案依赖MongoDB 4.2及以上版本,因为只有4.2+才支持在更新操作中使用聚合管道
- 如果需要确保
users数组中user的唯一性,$concatArrays和$addToSet效果一致(因为user是ObjectId,不会重复);如果业务场景可能出现重复的user值,建议替换为$addToSet以保证唯一性
内容的提问来源于stack exchange,提问作者Ankeeta Sahoo
相关产品推荐
相关产品推荐

