How to Update Multiple Array Elements in mongodb












152















I have a Mongo document which holds an array of elements.



I'd like to reset the .handled attribute of all objects in the array where .profile = XX.



The document is in the following form:



{
"_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
"events": [{
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 20,
"data": "....."
}
...
]
}


so, I tried the following:



.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)


However it updates only the first matched array element in each document. (That's the defined behaviour for $ - the positional operator.)



How can I update all matched array elements?










share|improve this question




















  • 1





    Updating a subset or all array items has been added to mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…

    – Jaap
    Dec 8 '17 at 14:48











  • be sure to check out arrayFilters and consider which query to use to make the update efficient. Check out the answer by Neil Lunn: stackoverflow.com/a/46054172/337401

    – Jaap
    Dec 8 '17 at 20:48


















152















I have a Mongo document which holds an array of elements.



I'd like to reset the .handled attribute of all objects in the array where .profile = XX.



The document is in the following form:



{
"_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
"events": [{
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 20,
"data": "....."
}
...
]
}


so, I tried the following:



.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)


However it updates only the first matched array element in each document. (That's the defined behaviour for $ - the positional operator.)



How can I update all matched array elements?










share|improve this question




















  • 1





    Updating a subset or all array items has been added to mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…

    – Jaap
    Dec 8 '17 at 14:48











  • be sure to check out arrayFilters and consider which query to use to make the update efficient. Check out the answer by Neil Lunn: stackoverflow.com/a/46054172/337401

    – Jaap
    Dec 8 '17 at 20:48
















152












152








152


51






I have a Mongo document which holds an array of elements.



I'd like to reset the .handled attribute of all objects in the array where .profile = XX.



The document is in the following form:



{
"_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
"events": [{
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 20,
"data": "....."
}
...
]
}


so, I tried the following:



.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)


However it updates only the first matched array element in each document. (That's the defined behaviour for $ - the positional operator.)



How can I update all matched array elements?










share|improve this question
















I have a Mongo document which holds an array of elements.



I'd like to reset the .handled attribute of all objects in the array where .profile = XX.



The document is in the following form:



{
"_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
"events": [{
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 20,
"data": "....."
}
...
]
}


so, I tried the following:



.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)


However it updates only the first matched array element in each document. (That's the defined behaviour for $ - the positional operator.)



How can I update all matched array elements?







arrays mongodb mongodb-query






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 23 '18 at 5:38









Narendra Jadhav

5,665111930




5,665111930










asked Jan 12 '11 at 13:13









LiorHLiorH

10.2k135680




10.2k135680








  • 1





    Updating a subset or all array items has been added to mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…

    – Jaap
    Dec 8 '17 at 14:48











  • be sure to check out arrayFilters and consider which query to use to make the update efficient. Check out the answer by Neil Lunn: stackoverflow.com/a/46054172/337401

    – Jaap
    Dec 8 '17 at 20:48
















  • 1





    Updating a subset or all array items has been added to mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…

    – Jaap
    Dec 8 '17 at 14:48











  • be sure to check out arrayFilters and consider which query to use to make the update efficient. Check out the answer by Neil Lunn: stackoverflow.com/a/46054172/337401

    – Jaap
    Dec 8 '17 at 20:48










1




1





Updating a subset or all array items has been added to mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…

– Jaap
Dec 8 '17 at 14:48





Updating a subset or all array items has been added to mongodb 3.6: docs.mongodb.com/manual/reference/operator/update/…

– Jaap
Dec 8 '17 at 14:48













be sure to check out arrayFilters and consider which query to use to make the update efficient. Check out the answer by Neil Lunn: stackoverflow.com/a/46054172/337401

– Jaap
Dec 8 '17 at 20:48







be sure to check out arrayFilters and consider which query to use to make the update efficient. Check out the answer by Neil Lunn: stackoverflow.com/a/46054172/337401

– Jaap
Dec 8 '17 at 20:48














10 Answers
10






active

oldest

votes


















102














At this moment it is not possible to use the positional operator to update all items in an array. See JIRA http://jira.mongodb.org/browse/SERVER-1243



As a work around you can:




  • Update each item individually
    (events.0.handled events.1.handled
    ...) or...

  • Read the document, do the edits
    manually and save it replacing the
    older one (check "Update if
    Current" if you want to ensure
    atomic updates)






share|improve this answer





















  • 14





    if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

    – LiorH
    Jan 12 '11 at 16:08











  • I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

    – adam
    Dec 15 '11 at 19:50






  • 1





    Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

    – Ixx
    Oct 1 '13 at 20:24






  • 12





    All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

    – Sung Won Cho
    Feb 17 '16 at 4:13








  • 3





    Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

    – Jaap
    Dec 8 '17 at 20:52



















60














What worked for me was this:



db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
.forEach(function (doc) {
doc.events.forEach(function (event) {
if (event.profile === 10) {
event.handled=0;
}
});
db.collection.save(doc);
});


I think it's clearer for mongo newbies and anyone familiar with JQuery & friends.






share|improve this answer


























  • I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

    – Squirrl
    Dec 21 '13 at 4:20






  • 3





    @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

    – Daniel Cerecedo
    Feb 11 '14 at 10:34






  • 2





    @dcerecedo works excellent for me.

    – datasci
    Jun 11 '15 at 14:07











  • @Squirrl try db.posts.find(...).toArray().forEach(...)

    – marmor
    Dec 12 '16 at 15:30






  • 1





    Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

    – chiku
    Jul 29 '17 at 11:07



















37














With the release of MongoDB 3.6 ( and available in the development branch from MongoDB 3.5.12 ) you can now update multiple array elements in a single request.



This uses the filtered positional $[<identifier>] update operator syntax introduced in this version:



db.collection.update(
{ "events.profile":10 },
{ "$set": { "events.$[elem].handled": 0 } },
{ "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
)


The "arrayFilters" as passed to the options for .update() or even
.updateOne(), .updateMany(), .findOneAndUpdate() or .bulkWrite() method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.



Noting that the "multi" as given in the context of the question was used in the expectation that this would "update multiple elements" but this was not and still is not the case. It's usage here applies to "multiple documents" as has always been the case or now otherwise specified as the mandatory setting of .updateMany() in modern API versions.




NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.



However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.



So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.




See also positional all $ which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.



Also see Updating a Nested Array with MongoDB for how these new positional operators apply to "nested" array structures, where "arrays are within other arrays".




IMPORTANT - Upgraded installations from previous versions "may" have not enabled MongoDB features, which can also cause statements to fail. You should ensure your upgrade procedure is complete with details such as index upgrades and then run



   db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )


This enabled such features as the new positional update operators and others. You can also check with:



   db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )


To return the current setting







share|improve this answer





















  • 4





    The accepted answer should be updated and refer to this answer.

    – Jaap
    Dec 8 '17 at 20:51











  • What is elem?

    – user1063287
    Sep 10 '18 at 8:01






  • 1





    This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

    – drlff
    Sep 28 '18 at 22:25













  • thank you, Neil, especially for the IMPORTANT section, exactly what I needed

    – janfabian
    Oct 5 '18 at 10:53



















18














This can also be accomplished with a while loop which checks to see if any documents remain that still have subdocuments that have not been updated. This method preserves the atomicity of your updates (which many of the other solutions here do not).



var query = {
events: {
$elemMatch: {
profile: 10,
handled: { $ne: 0 }
}
}
};

while (db.yourCollection.find(query).count() > 0) {
db.yourCollection.update(
query,
{ $set: { "events.$.handled": 0 } },
{ multi: true }
);
}


The number of times the loop is executed will equal the maximum number of times subdocuments with profile equal to 10 and handled not equal to 0 occur in any of the documents in your collection. So if you have 100 documents in your collection and one of them has three subdocuments that match query and all the other documents have fewer matching subdocuments, the loop will execute three times.



This method avoids the danger of clobbering other data that may be updated by another process while this script executes. It also minimizes the amount of data being transferred between client and server.






share|improve this answer





















  • 1





    this works quite well for anyone stuck < 3.6

    – nottmey
    May 25 '18 at 12:39



















13
















This does in fact relate to the long standing issue at http://jira.mongodb.org/browse/SERVER-1243 where there are in fact a number of challenges to a clear syntax that supports "all cases" where mutiple array matches are found. There are in fact methods already in place that "aid" in solutions to this problem, such as Bulk Operations which have been implemented after this original post.



It is still not possible to update more than a single matched array element in a single update statement, so even with a "multi" update all you will ever be able to update is just one mathed element in the array for each document in that single statement.



The best possible solution at present is to find and loop all matched documents and process Bulk updates which will at least allow many operations to be sent in a single request with a singular response. You can optionally use .aggregate() to reduce the array content returned in the search result to just those that match the conditions for the update selection:



