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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37427610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 20:45:33  来源:igfitidea点击:

MongoDB - Update or Insert object in array

mongodb

提问by Narupon Tee Srisantitham

I have the following collection

我有以下收藏

{
    "_id" : ObjectId("57315ba4846dd82425ca2408"),
    "myarray" : [ 
             {
                userId : ObjectId("570ca5e48dbe673802c2d035"),
                point : 5
             },
             {
                userId : ObjectId("613ca5e48dbe673802c2d521"),
                point : 2
             },        
     ]
}

These are my questions

这些是我的问题

I want to push into myarray if userId doesn't exist, it should be appended to myarray. If userId exists, it should be updated to point.

如果 userId 不存在,我想推入 myarray,它应该附加到 myarray。如果 userId 存在,则应将其更新为指向。

I found this

我找到了这个

db.collection.update({
  _id : ObjectId("57315ba4846dd82425ca2408"), "myarray.userId" :  ObjectId("570ca5e48dbe673802c2d035")
},{
   $set: {"myarray.$.point": 10}
})

But if userId doesn't exist, nothing happens.

但如果 userId 不存在,则什么也不会发生。

and

db.collection.update({
  _id : ObjectId("57315ba4846dd82425ca2408")
},{
  $push: {"myarray": {userId: ObjectId("570ca5e48dbe673802c2d035"), point: 10}}
})

But if userId object already exists, it will push again.

但是如果 userId 对象已经存在,它会再次推送。

What is the best way to do this in MongoDB?

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

采纳答案by Flying Fisher

Try this

尝试这个

db.collection.update(
    { _id : ObjectId("57315ba4846dd82425ca2408")},
    { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
    { _id : ObjectId("57315ba4846dd82425ca2408")},
    { $push: {"myarray": {
        userId:ObjectId("570ca5e48dbe673802c2d035"),
        point: 10
    }}
)

Explination: in the first statment $pullremoves the element with userId= ObjectId("570ca5e48dbe673802c2d035")from the array on the document where _id = ObjectId("57315ba4846dd82425ca2408")

说明:在第一个语句$pulluserId= ObjectId("570ca5e48dbe673802c2d035")从文档上的数组中删除元素 with_id = ObjectId("57315ba4846dd82425ca2408")

In the second one $pushinserts this object { userId:ObjectId("570ca5e48dbe673802c2d035"), point: 10 }in the same array.

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

回答by Yeti

The accepted answer by Flying Fisher is that the existing record will first be deleted, and then it will be pushed again.

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

A safer approach (common sense) would be to try to update the record first, and if that did not find a match, insert it, like so:

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

// 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
                }
            }
        }
    );
}

This should also give (common sense, untested) an increase in performance, if in most cases the record already exists, only the first query will be executed.

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

回答by Sandesh

Unfortunately "upsert" operation is not possible on embedded array. Operators simply do not exist so that this is not possible in a single statement.Hence you must perform two update operations in order to do what you want. Also the order of application for these two updates is important to get desired result.

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

回答by joniba

In MongoDB 3.6 it is now possible to upsert elements in an array.

在 MongoDB 3.6 中,现在可以在数组中插入元素

回答by Fabio Formosa

I haven't found any solutions based on a one atomic query. Instead there are 3 ways based on a sequence of two queries:

我还没有找到任何基于单原子查询的解决方案。相反,有 3 种基于两个查询序列的方法:

  1. always $pull(to remove the item from array), then $push(to add the updated item to array)

    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
    )
    
    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   {
                     $push: {
                              "myarray": {
                                          userId:ObjectId("570ca5e48dbe673802c2d035"),
                                          point: 10
                                         }
                             }
                    }
    )
    
  2. try to $set(to update the item in array if exists), then get the result and check if the updating operation successed or if a $pushneeds (to insert the item)

    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
                                              }
                                }
           );
    
  3. always $addToSet(to add the item if not exists), then always $setto update the item in array

       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});
    
  1. 总是$pull(从数组中删除项目),然后$push(将更新的项目添加到数组中)

    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
    )
    
    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   {
                     $push: {
                              "myarray": {
                                          userId:ObjectId("570ca5e48dbe673802c2d035"),
                                          point: 10
                                         }
                             }
                    }
    )
    
  2. 尝试$set(如果存在则更新数组中的项目),然后获取结果并检查更新操作是否成功或是否$push需要(插入项目)

    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
                                              }
                                }
           );
    
  3. 始终$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});
    

1 and 2 ways are unsafe, so transaction must be established to avoid two concurrent requests could pushthe same item generating a duplicate.

1 和 2 方式是不安全的,因此必须建立事务以避免两个并发请求可能push导致同一个项目产生重复。

3 way is safe. $addToSetadd only if the item doesn't exist, otherwise nothing happens. In case of two concurrent requests, only one of them add the missing item to the array.

3路是安全的。$addToSet仅当项目不存在时才添加,否则什么都不会发生。在两个并发请求的情况下,只有其中一个将缺少的项添加到数组中。

回答by Gagandeep Kalra

array update and create don't mix in under one query, if you care much about atomicity then there's this solution:

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

normalise your schema to,

将您的架构规范化为,

{
    "_id" : ObjectId("57315ba4846dd82425ca2408"),
    userId : ObjectId("570ca5e48dbe673802c2d035"),
    point : 5
}

回答by Роман Зыков

When you want update or insert value in array try it

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

Object in db

数据库中的对象

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

Query

询问

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
    }
]