Reputation: 454
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?
Upvotes: 2
Views: 641
Reputation: 50406
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.
Upvotes: 3