How to Update Multiple Array objects in mongodb. This question was asked earlier - How to Update Multiple Array elements in mongodb
But that didn't work for me. I have an array of objects
{
"_id" : ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id" : "714638ba-2e08-2168-2b99-00002f3d43c0",
"events" : [
{
"handled" : {
"name": "Mike",
"visibile": false
},
"profile" : 10,
"data" : "....."
}
{
"handled" : {
"name": "Shaun",
"visibile": false
},
"profile" : 10,
"data" : "....."
}
{
"handled" : {
"name": "Glen",
"visibile": true
},
"profile" : 20,
"data" : "....."
}
...
]
}
And I want to update all the events.handled.visible:false to "events.handled.visible":true.
I tried
collection.aggregate({
$match: {
_id: ObjectId("4d2d8deff4e6c1d71fc29a07")
}
}, {
$unwind: "$events"
}, {
"$match": {
"events.handled.visible": false
}
}, {
"$group": {
"_id": "$_id",
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": null,
"count": {
"$max": "$count"
}
}
}, function(err, res) {
var max = res[0].count;
while (max--) {
collection.update({
"events.handled.visible": 1
}, {
"$set": {
"events.$.handled.visible": true
}
}, {
"multi": true
}, function(err, res) {
if (err) {
console.log("Whoops! " + err)
} else {
console.log("Yay! " + res)
}
})
}
} //End Function
) //End Aggregate
But that didn't update anything. What am I missing?
1 Answer 1
While I don't think that iterating over an expected count is the "best" way to do this, here is basically the corrections to what you are trying to do, with some help by the node async library for flow control:
async.waterfall(
[
function(callback) {
collection.aggregate(
[
{ "$match": { "_id": ObjectId("4d2d8deff4e6c1d71fc29a07") } },
{ "$unwind": "$events" },
{ "$match": { "events.handled.visibile": false } },
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}}
],
callback
);
},
function(results,callback) {
console.log(results);
var result = results[0];
async.whilst(
function() { return result.count-- },
function(callback) {
collection.update(
{ "_id": result._id, "events.handled.visibile": false },
{ "$set": { "events.$.handled.visibile": true } },
callback
)
},
callback
);
}
],
function(err) {
if (err) throw err;
// finished now
}
);
So the main things here are that your .update() statement should instead be looking for the "events.handled.visibile": false matches, and of course you need to make sure the operations execute "in series", otherwise there is no real guarantee that you are in fact grabbing the document in an altered state from the previous .update().
The async.whilst handles the flow control so that it waits for completion of each .update() until executing the next. When it's first logical statement is true ( counter depleted ) and all .update() statements are run then the loop will release to the final callback.
Where possible you should really be using "Bulk" update operations as referenced in the answer that you are following. That sends all updates and once and only has one response, so the overhead of waiting for each operation to complete is eliminated.
{ "events.handled.visible": 1}should be{ "events.handled.visible": false}. Not to sure what you thought the additional$groupstages were supposed to do. As the author of the answer you seem to be following on the linked post, you are not following the rule of "Don't assume" and are just trying to updatentimes. It would be better to update until there was no update count returned. That way you are sure everything isfalse..update(), that way you are not queueing up operations that are like going to actually execute in parallel, rather than the serial order your operation is expecting.$elemMatchis meant to do. And depends where you are using it. That would be "before"$unwind, but "after" it does not apply.