Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB update multiple records of array [duplicate]

Tags:

arrays

mongodb

I recently started using MongoDB and I have a question regarding updating arrays in a document. I got structure like this:

{
"_id" : ObjectId(),
"post" : "",
"comments" : [
        {
                "user" : "test",
                "avatar" : "/static/avatars/asd.jpg",
                "text" : "....."
        }
        {
                "user" : "test",
                "avatar" : "/static/avatars/asd.jpg",
                "text" : "....."
        }
        {
                "user" : "test",
                "avatar" : "/static/avatars/asd.jpg",
                "text" : "....."
        }
        ...
   ]
}

I'm trying to execute the following query:

update({"comments.user":"test"},{$set:{"comments.$.avatar": "new_avatar.jpg"}},false,true)

The problem is that it update all documents, but it update only the first array element in every document. Is there any way to update all array elements or I should try to do it manually? Thanks.

like image 292
Viktor Kirilov Avatar asked Feb 06 '13 02:02

Viktor Kirilov


2 Answers

You cannot modify multiple array elements in a single update operation. Thus, you'll have to repeat the update in order to migrate documents which need multiple array elements to be modified. You can do this by iterating through each document in the collection, repeatedly applying an update with $elemMatch until the document has all of its relevant comments replaced, e.g.:

db.collection.find().forEach( function(doc) {
  do {
    db.collection.update({_id: doc._id,
                          comments:{$elemMatch:{user:"test",
                                                avatar:{$ne:"new_avatar.jpg"}}}},
                         {$set:{"comments.$.avatar":"new_avatar.jpg"}});
  } while (db.getPrevError().n != 0);
})

Note that if efficiency of this operation is a requirement for your application, you should normalize your schema such that the location of the user's avatar is stored in a single document, rather than in every comment.

like image 83
J Rassi Avatar answered Nov 06 '22 13:11

J Rassi


One solution could be creating a function to be used with a forEach and evaling it (so it runs quickly). Assuming your collection is "article", you could run the following:

var runUpdate = function(){
    db.article.find({"comments.user":"test").forEach( function(article) {
        for(var i in article.comments){
            article.comments[i].avatar = 'new_avatar.jpg';
        }
        db.article.save(article);
    });
};

db.eval(runUpdate);
like image 40
David Welch Avatar answered Nov 06 '22 12:11

David Welch