Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB - $set to update or push Array element

Tags:

In products collection, i have an Array of recentviews which has 2 fields viewedBy & viewedDate.

In a scenario if i already have a record with viewedby, then i need to update it. For e.g if i have array like this :-

"recentviews" : [          {             "viewedby" : "abc",             "vieweddate" : ISODate("2014-05-08T04:12:47.907Z")         }     ] 

And user is abc, so i need to update the above & if there is no record for abc i have to $push.

I have tried $set as follows :-

db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },                     { $set:                          { "recentviews":                              {                                  viewedby: 'abc',                                 vieweddate: ISODate("2014-05-09T04:12:47.907Z")                              }                         }                       }             ) 

The above query erases all my other elements in Array.

like image 203
Anup Avatar asked Jun 09 '14 06:06

Anup


People also ask

How do you update an element in an array in MongoDB?

To update a set of elements matching certain filters, we must use the filtered positional operator $[<identifier>] where <identifier> is a placeholder for a value that represents a single element of the array. We must then use the third parameter (options) of the updateMany method to specify a set of arrayFilters .

How do I push an array object in MongoDB?

In MongoDB, the $push operator is used to appends a specified value to an array. If the mentioned field is absent in the document to update, the $push operator add it as a new field and includes mentioned value as its element. If the updating field is not an array type field the operation failed.

What is the difference between update and Upsert in MongoDB?

Upsert is a combination of insert and update (inSERT + UPdate = upsert). We can use the upsert with different update methods, i.e., update, findAndModify, and replaceOne. Here in MongoDB, the upsert option is a Boolean value. Suppose the value is true and the documents match the specified query filter.


1 Answers

Actually doing what it seems like you say you are doing is not a singular operation, but I'll walk through the parts required in order to do this or otherwise cover other possible situations.

What you are looking for is in part the positional $ operator. You need part of your query to also "find" the element of the array you want.

db.products.update(     {          "_id": ObjectId("536c55bf9c8fb24c21000095"),         "recentviews.viewedby": "abc"     },     {          "$set": {              "recentviews.$.vieweddate": ISODate("2014-05-09T04:12:47.907Z")         }     } ) 

So the $ stands for the matched position in the array so the update portion knows which item in the array to update. You can access individual fields of the document in the array or just specify the whole document to update at that position.

db.products.update(     {          "_id": ObjectId("536c55bf9c8fb24c21000095"),         "recentviews.viewedby": "abc"     },     {          "$set": {              "recentviews.$": {                  "viewedby": "abc",                  "vieweddate": ISODate("2014-05-09T04:12:47.907Z")         }     } ) 

If the fields do not in fact change and you just want to insert a new array element if the exact same one does not exist, then you can use $addToSet

db.products.update(     {          "_id": ObjectId("536c55bf9c8fb24c21000095"),         "recentviews.viewedby": "abc"     },     {          $addToSet:{              "recentviews": {                  "viewedby": "abc",                  "vieweddate": ISODate("2014-05-09T04:12:47.907Z")         }     } ) 

However if you are just looking for for "pushing" to an array by a singular key value if that does not exist then you need to do some more manual handling, by first seeing if the element in the array exists and then making the $push statement where it does not.

You get some help from the mongoose methods in doing this by tracking the number of documents affected by the update:

Product.update(     {          "_id": ObjectId("536c55bf9c8fb24c21000095"),         "recentviews.viewedby": "abc"     },     {          "$set": {              "recentviews.$": {                  "viewedby": "abc",                  "vieweddate": ISODate("2014-05-09T04:12:47.907Z")         }     },     function(err,numAffected) {          if (numAffected == 0) {             // Document not updated so you can push onto the array             Product.update(                 {                      "_id": ObjectId("536c55bf9c8fb24c21000095")                 },                 {                      "$push": {                          "recentviews": {                             "viewedby": "abc",                             "vieweddate": ISODate("2014-05-09T04:12:47.907Z")                         }                     }                 },                 function(err,numAffected) {                  }             );         }                  } ); 

The only word of caution here is that there is a bit of an implementation change in the writeConcern messages from MongoDB 2.6 to earlier versions. Being unsure right now as to how the mongoose API actually implements the return of the numAffected argument in the callback the difference could mean something.

In prior versions, even if the data you sent in the initial update exactly matched an existing element and there was no real change required then the "modified" amount would be returned as 1 even though nothing was actually updated.

From MongoDB 2.6 the write concern response contains two parts. One part shows the modified document and the other shows the match. So while the match would be returned by the query portion matching an existing element, the actual modified document count would return as 0 if in fact there was no change required.

So depending on how the return number is actually implemented in mongoose, it might actually be safer to use the $addToSet operator on that inner update to make sure that if the reason for the zero affected documents was not just that the exact element already existed.

like image 119
Neil Lunn Avatar answered Oct 16 '22 20:10

Neil Lunn