db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$setDifference": [
{ "$map": {
"input": "$events",
"as": "event",
"in": {
"$cond": [
{ "$eq": [ "$$event.handled", 1 ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
]).forEach(function(doc) {
doc.events.forEach(function(event) {
bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
"$set": { "events.$.handled": 0 }
});
count++;

if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.collection.initializeOrderedBulkOp();
}
});
});

if ( count % 1000 != 0 )
bulk.execute();


The .aggregate() portion there will work when there is a "unique" identifier for the array or all content for each element forms a "unique" element itself. This is due to the "set" operator in $setDifference used to filter any false values returned from the $map operation used to process the array for matches.



If your array content does not have unique elements you can try an alternate approach with $redact:



db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])


Where it's limitation is that if "handled" was in fact a field meant to be present at other document levels then you are likely going to get unexepected results, but is fine where that field appears only in one document position and is an equality match.



Future releases ( post 3.1 MongoDB ) as of writing will have a $filter operation that is simpler:



db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$filter": {
"input": "$events",
"as": "event",
"cond": { "$eq": [ "$$event.handled", 1 ] }
}
}
}}
])


And all releases that support .aggregate() can use the following approach with $unwind, but the usage of that operator makes it the least efficient approach due to the array expansion in the pipeline:



db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"events": { "$push": "$events" }
}}
])


In all cases where the MongoDB version supports a "cursor" from aggregate output, then this is just a matter of choosing an approach and iterating the results with the same block of code shown to process the Bulk update statements. Bulk Operations and "cursors" from aggregate output are introduced in the same version ( MongoDB 2.6 ) and therefore usually work hand in hand for processing.



In even earlier versions then it is probably best to just use .find() to return the cursor, and filter out the execution of statements to just the number of times the array element is matched for the .update() iterations:



db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
});
});


If you are aboslutely determined to do "multi" updates or deem that to be ultimately more efficient than processing multiple updates for each matched document, then you can always determine the maximum number of possible array matches and just execute a "multi" update that many times, until basically there are no more documents to update.



A valid approach for MongoDB 2.4 and 2.2 versions could also use .aggregate() to find this value:



var result = db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": null,
"count": { "$max": "$count" }
}}
]);

var max = result.result[0].count;

while ( max-- ) {
db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}


Whatever the case, there are certain things you do not want to do within the update:




  1. Do not "one shot" update the array: Where if you think it might be more efficient to update the whole array content in code and then just $set the whole array in each document. This might seem faster to process, but there is no guarantee that the array content has not changed since it was read and the update is performed. Though $set is still an atomic operator, it will only update the array with what it "thinks" is the correct data, and thus is likely to overwrite any changes occurring between read and write.



  2. Do not calculate index values to update: Where similar to the "one shot" approach you just work out that position 0 and position 2 ( and so on ) are the elements to update and code these in with and eventual statement like:



    { "$set": {
    "events.0.handled": 0,
    "events.2.handled": 0
    }}


    Again the problem here is the "presumption" that those index values found when the document was read are the same index values in th array at the time of update. If new items are added to the array in a way that changes the order then those positions are not longer valid and the wrong items are in fact updated.




So until there is a reasonable syntax determined for allowing multiple matched array elements to be processed in single update statement then the basic approach is to either update each matched array element in an indvidual statement ( ideally in Bulk ) or essentially work out the maximum array elements to update or keep updating until no more modified results are returned. At any rate, you should "always" be processing positional $ updates on the matched array element, even if that is only updating one element per statement.



Bulk Operations are in fact the "generalized" solution to processing any operations that work out to be "multiple operations", and since there are more applications for this than merely updating mutiple array elements with the same value, then it has of course been implemented already, and it is presently the best approach to solve this problem.






