MongoDB - Update or Insert object in array

2019-01-23 05:41发布

问题:

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.

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.

and

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

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

What is the best way to do this in MongoDB?

回答1:

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


回答2:

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

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.



回答3:

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.



标签: mongodb