MongoDB数组元素处理:不存在则推入,存在则更新指定字段
嘿,这个需求我之前也碰到过,刚好可以用MongoDB的更新操作完美解决。分两种场景给你说明,优先推荐原子性的方案,毕竟数据操作的原子性很重要:
解决方案1:原子性更新(MongoDB 4.2+)
从MongoDB 4.2开始,支持在更新操作中使用聚合管道,这样我们可以在单个原子操作里完成「判断-更新/插入」的逻辑,避免多步操作可能带来的并发问题。
具体代码如下,你只需要替换对应的_id、目标userId和internalUserId即可:
db.yourCollectionName.updateOne( { "_id": ObjectId("5adef141502f49a6a2812016") }, // 定位目标文档 [ { $set: { users: { $cond: { // 第一步:判断目标userId是否已经存在于users数组中 if: { $in: ["1111111", "$users.userId"] }, // 如果存在:遍历数组,替换匹配用户的internalUserId then: { $map: { input: "$users", as: "user", in: { $cond: { if: { $eq: ["$$user.userId", "1111111"] }, then: { userId: "1111111", internalUserId: "bbbb" }, else: "$$user" // 其他用户保持原样 } } } }, // 如果不存在:把新用户对象追加到数组末尾 else: { $concatArrays: ["$users", [{ userId: "1111111", internalUserId: "bbbb" }]] } } } } } ] )
执行这段代码后,就会得到你想要的结果:
{ "_id" : ObjectId("5adef141502f49a6a2812016"), "productId" : "123456", "users" : [ { "userId" : "1111111", "internalUserId" : "bbbb" }, { "userId" : "2222222", "internalUserId" : "dsdasd" } ] }
解决方案2:旧版本MongoDB兼容方案(低于4.2)
如果你的MongoDB版本低于4.2,没法用聚合管道更新,那就只能分两步操作——先尝试更新已存在的用户,若没有匹配到,再插入新用户:
- 先更新匹配userId的用户的internalUserId:
db.yourCollectionName.updateOne( { "_id": ObjectId("5adef141502f49a6a2812016"), "users.userId": "1111111" }, { "$set": { "users.$.internalUserId": "bbbb" } } )
- 检查上一步的修改结果,如果
modifiedCount为0(说明没有找到对应userId),就执行插入:
db.yourCollectionName.updateOne( { "_id": ObjectId("5adef141502f49a6a2812016"), "users.userId": { "$ne": "1111111" } }, { "$push": { "users": { "userId": "1111111", "internalUserId": "bbbb" } } } )
注意:这种方式不是原子性的,如果有并发操作,可能会出现重复插入的情况,所以优先推荐第一种方案。
内容的提问来源于stack exchange,提问作者Yonatan Shorani
相关产品推荐
相关产品推荐