share|improve this answer

































    8














    I'm amazed this still hasn't been addressed in mongo. Overall mongo doesn't seem to be great when dealing with sub-arrays. You can't count sub-arrays simply for example.



    I used Javier's first solution. Read the array into events then loop through and build the set exp:



    var set = {}, i, l;
    for(i=0,l=events.length;i<l;i++) {
    if(events[i].profile == 10) {
    set['events.' + i + '.handled'] = 0;
    }
    }

    .update(objId, {$set:set});


    This can be abstracted into a function using a callback for the conditional test






    share|improve this answer


























    • Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

      – Zaheer
      Mar 11 '14 at 14:46






    • 9





      This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

      – Merc
      Mar 15 '14 at 1:10



















    2














    I've been looking for a solution to this using the newest driver for C# 3.6 and here's the fix I eventually settled on. The key here is using "$" which according to MongoDB is new as of version 3.6. See https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up.S for more information.



    Here's the code:



    {
    var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
    var update = Builders<Scene>.Update.Unset("area.$.discoveredBy");
    var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
    }


    For more context see my original post here:
    Remove array element from ALL documents using MongoDB C# driver






    share|improve this answer































      1














      I tried the following and its working fine.



      .update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);


      // callback function in case of nodejs






      share|improve this answer































        0














        Actually, The save command is only on instance of Document class.
        That have a lot of methods and attribute. So you can use lean() function to reduce work load.
        Refer here. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j



        Another problem with save function, that will make conflict data in with multi-save at a same time.
        Model.Update will make data consistently.
        So to update multi items in array of document. Use your familiar programming language and try something like this, I use mongoose in that:



        User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
        .then(usr =>{
        if(!usr) return
        usr.events.forEach( e => {
        if(e && e.profile==10 ) e.handled = 0
        })
        User.findOneAndUpdate(
        {'_id': '4d2d8deff4e6c1d71fc29a07'},
        {$set: {events: usr.events}},
        {new: true}
        ).lean().exec().then(updatedUsr => console.log(updatedUsr))
        })





        share|improve this answer































          -1














          I just wanted to add another solution that worked for me and is pretty straightforward. Here it's just an array of tags (strings) so to update a tag called "test" to "changed", just do this:



          myDocuments.find({tags: "test" }, {fields: {_id: 1}}).forEach(function (doc) {
          myDocuments.update(
          {_id: doc._id, tags: "test"},
          {$set:{'tags.$': "changed"}});
          });





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f4669178%2fhow-to-update-multiple-array-elements-in-mongodb%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            10 Answers
            10






            active

            oldest

            votes








            10 Answers
            10






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            102














            At this moment it is not possible to use the positional operator to update all items in an array. See JIRA http://jira.mongodb.org/browse/SERVER-1243



            As a work around you can:




            • Update each item individually
              (events.0.handled events.1.handled
              ...) or...

            • Read the document, do the edits
              manually and save it replacing the
              older one (check "Update if
              Current" if you want to ensure
              atomic updates)






            share|improve this answer





















            • 14





              if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

              – LiorH
              Jan 12 '11 at 16:08











            • I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

              – adam
              Dec 15 '11 at 19:50






            • 1





              Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

              – Ixx
              Oct 1 '13 at 20:24






            • 12





              All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

              – Sung Won Cho
              Feb 17 '16 at 4:13








            • 3





              Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

              – Jaap
              Dec 8 '17 at 20:52
















            102














            At this moment it is not possible to use the positional operator to update all items in an array. See JIRA http://jira.mongodb.org/browse/SERVER-1243



            As a work around you can:




            • Update each item individually
              (events.0.handled events.1.handled
              ...) or...

            • Read the document, do the edits
              manually and save it replacing the
              older one (check "Update if
              Current" if you want to ensure
              atomic updates)






            share|improve this answer





















            • 14





              if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

              – LiorH
              Jan 12 '11 at 16:08











            • I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

              – adam
              Dec 15 '11 at 19:50






            • 1





              Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

              – Ixx
              Oct 1 '13 at 20:24






            • 12





              All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

              – Sung Won Cho
              Feb 17 '16 at 4:13








            • 3





              Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

              – Jaap
              Dec 8 '17 at 20:52














            102












            102








            102







            At this moment it is not possible to use the positional operator to update all items in an array. See JIRA http://jira.mongodb.org/browse/SERVER-1243



            As a work around you can:




            • Update each item individually
              (events.0.handled events.1.handled
              ...) or...

            • Read the document, do the edits
              manually and save it replacing the
              older one (check "Update if
              Current" if you want to ensure
              atomic updates)






            share|improve this answer















            At this moment it is not possible to use the positional operator to update all items in an array. See JIRA http://jira.mongodb.org/browse/SERVER-1243



            As a work around you can:




            • Update each item individually
              (events.0.handled events.1.handled
              ...) or...

            • Read the document, do the edits
              manually and save it replacing the
              older one (check "Update if
              Current" if you want to ensure
              atomic updates)







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Apr 24 '18 at 19:49









            Neil Lunn

            97.9k23174184




            97.9k23174184










            answered Jan 12 '11 at 14:09









            Javier FerreroJavier Ferrero

            6,84683647




            6,84683647








            • 14





              if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

              – LiorH
              Jan 12 '11 at 16:08











            • I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

              – adam
              Dec 15 '11 at 19:50






            • 1





              Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

              – Ixx
              Oct 1 '13 at 20:24






            • 12





              All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

              – Sung Won Cho
              Feb 17 '16 at 4:13








            • 3





              Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

              – Jaap
              Dec 8 '17 at 20:52














            • 14





              if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

              – LiorH
              Jan 12 '11 at 16:08











            • I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

              – adam
              Dec 15 '11 at 19:50






            • 1





              Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

              – Ixx
              Oct 1 '13 at 20:24






            • 12





              All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

              – Sung Won Cho
              Feb 17 '16 at 4:13








            • 3





              Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

              – Jaap
              Dec 8 '17 at 20:52








            14




            14





            if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

            – LiorH
            Jan 12 '11 at 16:08





            if you have similar problem, vote for this issue - jira.mongodb.org/browse/SERVER-1243

            – LiorH
            Jan 12 '11 at 16:08













            I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

            – adam
            Dec 15 '11 at 19:50





            I actually like the read document and save approach. But I used Couch before Mongo so that approach seems more natural since there is no query API for Couch, just a REST api for whole documents

            – adam
            Dec 15 '11 at 19:50




            1




            1





            Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

            – Ixx
            Oct 1 '13 at 20:24





            Both of these approaches require quite high amount of memory, right? If there's a lot of documents which have to be searched, and have to load all of them (or the nested arrays) in order to update... + also a bit troublesome to implement if this has to be done asynchronously...

            – Ixx
            Oct 1 '13 at 20:24




            12




            12





            All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

            – Sung Won Cho
            Feb 17 '16 at 4:13







            All technical difficulties aside, it is rather astonishing that this feature is not available in MongoDB. This constraint takes away much freedom from customizing database schema.

            – Sung Won Cho
            Feb 17 '16 at 4:13






            3




            3





            Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

            – Jaap
            Dec 8 '17 at 20:52





            Neil Lunn stackoverflow.com/a/46054172/337401 answered this for version 3.6. Since this is a popular question it might be worth while to update this accepted answer with a referral to the answer by Neil Lunn.

            – Jaap
            Dec 8 '17 at 20:52













            60














            What worked for me was this:



            db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
            .forEach(function (doc) {
            doc.events.forEach(function (event) {
            if (event.profile === 10) {
            event.handled=0;
            }
            });
            db.collection.save(doc);
            });


            I think it's clearer for mongo newbies and anyone familiar with JQuery & friends.






            share|improve this answer


























            • I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

              – Squirrl
              Dec 21 '13 at 4:20






            • 3





              @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

              – Daniel Cerecedo
              Feb 11 '14 at 10:34






            • 2





              @dcerecedo works excellent for me.

              – datasci
              Jun 11 '15 at 14:07











            • @Squirrl try db.posts.find(...).toArray().forEach(...)

              – marmor
              Dec 12 '16 at 15:30






            • 1





              Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

              – chiku
              Jul 29 '17 at 11:07
















            60














            What worked for me was this:



            db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
            .forEach(function (doc) {
            doc.events.forEach(function (event) {
            if (event.profile === 10) {
            event.handled=0;
            }
            });
            db.collection.save(doc);
            });


            I think it's clearer for mongo newbies and anyone familiar with JQuery & friends.






            share|improve this answer


























            • I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

              – Squirrl
              Dec 21 '13 at 4:20






            • 3





              @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

              – Daniel Cerecedo
              Feb 11 '14 at 10:34






            • 2





              @dcerecedo works excellent for me.

              – datasci
              Jun 11 '15 at 14:07











            • @Squirrl try db.posts.find(...).toArray().forEach(...)

              – marmor
              Dec 12 '16 at 15:30






            • 1





              Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

              – chiku
              Jul 29 '17 at 11:07














            60












            60








            60







            What worked for me was this:



            db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
            .forEach(function (doc) {
            doc.events.forEach(function (event) {
            if (event.profile === 10) {
            event.handled=0;
            }
            });
            db.collection.save(doc);
            });


            I think it's clearer for mongo newbies and anyone familiar with JQuery & friends.






            share|improve this answer















            What worked for me was this:



            db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
            .forEach(function (doc) {
            doc.events.forEach(function (event) {
            if (event.profile === 10) {
            event.handled=0;
            }
            });
            db.collection.save(doc);
            });


            I think it's clearer for mongo newbies and anyone familiar with JQuery & friends.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 25 '15 at 9:36









            Balthazar

            30.3k106287




            30.3k106287










            answered Dec 15 '13 at 23:32









            Daniel CerecedoDaniel Cerecedo

            3,63832842




            3,63832842













            • I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

              – Squirrl
              Dec 21 '13 at 4:20






            • 3





              @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

              – Daniel Cerecedo
              Feb 11 '14 at 10:34






            • 2





              @dcerecedo works excellent for me.

              – datasci
              Jun 11 '15 at 14:07











            • @Squirrl try db.posts.find(...).toArray().forEach(...)

              – marmor
              Dec 12 '16 at 15:30






            • 1





              Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

              – chiku
              Jul 29 '17 at 11:07



















            • I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

              – Squirrl
              Dec 21 '13 at 4:20






            • 3





              @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

              – Daniel Cerecedo
              Feb 11 '14 at 10:34






            • 2





              @dcerecedo works excellent for me.

              – datasci
              Jun 11 '15 at 14:07











            • @Squirrl try db.posts.find(...).toArray().forEach(...)

              – marmor
              Dec 12 '16 at 15:30






            • 1





              Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

              – chiku
              Jul 29 '17 at 11:07

















            I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

            – Squirrl
            Dec 21 '13 at 4:20





            I'm using db.posts.find({ 'permalink':permalink }).forEach( function(doc) {... and I'm getting Oops.. TypeError: Object # has no method 'forEach'

            – Squirrl
            Dec 21 '13 at 4:20




            3




            3





            @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

            – Daniel Cerecedo
            Feb 11 '14 at 10:34





            @Squirrl could be outdated mongodb version? The doc is clear on how to apply forEach function on a cursor but does not state since which version is supported. docs.mongodb.org/manual/reference/method/cursor.forEach

            – Daniel Cerecedo
            Feb 11 '14 at 10:34




            2




            2





            @dcerecedo works excellent for me.

            – datasci
            Jun 11 '15 at 14:07





            @dcerecedo works excellent for me.

            – datasci
            Jun 11 '15 at 14:07













            @Squirrl try db.posts.find(...).toArray().forEach(...)

            – marmor
            Dec 12 '16 at 15:30





            @Squirrl try db.posts.find(...).toArray().forEach(...)

            – marmor
            Dec 12 '16 at 15:30




            1




            1





            Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

            – chiku
            Jul 29 '17 at 11:07





            Can you please write this query in mongodb driver for java or with spring-data-mongodb? Thanks, kris

            – chiku
            Jul 29 '17 at 11:07











            37














            With the release of MongoDB 3.6 ( and available in the development branch from MongoDB 3.5.12 ) you can now update multiple array elements in a single request.



            This uses the filtered positional $[<identifier>] update operator syntax introduced in this version:



            db.collection.update(
            { "events.profile":10 },
            { "$set": { "events.$[elem].handled": 0 } },
            { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
            )


            The "arrayFilters" as passed to the options for .update() or even
            .updateOne(), .updateMany(), .findOneAndUpdate() or .bulkWrite() method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.



            Noting that the "multi" as given in the context of the question was used in the expectation that this would "update multiple elements" but this was not and still is not the case. It's usage here applies to "multiple documents" as has always been the case or now otherwise specified as the mandatory setting of .updateMany() in modern API versions.




            NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.



            However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.



            So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.




            See also positional all $ which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.



            Also see Updating a Nested Array with MongoDB for how these new positional operators apply to "nested" array structures, where "arrays are within other arrays".




            IMPORTANT - Upgraded installations from previous versions "may" have not enabled MongoDB features, which can also cause statements to fail. You should ensure your upgrade procedure is complete with details such as index upgrades and then run



               db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )


            This enabled such features as the new positional update operators and others. You can also check with:



               db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )


            To return the current setting







            share|improve this answer





















            • 4





              The accepted answer should be updated and refer to this answer.

              – Jaap
              Dec 8 '17 at 20:51











            • What is elem?

              – user1063287
              Sep 10 '18 at 8:01






            • 1





              This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

              – drlff
              Sep 28 '18 at 22:25













            • thank you, Neil, especially for the IMPORTANT section, exactly what I needed

              – janfabian
              Oct 5 '18 at 10:53
















            37














            With the release of MongoDB 3.6 ( and available in the development branch from MongoDB 3.5.12 ) you can now update multiple array elements in a single request.



            This uses the filtered positional $[<identifier>] update operator syntax introduced in this version:



            db.collection.update(
            { "events.profile":10 },
            { "$set": { "events.$[elem].handled": 0 } },
            { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
            )


            The "arrayFilters" as passed to the options for .update() or even
            .updateOne(), .updateMany(), .findOneAndUpdate() or .bulkWrite() method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.



            Noting that the "multi" as given in the context of the question was used in the expectation that this would "update multiple elements" but this was not and still is not the case. It's usage here applies to "multiple documents" as has always been the case or now otherwise specified as the mandatory setting of .updateMany() in modern API versions.




            NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.



            However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.



            So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.




            See also positional all $ which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.



            Also see Updating a Nested Array with MongoDB for how these new positional operators apply to "nested" array structures, where "arrays are within other arrays".




            IMPORTANT - Upgraded installations from previous versions "may" have not enabled MongoDB features, which can also cause statements to fail. You should ensure your upgrade procedure is complete with details such as index upgrades and then run



               db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )


            This enabled such features as the new positional update operators and others. You can also check with:



               db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )


            To return the current setting







            share|improve this answer





















            • 4





              The accepted answer should be updated and refer to this answer.

              – Jaap
              Dec 8 '17 at 20:51











            • What is elem?

              – user1063287
              Sep 10 '18 at 8:01






            • 1





              This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

              – drlff
              Sep 28 '18 at 22:25













            • thank you, Neil, especially for the IMPORTANT section, exactly what I needed

              – janfabian
              Oct 5 '18 at 10:53














            37












            37








            37







            With the release of MongoDB 3.6 ( and available in the development branch from MongoDB 3.5.12 ) you can now update multiple array elements in a single request.



            This uses the filtered positional $[<identifier>] update operator syntax introduced in this version:



            db.collection.update(
            { "events.profile":10 },
            { "$set": { "events.$[elem].handled": 0 } },
            { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
            )


            The "arrayFilters" as passed to the options for .update() or even
            .updateOne(), .updateMany(), .findOneAndUpdate() or .bulkWrite() method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.



            Noting that the "multi" as given in the context of the question was used in the expectation that this would "update multiple elements" but this was not and still is not the case. It's usage here applies to "multiple documents" as has always been the case or now otherwise specified as the mandatory setting of .updateMany() in modern API versions.




            NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.



            However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.



            So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.




            See also positional all $ which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.



            Also see Updating a Nested Array with MongoDB for how these new positional operators apply to "nested" array structures, where "arrays are within other arrays".




            IMPORTANT - Upgraded installations from previous versions "may" have not enabled MongoDB features, which can also cause statements to fail. You should ensure your upgrade procedure is complete with details such as index upgrades and then run



               db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )


            This enabled such features as the new positional update operators and others. You can also check with:



               db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )


            To return the current setting







            share|improve this answer















            With the release of MongoDB 3.6 ( and available in the development branch from MongoDB 3.5.12 ) you can now update multiple array elements in a single request.



            This uses the filtered positional $[<identifier>] update operator syntax introduced in this version:



            db.collection.update(
            { "events.profile":10 },
            { "$set": { "events.$[elem].handled": 0 } },
            { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
            )


            The "arrayFilters" as passed to the options for .update() or even
            .updateOne(), .updateMany(), .findOneAndUpdate() or .bulkWrite() method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.



            Noting that the "multi" as given in the context of the question was used in the expectation that this would "update multiple elements" but this was not and still is not the case. It's usage here applies to "multiple documents" as has always been the case or now otherwise specified as the mandatory setting of .updateMany() in modern API versions.




            NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.



            However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.



            So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.




            See also positional all $ which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.



            Also see Updating a Nested Array with MongoDB for how these new positional operators apply to "nested" array structures, where "arrays are within other arrays".




            IMPORTANT - Upgraded installations from previous versions "may" have not enabled MongoDB features, which can also cause statements to fail. You should ensure your upgrade procedure is complete with details such as index upgrades and then run



               db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )


            This enabled such features as the new positional update operators and others. You can also check with:



               db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )


            To return the current setting








            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Jun 9 '18 at 22:34

























            answered Sep 5 '17 at 11:43









            Neil LunnNeil Lunn

            97.9k23174184




            97.9k23174184








            • 4





              The accepted answer should be updated and refer to this answer.

              – Jaap
              Dec 8 '17 at 20:51











            • What is elem?

              – user1063287
              Sep 10 '18 at 8:01






            • 1





              This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

              – drlff
              Sep 28 '18 at 22:25













            • thank you, Neil, especially for the IMPORTANT section, exactly what I needed

              – janfabian
              Oct 5 '18 at 10:53














            • 4





              The accepted answer should be updated and refer to this answer.

              – Jaap
              Dec 8 '17 at 20:51











            • What is elem?

              – user1063287
              Sep 10 '18 at 8:01






            • 1





              This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

              – drlff
              Sep 28 '18 at 22:25













            • thank you, Neil, especially for the IMPORTANT section, exactly what I needed

              – janfabian
              Oct 5 '18 at 10:53








            4




            4





            The accepted answer should be updated and refer to this answer.

            – Jaap
            Dec 8 '17 at 20:51





            The accepted answer should be updated and refer to this answer.

            – Jaap
            Dec 8 '17 at 20:51













            What is elem?

            – user1063287
            Sep 10 '18 at 8:01





            What is elem?

            – user1063287
            Sep 10 '18 at 8:01




            1




            1





            This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

            – drlff
            Sep 28 '18 at 22:25







            This is correct. Note that RoboMongo doesn't support arrayFilters yet, so running update via CLI. stackoverflow.com/questions/48322834/…

            – drlff
            Sep 28 '18 at 22:25















            thank you, Neil, especially for the IMPORTANT section, exactly what I needed

            – janfabian
            Oct 5 '18 at 10:53





            thank you, Neil, especially for the IMPORTANT section, exactly what I needed

            – janfabian
            Oct 5 '18 at 10:53











            18














            This can also be accomplished with a while loop which checks to see if any documents remain that still have subdocuments that have not been updated. This method preserves the atomicity of your updates (which many of the other solutions here do not).



            var query = {
            events: {
            $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
            }
            }
            };

            while (db.yourCollection.find(query).count() > 0) {
            db.yourCollection.update(
            query,
            { $set: { "events.$.handled": 0 } },
            { multi: true }
            );
            }


            The number of times the loop is executed will equal the maximum number of times subdocuments with profile equal to 10 and handled not equal to 0 occur in any of the documents in your collection. So if you have 100 documents in your collection and one of them has three subdocuments that match query and all the other documents have fewer matching subdocuments, the loop will execute three times.



            This method avoids the danger of clobbering other data that may be updated by another process while this script executes. It also minimizes the amount of data being transferred between client and server.






            share|improve this answer





















            • 1





              this works quite well for anyone stuck < 3.6

              – nottmey
              May 25 '18 at 12:39
















            18














            This can also be accomplished with a while loop which checks to see if any documents remain that still have subdocuments that have not been updated. This method preserves the atomicity of your updates (which many of the other solutions here do not).



            var query = {
            events: {
            $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
            }
            }
            };

            while (db.yourCollection.find(query).count() > 0) {
            db.yourCollection.update(
            query,
            { $set: { "events.$.handled": 0 } },
            { multi: true }
            );
            }


            The number of times the loop is executed will equal the maximum number of times subdocuments with profile equal to 10 and handled not equal to 0 occur in any of the documents in your collection. So if you have 100 documents in your collection and one of them has three subdocuments that match query and all the other documents have fewer matching subdocuments, the loop will execute three times.



            This method avoids the danger of clobbering other data that may be updated by another process while this script executes. It also minimizes the amount of data being transferred between client and server.






            share|improve this answer





















            • 1





              this works quite well for anyone stuck < 3.6

              – nottmey
              May 25 '18 at 12:39














            18












            18








            18







            This can also be accomplished with a while loop which checks to see if any documents remain that still have subdocuments that have not been updated. This method preserves the atomicity of your updates (which many of the other solutions here do not).



            var query = {
            events: {
            $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
            }
            }
            };

            while (db.yourCollection.find(query).count() > 0) {
            db.yourCollection.update(
            query,
            { $set: { "events.$.handled": 0 } },
            { multi: true }
            );
            }


            The number of times the loop is executed will equal the maximum number of times subdocuments with profile equal to 10 and handled not equal to 0 occur in any of the documents in your collection. So if you have 100 documents in your collection and one of them has three subdocuments that match query and all the other documents have fewer matching subdocuments, the loop will execute three times.



            This method avoids the danger of clobbering other data that may be updated by another process while this script executes. It also minimizes the amount of data being transferred between client and server.






            share|improve this answer















            This can also be accomplished with a while loop which checks to see if any documents remain that still have subdocuments that have not been updated. This method preserves the atomicity of your updates (which many of the other solutions here do not).



            var query = {
            events: {
            $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
            }
            }
            };

            while (db.yourCollection.find(query).count() > 0) {
            db.yourCollection.update(
            query,
            { $set: { "events.$.handled": 0 } },
            { multi: true }
            );
            }


            The number of times the loop is executed will equal the maximum number of times subdocuments with profile equal to 10 and handled not equal to 0 occur in any of the documents in your collection. So if you have 100 documents in your collection and one of them has three subdocuments that match query and all the other documents have fewer matching subdocuments, the loop will execute three times.



            This method avoids the danger of clobbering other data that may be updated by another process while this script executes. It also minimizes the amount of data being transferred between client and server.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Sep 7 '16 at 4:29

























            answered Jan 19 '16 at 21:38









            SeanSean

            2,3511728




            2,3511728








            • 1





              this works quite well for anyone stuck < 3.6

              – nottmey
              May 25 '18 at 12:39














            • 1





              this works quite well for anyone stuck < 3.6

              – nottmey
              May 25 '18 at 12:39








            1




            1





            this works quite well for anyone stuck < 3.6

            – nottmey
            May 25 '18 at 12:39





            this works quite well for anyone stuck < 3.6

            – nottmey
            May 25 '18 at 12:39











            13
















            This does in fact relate to the long standing issue at http://jira.mongodb.org/browse/SERVER-1243 where there are in fact a number of challenges to a clear syntax that supports "all cases" where mutiple array matches are found. There are in fact methods already in place that "aid" in solutions to this problem, such as Bulk Operations which have been implemented after this original post.



            It is still not possible to update more than a single matched array element in a single update statement, so even with a "multi" update all you will ever be able to update is just one mathed element in the array for each document in that single statement.



            The best possible solution at present is to find and loop all matched documents and process Bulk updates which will at least allow many operations to be sent in a single request with a singular response. You can optionally use .aggregate() to reduce the array content returned in the search result to just those that match the conditions for the update selection:



            db.collection.aggregate([
            { "$match": { "events.handled": 1 } },
            { "$project": {
            "events": {
            "$setDifference": [
            { "$map": {
            "input": "$events",
            "as": "event",
            "in": {
            "$cond": [
            { "$eq": [ "$$event.handled", 1 ] },
            "$$el",
            false
            ]
            }
            }},
            [false]
            ]
            }
            }}
            ]).forEach(function(doc) {
            doc.events.forEach(function(event) {
            bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
            "$set": { "events.$.handled": 0 }
            });
            count++;

            if ( count % 1000 == 0 ) {
            bulk.execute();
            bulk = db.collection.initializeOrderedBulkOp();
            }
            });
            });

            if ( count % 1000 != 0 )
            bulk.execute();


            The .aggregate() portion there will work when there is a "unique" identifier for the array or all content for each element forms a "unique" element itself. This is due to the "set" operator in $setDifference used to filter any false values returned from the $map operation used to process the array for matches.



            If your array content does not have unique elements you can try an alternate approach with $redact:



            db.collection.aggregate([
            { "$match": { "events.handled": 1 } },
            { "$redact": {
            "$cond": {
            "if": {
            "$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
            },
            "then": "$$DESCEND",
            "else": "$$PRUNE"
            }
            }}
            ])


            Where it's limitation is that if "handled" was in fact a field meant to be present at other document levels then you are likely going to get unexepected results, but is fine where that field appears only in one document position and is an equality match.



            Future releases ( post 3.1 MongoDB ) as of writing will have a $filter operation that is simpler:



            db.collection.aggregate([
            { "$match": { "events.handled": 1 } },
            { "$project": {
            "events": {
            "$filter": {
            "input": "$events",
            "as": "event",
            "cond": { "$eq": [ "$$event.handled", 1 ] }
            }
            }
            }}
            ])


            And all releases that support .aggregate() can use the following approach with $unwind, but the usage of that operator makes it the least efficient approach due to the array expansion in the pipeline:



            db.collection.aggregate([
            { "$match": { "events.handled": 1 } },
            { "$unwind": "$events" },
            { "$match": { "events.handled": 1 } },
            { "$group": {
            "_id": "$_id",
            "events": { "$push": "$events" }
            }}
            ])


            In all cases where the MongoDB version supports a "cursor" from aggregate output, then this is just a matter of choosing an approach and iterating the results with the same block of code shown to process the Bulk update statements. Bulk Operations and "cursors" from aggregate output are introduced in the same version ( MongoDB 2.6 ) and therefore usually work hand in hand for processing.



            In even earlier versions then it is probably best to just use .find() to return the cursor, and filter out the execution of statements to just the number of times the array element is matched for the .update() iterations:



            db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
            doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
            db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
            });
            });


            If you are aboslutely determined to do "multi" updates or deem that to be ultimately more efficient than processing multiple updates for each matched document, then you can always determine the maximum number of possible array matches and just execute a "multi" update that many times, until basically there are no more documents to update.



            A valid approach for MongoDB 2.4 and 2.2 versions could also use .aggregate() to find this value:



            var result = db.collection.aggregate([
            { "$match": { "events.handled": 1 } },
            { "$unwind": "$events" },
            { "$match": { "events.handled": 1 } },
            { "$group": {
            "_id": "$_id",
            "count": { "$sum": 1 }
            }},
            { "$group": {
            "_id": null,
            "count": { "$max": "$count" }
            }}
            ]);

            var max = result.result[0].count;

            while ( max-- ) {
            db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
            }


            Whatever the case, there are certain things you do not want to do within the update:




            1. Do not "one shot" update the array: Where if you think it might be more efficient to update the whole array content in code and then just $set the whole array in each document. This might seem faster to process, but there is no guarantee that the array content has not changed since it was read and the update is performed. Though $set is still an atomic operator, it will only update the array with what it "thinks" is the correct data, and thus is likely to overwrite any changes occurring between read and write.



            2. Do not calculate index values to update: Where similar to the "one shot" approach you just work out that position 0 and position 2 ( and so on ) are the elements to update and code these in with and eventual statement like:



              { "$set": {
              "events.0.handled": 0,
              "events.2.handled": 0
              }}


              Again the problem here is the "presumption" that those index values found when the document was read are the same index values in th array at the time of update. If new items are added to the array in a way that changes the order then those positions are not longer valid and the wrong items are in fact updated.




            So until there is a reasonable syntax determined for allowing multiple matched array elements to be processed in single update statement then the basic approach is to either update each matched array element in an indvidual statement ( ideally in Bulk ) or essentially work out the maximum array elements to update or keep updating until no more modified results are returned. At any rate, you should "always" be processing positional $ updates on the matched array element, even if that is only updating one element per statement.



            Bulk Operations are in fact the "generalized" solution to processing any operations that work out to be "multiple operations", and since there are more applications for this than merely updating mutiple array elements with the same value, then it has of course been implemented already, and it is presently the best approach to solve this problem.






            share|improve this answer






























              13
















              This does in fact relate to the long standing issue at http://jira.mongodb.org/browse/SERVER-1243 where there are in fact a number of challenges to a clear syntax that supports "all cases" where mutiple array matches are found. There are in fact methods already in place that "aid" in solutions to this problem, such as Bulk Operations which have been implemented after this original post.



              It is still not possible to update more than a single matched array element in a single update statement, so even with a "multi" update all you will ever be able to update is just one mathed element in the array for each document in that single statement.



              The best possible solution at present is to find and loop all matched documents and process Bulk updates which will at least allow many operations to be sent in a single request with a singular response. You can optionally use .aggregate() to reduce the array content returned in the search result to just those that match the conditions for the update selection:



              db.collection.aggregate([
              { "$match": { "events.handled": 1 } },
              { "$project": {
              "events": {
              "$setDifference": [
              { "$map": {
              "input": "$events",
              "as": "event",
              "in": {
              "$cond": [
              { "$eq": [ "$$event.handled", 1 ] },
              "$$el",
              false
              ]
              }
              }},
              [false]
              ]
              }
              }}
              ]).forEach(function(doc) {
              doc.events.forEach(function(event) {
              bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
              "$set": { "events.$.handled": 0 }
              });
              count++;

              if ( count % 1000 == 0 ) {
              bulk.execute();
              bulk = db.collection.initializeOrderedBulkOp();
              }
              });
              });

              if ( count % 1000 != 0 )
              bulk.execute();


              The .aggregate() portion there will work when there is a "unique" identifier for the array or all content for each element forms a "unique" element itself. This is due to the "set" operator in $setDifference used to filter any false values returned from the $map operation used to process the array for matches.



              If your array content does not have unique elements you can try an alternate approach with $redact:



              db.collection.aggregate([
              { "$match": { "events.handled": 1 } },
              { "$redact": {
              "$cond": {
              "if": {
              "$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
              },
              "then": "$$DESCEND",
              "else": "$$PRUNE"
              }
              }}
              ])


              Where it's limitation is that if "handled" was in fact a field meant to be present at other document levels then you are likely going to get unexepected results, but is fine where that field appears only in one document position and is an equality match.



              Future releases ( post 3.1 MongoDB ) as of writing will have a $filter operation that is simpler:



              db.collection.aggregate([
              { "$match": { "events.handled": 1 } },
              { "$project": {
              "events": {
              "$filter": {
              "input": "$events",
              "as": "event",
              "cond": { "$eq": [ "$$event.handled", 1 ] }
              }
              }
              }}
              ])


              And all releases that support .aggregate() can use the following approach with $unwind, but the usage of that operator makes it the least efficient approach due to the array expansion in the pipeline:



              db.collection.aggregate([
              { "$match": { "events.handled": 1 } },
              { "$unwind": "$events" },
              { "$match": { "events.handled": 1 } },
              { "$group": {
              "_id": "$_id",
              "events": { "$push": "$events" }
              }}
              ])


              In all cases where the MongoDB version supports a "cursor" from aggregate output, then this is just a matter of choosing an approach and iterating the results with the same block of code shown to process the Bulk update statements. Bulk Operations and "cursors" from aggregate output are introduced in the same version ( MongoDB 2.6 ) and therefore usually work hand in hand for processing.



              In even earlier versions then it is probably best to just use .find() to return the cursor, and filter out the execution of statements to just the number of times the array element is matched for the .update() iterations:



              db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
              doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
              db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
              });
              });


              If you are aboslutely determined to do "multi" updates or deem that to be ultimately more efficient than processing multiple updates for each matched document, then you can always determine the maximum number of possible array matches and just execute a "multi" update that many times, until basically there are no more documents to update.



              A valid approach for MongoDB 2.4 and 2.2 versions could also use .aggregate() to find this value:



              var result = db.collection.aggregate([
              { "$match": { "events.handled": 1 } },
              { "$unwind": "$events" },
              { "$match": { "events.handled": 1 } },
              { "$group": {
              "_id": "$_id",
              "count": { "$sum": 1 }
              }},
              { "$group": {
              "_id": null,
              "count": { "$max": "$count" }
              }}
              ]);

              var max = result.result[0].count;

              while ( max-- ) {
              db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
              }


              Whatever the case, there are certain things you do not want to do within the update:




              1. Do not "one shot" update the array: Where if you think it might be more efficient to update the whole array content in code and then just $set the whole array in each document. This might seem faster to process, but there is no guarantee that the array content has not changed since it was read and the update is performed. Though $set is still an atomic operator, it will only update the array with what it "thinks" is the correct data, and thus is likely to overwrite any changes occurring between read and write.



              2. Do not calculate index values to update: Where similar to the "one shot" approach you just work out that position 0 and position 2 ( and so on ) are the elements to update and code these in with and eventual statement like:



                { "$set": {
                "events.0.handled": 0,
                "events.2.handled": 0
                }}


                Again the problem here is the "presumption" that those index values found when the document was read are the same index values in th array at the time of update. If new items are added to the array in a way that changes the order then those positions are not longer valid and the wrong items are in fact updated.




              So until there is a reasonable syntax determined for allowing multiple matched array elements to be processed in single update statement then the basic approach is to either update each matched array element in an indvidual statement ( ideally in Bulk ) or essentially work out the maximum array elements to update or keep updating until no more modified results are returned. At any rate, you should "always" be processing positional $ updates on the matched array element, even if that is only updating one element per statement.



              Bulk Operations are in fact the "generalized" solution to processing any operations that work out to be "multiple operations", and since there are more applications for this than merely updating mutiple array elements with the same value, then it has of course been implemented already, and it is presently the best approach to solve this problem.






              share|improve this answer




























                13












                13








                13









                This does in fact relate to the long standing issue at http://jira.mongodb.org/browse/SERVER-1243 where there are in fact a number of challenges to a clear syntax that supports "all cases" where mutiple array matches are found. There are in fact methods already in place that "aid" in solutions to this problem, such as Bulk Operations which have been implemented after this original post.



                It is still not possible to update more than a single matched array element in a single update statement, so even with a "multi" update all you will ever be able to update is just one mathed element in the array for each document in that single statement.



                The best possible solution at present is to find and loop all matched documents and process Bulk updates which will at least allow many operations to be sent in a single request with a singular response. You can optionally use .aggregate() to reduce the array content returned in the search result to just those that match the conditions for the update selection:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$project": {
                "events": {
                "$setDifference": [
                { "$map": {
                "input": "$events",
                "as": "event",
                "in": {
                "$cond": [
                { "$eq": [ "$$event.handled", 1 ] },
                "$$el",
                false
                ]
                }
                }},
                [false]
                ]
                }
                }}
                ]).forEach(function(doc) {
                doc.events.forEach(function(event) {
                bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
                "$set": { "events.$.handled": 0 }
                });
                count++;

                if ( count % 1000 == 0 ) {
                bulk.execute();
                bulk = db.collection.initializeOrderedBulkOp();
                }
                });
                });

                if ( count % 1000 != 0 )
                bulk.execute();


                The .aggregate() portion there will work when there is a "unique" identifier for the array or all content for each element forms a "unique" element itself. This is due to the "set" operator in $setDifference used to filter any false values returned from the $map operation used to process the array for matches.



                If your array content does not have unique elements you can try an alternate approach with $redact:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$redact": {
                "$cond": {
                "if": {
                "$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
                },
                "then": "$$DESCEND",
                "else": "$$PRUNE"
                }
                }}
                ])


                Where it's limitation is that if "handled" was in fact a field meant to be present at other document levels then you are likely going to get unexepected results, but is fine where that field appears only in one document position and is an equality match.



                Future releases ( post 3.1 MongoDB ) as of writing will have a $filter operation that is simpler:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$project": {
                "events": {
                "$filter": {
                "input": "$events",
                "as": "event",
                "cond": { "$eq": [ "$$event.handled", 1 ] }
                }
                }
                }}
                ])


                And all releases that support .aggregate() can use the following approach with $unwind, but the usage of that operator makes it the least efficient approach due to the array expansion in the pipeline:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$unwind": "$events" },
                { "$match": { "events.handled": 1 } },
                { "$group": {
                "_id": "$_id",
                "events": { "$push": "$events" }
                }}
                ])


                In all cases where the MongoDB version supports a "cursor" from aggregate output, then this is just a matter of choosing an approach and iterating the results with the same block of code shown to process the Bulk update statements. Bulk Operations and "cursors" from aggregate output are introduced in the same version ( MongoDB 2.6 ) and therefore usually work hand in hand for processing.



                In even earlier versions then it is probably best to just use .find() to return the cursor, and filter out the execution of statements to just the number of times the array element is matched for the .update() iterations:



                db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
                doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
                db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
                });
                });


                If you are aboslutely determined to do "multi" updates or deem that to be ultimately more efficient than processing multiple updates for each matched document, then you can always determine the maximum number of possible array matches and just execute a "multi" update that many times, until basically there are no more documents to update.



                A valid approach for MongoDB 2.4 and 2.2 versions could also use .aggregate() to find this value:



                var result = db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$unwind": "$events" },
                { "$match": { "events.handled": 1 } },
                { "$group": {
                "_id": "$_id",
                "count": { "$sum": 1 }
                }},
                { "$group": {
                "_id": null,
                "count": { "$max": "$count" }
                }}
                ]);

                var max = result.result[0].count;

                while ( max-- ) {
                db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
                }


                Whatever the case, there are certain things you do not want to do within the update:




                1. Do not "one shot" update the array: Where if you think it might be more efficient to update the whole array content in code and then just $set the whole array in each document. This might seem faster to process, but there is no guarantee that the array content has not changed since it was read and the update is performed. Though $set is still an atomic operator, it will only update the array with what it "thinks" is the correct data, and thus is likely to overwrite any changes occurring between read and write.



                2. Do not calculate index values to update: Where similar to the "one shot" approach you just work out that position 0 and position 2 ( and so on ) are the elements to update and code these in with and eventual statement like:



                  { "$set": {
                  "events.0.handled": 0,
                  "events.2.handled": 0
                  }}


                  Again the problem here is the "presumption" that those index values found when the document was read are the same index values in th array at the time of update. If new items are added to the array in a way that changes the order then those positions are not longer valid and the wrong items are in fact updated.




                So until there is a reasonable syntax determined for allowing multiple matched array elements to be processed in single update statement then the basic approach is to either update each matched array element in an indvidual statement ( ideally in Bulk ) or essentially work out the maximum array elements to update or keep updating until no more modified results are returned. At any rate, you should "always" be processing positional $ updates on the matched array element, even if that is only updating one element per statement.



                Bulk Operations are in fact the "generalized" solution to processing any operations that work out to be "multiple operations", and since there are more applications for this than merely updating mutiple array elements with the same value, then it has of course been implemented already, and it is presently the best approach to solve this problem.






                share|improve this answer

















                This does in fact relate to the long standing issue at http://jira.mongodb.org/browse/SERVER-1243 where there are in fact a number of challenges to a clear syntax that supports "all cases" where mutiple array matches are found. There are in fact methods already in place that "aid" in solutions to this problem, such as Bulk Operations which have been implemented after this original post.



                It is still not possible to update more than a single matched array element in a single update statement, so even with a "multi" update all you will ever be able to update is just one mathed element in the array for each document in that single statement.



                The best possible solution at present is to find and loop all matched documents and process Bulk updates which will at least allow many operations to be sent in a single request with a singular response. You can optionally use .aggregate() to reduce the array content returned in the search result to just those that match the conditions for the update selection:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$project": {
                "events": {
                "$setDifference": [
                { "$map": {
                "input": "$events",
                "as": "event",
                "in": {
                "$cond": [
                { "$eq": [ "$$event.handled", 1 ] },
                "$$el",
                false
                ]
                }
                }},
                [false]
                ]
                }
                }}
                ]).forEach(function(doc) {
                doc.events.forEach(function(event) {
                bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
                "$set": { "events.$.handled": 0 }
                });
                count++;

                if ( count % 1000 == 0 ) {
                bulk.execute();
                bulk = db.collection.initializeOrderedBulkOp();
                }
                });
                });

                if ( count % 1000 != 0 )
                bulk.execute();


                The .aggregate() portion there will work when there is a "unique" identifier for the array or all content for each element forms a "unique" element itself. This is due to the "set" operator in $setDifference used to filter any false values returned from the $map operation used to process the array for matches.



                If your array content does not have unique elements you can try an alternate approach with $redact:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$redact": {
                "$cond": {
                "if": {
                "$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
                },
                "then": "$$DESCEND",
                "else": "$$PRUNE"
                }
                }}
                ])


                Where it's limitation is that if "handled" was in fact a field meant to be present at other document levels then you are likely going to get unexepected results, but is fine where that field appears only in one document position and is an equality match.



                Future releases ( post 3.1 MongoDB ) as of writing will have a $filter operation that is simpler:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$project": {
                "events": {
                "$filter": {
                "input": "$events",
                "as": "event",
                "cond": { "$eq": [ "$$event.handled", 1 ] }
                }
                }
                }}
                ])


                And all releases that support .aggregate() can use the following approach with $unwind, but the usage of that operator makes it the least efficient approach due to the array expansion in the pipeline:



                db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$unwind": "$events" },
                { "$match": { "events.handled": 1 } },
                { "$group": {
                "_id": "$_id",
                "events": { "$push": "$events" }
                }}
                ])


                In all cases where the MongoDB version supports a "cursor" from aggregate output, then this is just a matter of choosing an approach and iterating the results with the same block of code shown to process the Bulk update statements. Bulk Operations and "cursors" from aggregate output are introduced in the same version ( MongoDB 2.6 ) and therefore usually work hand in hand for processing.



                In even earlier versions then it is probably best to just use .find() to return the cursor, and filter out the execution of statements to just the number of times the array element is matched for the .update() iterations:



                db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
                doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
                db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
                });
                });


                If you are aboslutely determined to do "multi" updates or deem that to be ultimately more efficient than processing multiple updates for each matched document, then you can always determine the maximum number of possible array matches and just execute a "multi" update that many times, until basically there are no more documents to update.



                A valid approach for MongoDB 2.4 and 2.2 versions could also use .aggregate() to find this value:



                var result = db.collection.aggregate([
                { "$match": { "events.handled": 1 } },
                { "$unwind": "$events" },
                { "$match": { "events.handled": 1 } },
                { "$group": {
                "_id": "$_id",
                "count": { "$sum": 1 }
                }},
                { "$group": {
                "_id": null,
                "count": { "$max": "$count" }
                }}
                ]);

                var max = result.result[0].count;

                while ( max-- ) {
                db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
                }


                Whatever the case, there are certain things you do not want to do within the update:




                1. Do not "one shot" update the array: Where if you think it might be more efficient to update the whole array content in code and then just $set the whole array in each document. This might seem faster to process, but there is no guarantee that the array content has not changed since it was read and the update is performed. Though $set is still an atomic operator, it will only update the array with what it "thinks" is the correct data, and thus is likely to overwrite any changes occurring between read and write.



                2. Do not calculate index values to update: Where similar to the "one shot" approach you just work out that position 0 and position 2 ( and so on ) are the elements to update and code these in with and eventual statement like:



                  { "$set": {
                  "events.0.handled": 0,
                  "events.2.handled": 0
                  }}


                  Again the problem here is the "presumption" that those index values found when the document was read are the same index values in th array at the time of update. If new items are added to the array in a way that changes the order then those positions are not longer valid and the wrong items are in fact updated.




                So until there is a reasonable syntax determined for allowing multiple matched array elements to be processed in single update statement then the basic approach is to either update each matched array element in an indvidual statement ( ideally in Bulk ) or essentially work out the maximum array elements to update or keep updating until no more modified results are returned. At any rate, you should "always" be processing positional $ updates on the matched array element, even if that is only updating one element per statement.



                Bulk Operations are in fact the "generalized" solution to processing any operations that work out to be "multiple operations", and since there are more applications for this than merely updating mutiple array elements with the same value, then it has of course been implemented already, and it is presently the best approach to solve this problem.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Jul 24 '17 at 21:46









                The Bearded Llama

                1,32341427




                1,32341427










                answered Oct 18 '15 at 0:58









                Blakes SevenBlakes Seven

                34.2k86577




                34.2k86577























                    8














                    I'm amazed this still hasn't been addressed in mongo. Overall mongo doesn't seem to be great when dealing with sub-arrays. You can't count sub-arrays simply for example.



                    I used Javier's first solution. Read the array into events then loop through and build the set exp:



                    var set = {}, i, l;
                    for(i=0,l=events.length;i<l;i++) {
                    if(events[i].profile == 10) {
                    set['events.' + i + '.handled'] = 0;
                    }
                    }

                    .update(objId, {$set:set});


                    This can be abstracted into a function using a callback for the conditional test






                    share|improve this answer


























                    • Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

                      – Zaheer
                      Mar 11 '14 at 14:46






                    • 9





                      This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

                      – Merc
                      Mar 15 '14 at 1:10
















                    8














                    I'm amazed this still hasn't been addressed in mongo. Overall mongo doesn't seem to be great when dealing with sub-arrays. You can't count sub-arrays simply for example.



                    I used Javier's first solution. Read the array into events then loop through and build the set exp:



                    var set = {}, i, l;
                    for(i=0,l=events.length;i<l;i++) {
                    if(events[i].profile == 10) {
                    set['events.' + i + '.handled'] = 0;
                    }
                    }

                    .update(objId, {$set:set});


                    This can be abstracted into a function using a callback for the conditional test






                    share|improve this answer


























                    • Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

                      – Zaheer
                      Mar 11 '14 at 14:46






                    • 9





                      This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

                      – Merc
                      Mar 15 '14 at 1:10














                    8












                    8








                    8







                    I'm amazed this still hasn't been addressed in mongo. Overall mongo doesn't seem to be great when dealing with sub-arrays. You can't count sub-arrays simply for example.



                    I used Javier's first solution. Read the array into events then loop through and build the set exp:



                    var set = {}, i, l;
                    for(i=0,l=events.length;i<l;i++) {
                    if(events[i].profile == 10) {
                    set['events.' + i + '.handled'] = 0;
                    }
                    }

                    .update(objId, {$set:set});


                    This can be abstracted into a function using a callback for the conditional test






                    share|improve this answer















                    I'm amazed this still hasn't been addressed in mongo. Overall mongo doesn't seem to be great when dealing with sub-arrays. You can't count sub-arrays simply for example.



                    I used Javier's first solution. Read the array into events then loop through and build the set exp:



                    var set = {}, i, l;
                    for(i=0,l=events.length;i<l;i++) {
                    if(events[i].profile == 10) {
                    set['events.' + i + '.handled'] = 0;
                    }
                    }

                    .update(objId, {$set:set});


                    This can be abstracted into a function using a callback for the conditional test







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Aug 8 '13 at 7:59









                    UpTheCreek

                    15.6k31132207




                    15.6k31132207










                    answered Jun 2 '13 at 13:54









                    ljelewisljelewis

                    49747




                    49747













                    • Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

                      – Zaheer
                      Mar 11 '14 at 14:46






                    • 9





                      This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

                      – Merc
                      Mar 15 '14 at 1:10



















                    • Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

                      – Zaheer
                      Mar 11 '14 at 14:46






                    • 9





                      This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

                      – Merc
                      Mar 15 '14 at 1:10

















                    Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

                    – Zaheer
                    Mar 11 '14 at 14:46





                    Thanks for this! Can't believe this feature still isn't natively supported! Used this to increment every item of a subarray, for others reading... to update every item just remove the if statement.

                    – Zaheer
                    Mar 11 '14 at 14:46




                    9




                    9





                    This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

                    – Merc
                    Mar 15 '14 at 1:10





                    This is not a safe solution. If a record gets added while you are running the update, you will corrupt your data.

                    – Merc
                    Mar 15 '14 at 1:10











                    2














                    I've been looking for a solution to this using the newest driver for C# 3.6 and here's the fix I eventually settled on. The key here is using "$" which according to MongoDB is new as of version 3.6. See https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up.S for more information.



                    Here's the code:



                    {
                    var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
                    var update = Builders<Scene>.Update.Unset("area.$.discoveredBy");
                    var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
                    }


                    For more context see my original post here:
                    Remove array element from ALL documents using MongoDB C# driver






                    share|improve this answer




























                      2














                      I've been looking for a solution to this using the newest driver for C# 3.6 and here's the fix I eventually settled on. The key here is using "$" which according to MongoDB is new as of version 3.6. See https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up.S for more information.



                      Here's the code:



                      {
                      var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
                      var update = Builders<Scene>.Update.Unset("area.$.discoveredBy");
                      var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
                      }


                      For more context see my original post here:
                      Remove array element from ALL documents using MongoDB C# driver






                      share|improve this answer


























                        2












                        2








                        2







                        I've been looking for a solution to this using the newest driver for C# 3.6 and here's the fix I eventually settled on. The key here is using "$" which according to MongoDB is new as of version 3.6. See https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up.S for more information.



                        Here's the code:



                        {
                        var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
                        var update = Builders<Scene>.Update.Unset("area.$.discoveredBy");
                        var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
                        }


                        For more context see my original post here:
                        Remove array element from ALL documents using MongoDB C# driver






                        share|improve this answer













                        I've been looking for a solution to this using the newest driver for C# 3.6 and here's the fix I eventually settled on. The key here is using "$" which according to MongoDB is new as of version 3.6. See https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up.S for more information.



                        Here's the code:



                        {
                        var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
                        var update = Builders<Scene>.Update.Unset("area.$.discoveredBy");
                        var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
                        }


                        For more context see my original post here:
                        Remove array element from ALL documents using MongoDB C# driver







                        share|improve this answer












                        share|improve this answer



                        share|improve this answer










                        answered Jun 6 '18 at 0:27









                        C0d3 0n3C0d3 0n3

                        366




                        366























                            1














                            I tried the following and its working fine.



                            .update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);


                            // callback function in case of nodejs






                            share|improve this answer




























                              1














                              I tried the following and its working fine.



                              .update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);


                              // callback function in case of nodejs






                              share|improve this answer


























                                1












                                1








                                1







                                I tried the following and its working fine.



                                .update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);


                                // callback function in case of nodejs






                                share|improve this answer













                                I tried the following and its working fine.



                                .update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);


                                // callback function in case of nodejs







                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered Sep 4 '17 at 16:45









                                Pranay SahaPranay Saha

                                8419




                                8419























                                    0














                                    Actually, The save command is only on instance of Document class.
                                    That have a lot of methods and attribute. So you can use lean() function to reduce work load.
                                    Refer here. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j



                                    Another problem with save function, that will make conflict data in with multi-save at a same time.
                                    Model.Update will make data consistently.
                                    So to update multi items in array of document. Use your familiar programming language and try something like this, I use mongoose in that:



                                    User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
                                    .then(usr =>{
                                    if(!usr) return
                                    usr.events.forEach( e => {
                                    if(e && e.profile==10 ) e.handled = 0
                                    })
                                    User.findOneAndUpdate(
                                    {'_id': '4d2d8deff4e6c1d71fc29a07'},
                                    {$set: {events: usr.events}},
                                    {new: true}
                                    ).lean().exec().then(updatedUsr => console.log(updatedUsr))
                                    })





                                    share|improve this answer




























                                      0














                                      Actually, The save command is only on instance of Document class.
                                      That have a lot of methods and attribute. So you can use lean() function to reduce work load.
                                      Refer here. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j



                                      Another problem with save function, that will make conflict data in with multi-save at a same time.
                                      Model.Update will make data consistently.
                                      So to update multi items in array of document. Use your familiar programming language and try something like this, I use mongoose in that:



                                      User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
                                      .then(usr =>{
                                      if(!usr) return
                                      usr.events.forEach( e => {
                                      if(e && e.profile==10 ) e.handled = 0
                                      })
                                      User.findOneAndUpdate(
                                      {'_id': '4d2d8deff4e6c1d71fc29a07'},
                                      {$set: {events: usr.events}},
                                      {new: true}
                                      ).lean().exec().then(updatedUsr => console.log(updatedUsr))
                                      })





                                      share|improve this answer


























                                        0












                                        0








                                        0







                                        Actually, The save command is only on instance of Document class.
                                        That have a lot of methods and attribute. So you can use lean() function to reduce work load.
                                        Refer here. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j



                                        Another problem with save function, that will make conflict data in with multi-save at a same time.
                                        Model.Update will make data consistently.
                                        So to update multi items in array of document. Use your familiar programming language and try something like this, I use mongoose in that:



                                        User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
                                        .then(usr =>{
                                        if(!usr) return
                                        usr.events.forEach( e => {
                                        if(e && e.profile==10 ) e.handled = 0
                                        })
                                        User.findOneAndUpdate(
                                        {'_id': '4d2d8deff4e6c1d71fc29a07'},
                                        {$set: {events: usr.events}},
                                        {new: true}
                                        ).lean().exec().then(updatedUsr => console.log(updatedUsr))
                                        })





                                        share|improve this answer













                                        Actually, The save command is only on instance of Document class.
                                        That have a lot of methods and attribute. So you can use lean() function to reduce work load.
                                        Refer here. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j



                                        Another problem with save function, that will make conflict data in with multi-save at a same time.
                                        Model.Update will make data consistently.
                                        So to update multi items in array of document. Use your familiar programming language and try something like this, I use mongoose in that:



                                        User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
                                        .then(usr =>{
                                        if(!usr) return
                                        usr.events.forEach( e => {
                                        if(e && e.profile==10 ) e.handled = 0
                                        })
                                        User.findOneAndUpdate(
                                        {'_id': '4d2d8deff4e6c1d71fc29a07'},
                                        {$set: {events: usr.events}},
                                        {new: true}
                                        ).lean().exec().then(updatedUsr => console.log(updatedUsr))
                                        })






                                        share|improve this answer












                                        share|improve this answer



                                        share|improve this answer










                                        answered Jul 29 '17 at 18:35









                                        user3176403user3176403

                                        11




                                        11























                                            -1














                                            I just wanted to add another solution that worked for me and is pretty straightforward. Here it's just an array of tags (strings) so to update a tag called "test" to "changed", just do this:



                                            myDocuments.find({tags: "test" }, {fields: {_id: 1}}).forEach(function (doc) {
                                            myDocuments.update(
                                            {_id: doc._id, tags: "test"},
                                            {$set:{'tags.$': "changed"}});
                                            });





                                            share|improve this answer




























                                              -1














                                              I just wanted to add another solution that worked for me and is pretty straightforward. Here it's just an array of tags (strings) so to update a tag called "test" to "changed", just do this:



                                              myDocuments.find({tags: "test" }, {fields: {_id: 1}}).forEach(function (doc) {
                                              myDocuments.update(
                                              {_id: doc._id, tags: "test"},
                                              {$set:{'tags.$': "changed"}});
                                              });





                                              share|improve this answer


























                                                -1












                                                -1








                                                -1







                                                I just wanted to add another solution that worked for me and is pretty straightforward. Here it's just an array of tags (strings) so to update a tag called "test" to "changed", just do this:



                                                myDocuments.find({tags: "test" }, {fields: {_id: 1}}).forEach(function (doc) {
                                                myDocuments.update(
                                                {_id: doc._id, tags: "test"},
                                                {$set:{'tags.$': "changed"}});
                                                });





                                                share|improve this answer













                                                I just wanted to add another solution that worked for me and is pretty straightforward. Here it's just an array of tags (strings) so to update a tag called "test" to "changed", just do this:



                                                myDocuments.find({tags: "test" }, {fields: {_id: 1}}).forEach(function (doc) {
                                                myDocuments.update(
                                                {_id: doc._id, tags: "test"},
                                                {$set:{'tags.$': "changed"}});
                                                });






                                                share|improve this answer












                                                share|improve this answer



                                                share|improve this answer










                                                answered Mar 1 '15 at 22:51









                                                cfscfs

                                                780522




                                                780522






























                                                    draft saved

                                                    draft discarded




















































                                                    Thanks for contributing an answer to Stack Overflow!


                                                    • Please be sure to answer the question. Provide details and share your research!

                                                    But avoid



                                                    • Asking for help, clarification, or responding to other answers.

                                                    • Making statements based on opinion; back them up with references or personal experience.


                                                    To learn more, see our tips on writing great answers.




                                                    draft saved


                                                    draft discarded














                                                    StackExchange.ready(
                                                    function () {
                                                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f4669178%2fhow-to-update-multiple-array-elements-in-mongodb%23new-answer', 'question_page');
                                                    }
                                                    );

                                                    Post as a guest















                                                    Required, but never shown





















































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown

































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown







                                                    Popular posts from this blog

                                                    Create new schema in PostgreSQL using DBeaver

                                                    Deepest pit of an array with Javascript: test on Codility

                                                    Costa Masnaga