MongoDB update data in nested field
You need to use the Dot Notation for the arrays.
That is, you should replace the $
with the zero-based index of the element you're trying to update.
For example:
db.users.update ({_id: '123'}, { '$set': {"friends.0.emails.0.email" : '2222'} });
will update the first email of the first friend, and
db.users.update ({_id: '123'}, { '$set': {"friends.0.emails.1.email" : '2222'} })
will update the second email of the first friend.
One flexible way to do updates to a multilevel array is to use arrayFilters which allows for indexes to be queried for and assigned to an identifier.
db.collection.update(
{ <query selector> },
{ <update operator>: { "array.$[<identifier>].field" : value } },
{ arrayFilters: [ { <identifier>: <condition> } } ] }
)
Here's the example you provided plus a new friend with two emails:
{
_id : '123'
friends: [
{name: 'allen', emails: [
{email: '11111', using: 'true'}
]},
{name: 'lucy' , emails: [
{email: '[email protected]', using:'true'},
{email:'[email protected]', using : 'false'}
]}
]
}
Suppose [email protected] is being updated to [email protected]. We can use the name and email fields in an array filter to identify the index we want to update.
db.users.update({_id:123}, {$set:{
"friends.$[updateFriend].emails.$[updateEmail].email : "[email protected]"
}}, {
"arrayFilters": [
{"updateFriend.name" : "lucy"},
{"updateEmail.email" : "[email protected]"}
]
})
In this way our update is not sensitive to a particular array order. Also while emails are likely unique I would recommend including a unique id on all subdocuments so the arrayFilters can be exact.
Solution using Mongoose:
Users.findById("123", function(err, user) {
var friends = user.friends;
for ( i=0; i < friends.length; i++ ) {
if (friends[i].name == 'allen') {
friends[i].email = '2222';
user.save(function(err) {
if (err) throw err;
console.log("email updated");
});
} else {
console.log("no friends named allen");
}
}
}