3

I have an mongodb document like:

{
    "name" : "JohnDoe",
    "adds" : [
        {
            "status" : "PENDING",
            "date" : "2015-09-23"
        },
        {
            "status" : "PENDING",
            "date" : "2015-10-01"
        }
    ]
}

I want to update all the elements array like:

collection.update({'name':'JohnDoe'}, {'$set':{'adds.status':'APPROVED'}}).

How to do that?

EDIT: Most solution says to use positional operator to select the array element then use the operator to update that element. But In my case i have to update each element in array.

Manish Gupta
  • 4,438
  • 18
  • 57
  • 104
  • @sheilak i that post the user can select an individual array element then he can update it using positional operator. But i have to update each and every element in array. – Manish Gupta Oct 17 '15 at 12:34
  • The answers on that question explain that you can't perform a $set on all items in an array in one update. The accepted answer links to an open JIRA ticket about the issue. There are also multiple workarounds posted. – sheilak Oct 17 '15 at 12:41

1 Answers1

3

As mentioned, the main issue here is with updates on multiple elements with the positional operator as recorded in this long standing issue: http://jira.mongodb.org/browse/SERVER-1243

The basic case therefore is no single execution can do this, so in order to process for multiple array elements you need some method of determining how many elements you need to update and process one update statement per each element.

A simplified approach to this is generally using Bulk Operations to process what ends up being "mulitple" update operations as a single request and response to the server:

var bulk = db.collection.initializeOrderedBulkOp(),
    count = 0;

db.collection.find({ "name": "John Doe", "adds.status": "PENDING" }).forEach(function(doc) { 
    doc.adds.filter(function(add){ return add.status = "PENDING" }).forEach(function(add) {
        bulk.find({ "_id": doc._id, "adds.status": "PENDING" }).updateOne({
            "$set": { "adds.$.status": "APPROVED" }
        });
        count++;

        // Execute once in 1000 statements created and re-init
        if ( count % 1000 == 0 ) {
            bulk.execute();
            bulk = db.collection.initializeOrderedBulkOp();
        }
    });
});

// Execute any pending operations
if ( count % 1000 != 0 )
    bulk.execute();

If your updated documents is quite small, or indeed only a single document then you can forgo the count check and simply append all bulk updates within the required loops and just execute once at the end of all loops.

A longer explaination and alternates can be found on How to Update Multiple Array Elements, but all come down to different approaches to matching the element to update and processing a positional $ update mutliple times, for either each document matched or until there are no more modified documents returned.

Community
  • 1
  • 1
Blakes Seven
  • 49,422
  • 14
  • 129
  • 135