MongoDB-在数组中更新或插入对象

MongoDB-在数组中更新或插入对象,mongodb,Mongodb,我有以下收藏 { "_id" : ObjectId("57315ba4846dd82425ca2408"), "myarray" : [ { userId : ObjectId("570ca5e48dbe673802c2d035"), point : 5 }, { userId :

我有以下收藏

{
    "_id" : ObjectId("57315ba4846dd82425ca2408"),
    "myarray" : [ 
        {
            userId : ObjectId("570ca5e48dbe673802c2d035"),
            point : 5
        },
        {
            userId : ObjectId("613ca5e48dbe673802c2d521"),
            point : 2
        },        
     ]
}
这些是我的问题 我想进入
myarray
如果
userId
不存在,应该将它附加到
myarray
中。如果存在
userId
,则应将其更新到点

我找到了这个 但如果
userId
不存在,则不会发生任何事情

和 但如果
userId
对象已经存在,它将再次推送

在MongoDB中执行此操作的最佳方法是什么?

不幸的是,在嵌入式阵列上无法执行“upsert”操作。运算符根本不存在,因此在一条语句中不可能这样做。因此,必须执行两个更新操作才能执行所需操作。另外,这两个更新的应用顺序对于获得期望的结果也很重要。

试试这个

db.collection.update(
    { _id : ObjectId("57315ba4846dd82425ca2408")},
    { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
    { _id : ObjectId("57315ba4846dd82425ca2408")},
    { $push: {"myarray": {
        userId:ObjectId("570ca5e48dbe673802c2d035"),
        point: 10
    }}
)
解释: 在第一个语句中,
$pull
从文档上的数组中删除具有
userId=ObjectId(“570ca5e48dbe673802c2d035”)
的元素,其中
\u id=ObjectId(“57315ba4846dd82425ca2408”)

在第二个
$push
插入
这个对象
{userId:ObjectId(“570ca5e48dbe673802c2d035”),point:10}
在同一个数组中。

Flying Fisher接受的答案是先删除现有记录,然后再推送它

更安全的方法(常识)是首先尝试更新记录,如果没有找到匹配项,则插入它,如下所示:

// first try to overwrite existing value
var result = db.collection.update(
   {
       _id : ObjectId("57315ba4846dd82425ca2408"),
       "myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
   },
   {
       $set: {"myarray.$.point": {point: 10}}
   }
);
// you probably need to modify the following if-statement to some async callback
// checking depending on your server-side code and mongodb-driver
if(!result.nMatched)
{
    // record not found, so create a new entry
    // this can be done using $addToSet:
    db.collection.update(
        {
            _id: ObjectId("57315ba4846dd82425ca2408")
        },
        {
            $addToSet: {
                myarray: {
                    userId: ObjectId("570ca5e48dbe673802c2d035"),
                    point: 10
                }
            }
        }
    );
    // OR (the equivalent) using $push:
    db.collection.update(
        {
            _id: ObjectId("57315ba4846dd82425ca2408"),
            "myarray.userId": {$ne: ObjectId("570ca5e48dbe673802c2d035"}}
        },
        {
            $push: {
                myarray: {
                    userId: ObjectId("570ca5e48dbe673802c2d035"),
                    point: 10
                }
            }
        }
    );
}

这也会提高(常识,未测试)性能,如果在大多数情况下记录已经存在,则只会执行第一个查询。

当您想要更新或在数组中插入值时,请尝试执行它

数据库中的对象

key:name,
key1:name1,
arr:[
    {
        val:1,
        val2:1
    }
]
质疑


在MongoDB 3.6中,现在可以。

我还没有找到任何基于单原子查询的解决方案。取而代之的是基于两个查询序列的3种方法:

  • 始终
    $pull
    (从阵列中删除项目),然后
    $push
    (将更新的项目添加到阵列)

  • 尝试
    $set
    (如果存在,则更新数组中的项),然后获取结果并检查更新操作是否成功,或者是否需要
    $push
    (插入项)

  • 始终
    $addToSet
    (如果不存在项,则添加该项),然后始终
    $set
    更新数组中的项

       db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
                             myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
                            { 
                               $addToSet : {
                                             myarray: {
                                                        userId: ObjectId("570ca5e48dbe673802c2d035"),
                                                        point: 10
                                                       }
                                            }
                             },
                            { multi: false, upsert: false});
    
       db.collection.update({
                              _id: ObjectId("57315ba4846dd82425ca2408"),
                               "myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
                            },
                            { $set : { myArray.$.point: 10 } },
                            { multi: false, upsert: false});
    
  • 第一种和第二种方式是不安全的,因此必须建立事务,以避免两个并发请求推送相同的项目产生重复


    第三条路更安全。
    $addToSet
    仅在项目不存在时添加,否则不会发生任何事情。在两个并发请求的情况下,只有一个请求将缺少的项添加到数组中

    数组更新和创建不要在一个查询下混合使用,如果您非常关心原子性,那么有以下解决方案:

    将您的模式标准化为

    {
        "_id" : ObjectId("57315ba4846dd82425ca2408"),
        userId : ObjectId("570ca5e48dbe673802c2d035"),
        point : 5
    }
    
    有一个名为“从MongoDB v4.2开始”的选项

    • 检查
      myarray.userId
      中是否存在
      userId
      的条件
      $cond
    • 如果是,则
      $map
      迭代
      myarray
      数组的循环,并检查条件
      userId
      是否匹配,然后使用
      $mergeObjects
      与新文档合并
    • 如果否,则
      $concatarray
      到concat新对象和
      myarray

    作为参考,我在下面的链接中添加了详细的解决方案,该链接涵盖了相同的场景,即
    如何将新对象添加到对象数组中,除非存在特定的对象值(例如userId),在这种情况下,更新对象
    :如果能为将来的读者明确说明代码的作用,那就太好了OK,这很好,但在MongoDb中,id应该自动插入,而您手动将其添加到两个不同的CAL中,这是一个很好的想法,但代价很高。如果两个并发请求同时进入if语句,会发生什么情况?两个
    $addToSet
    中只有一个有效,不是吗?@FabioFormosa是的,但你是对的,这仍然是不安全的。如果同时,不仅执行了
    $addToSet
    ,而且更新了点。。。此代码肯定应该在事务中。在一个查询中可以创建或更新此架构文档。幸运的是,upsert选项并不能真正帮助解决此问题,因为它需要一个(即所有数组元素都在查询中显式定义)。如果未找到匹配项,则upsert将创建一个完整的新文档,但它不会将新元素“upsert”到数组中。方法#3如何安全?如果并发请求修改了数组元素,使得
    不再是
    10
    ,那么第一个调用将为相同的
    用户ID
    向数组添加第二个元素。唯一的竞争条件是:元素不在数组中,第一个请求完成
    $addToSet
    ,现在,第二个请求运行
    更新
    ,最后第一个请求运行
    $set
    。因为只有一个
    $addToSet
    命中,所以总是只有一个元素(从来没有两个!)!点字段的值将是执行
    $set
    的最后一个请求的值。您不应该对执行
    $addToSet
    的请求感兴趣。因此,这是一种更安全的方法,因为它不属于双插入。如果相同的
    userId
    存在一个数组元素,但具有不同的
    值,则相同的
    userId
    的第二个元素将添加到数组中。
    $addToSet
    方法只有在
    10
    永远是
    唯一可能的值时才有效,这是不可能的。啊,现在我明白你的意思了。我错过了
    $elemMatch
    ,因为我没有滚动足够远。在这种情况下,你是对的——它应该会起作用。您甚至可以使用
    $push
    而不是
    $addToSet
    ,因为
    var query = {
        $inc:{
            "arr.0.val": 2,
            "arr.0.val2": 2
        }
    }
    .updateOne( { "key": name }, query, { upsert: true }
    
    key:name,
    key1:name1,
    arr:[
        {
            val:3,
            val2:3
        }
    ]
    
    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
    )
    
    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   {
                     $push: {
                              "myarray": {
                                          userId:ObjectId("570ca5e48dbe673802c2d035"),
                                          point: 10
                                         }
                             }
                    }
    )
    
    var result = db.collection.update(
        {
           _id : ObjectId("57315ba4846dd82425ca2408"),
          "myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
        },
        {
           $set: {"myarray.$.point": {point: 10}}
        }
     );
    
    if(!result.nMatched){
           db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
                                {
                                  $addToSet: {
                                               myarray: {
                                                  userId: ObjectId("570ca5e48dbe673802c2d035"),
                                                  point: 10
                                              }
                                }
           );
    
       db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
                             myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
                            { 
                               $addToSet : {
                                             myarray: {
                                                        userId: ObjectId("570ca5e48dbe673802c2d035"),
                                                        point: 10
                                                       }
                                            }
                             },
                            { multi: false, upsert: false});
    
       db.collection.update({
                              _id: ObjectId("57315ba4846dd82425ca2408"),
                               "myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
                            },
                            { $set : { myArray.$.point: 10 } },
                            { multi: false, upsert: false});
    
    {
        "_id" : ObjectId("57315ba4846dd82425ca2408"),
        userId : ObjectId("570ca5e48dbe673802c2d035"),
        point : 5
    }
    
    let _id = ObjectId("57315ba4846dd82425ca2408");
    let uodateDoc = {
      userId: ObjectId("570ca5e48dbe673802c2d035"),
      point: 10
    };
    
    db.collection.update(
      { _id: _id },
      [{
        $set: {
          myarray: {
            $cond: [
              { $in: [updateDoc.userId, "$myarray.userId"] },
              {
                $map: {
                  input: "$myarray",
                  in: {
                    $mergeObjects: [
                      "$$this",
                      {
                        $cond: [
                          { $eq: ["$$this.userId", updateDoc.userId] },
                          uodateDoc,
                          {}
                        ]
                      }
                    ]
                  }
                }
              },
              { $concatArrays: ["$myarray", [uodateDoc]] }
            ]
          }
        }
      }]
    )