MongoDB update data in nested field

i'm using Mongo to be my database. i have a data:

 {
   _id : '123'
   friends: [
     {name: 'allen', emails: [{email: '11111', using: 'true'}]}
   ]
 }

now, i wanna to motify user's friends' emails ' email, whose _id is '123' i write like this:

db.users.update ({_id: '123'}, {$set: {"friends.0.emails.$.email" : '2222'} })

it's easy, but , it's wrong , when the emails array has two or more data. so, my question is: how can i motify the data in a nested filed --- just have two or more nested array? Thanks.


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 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 the update is not sensitive to a particular array order. The example above uses two identifiers updateFriend and updateEmail which will match – for the array they are applied too – any elements fulfilling the array filter criteria. As noted in the documentation:

The <identifier> must begin with a lowercase letter and contain only alphanumeric characters.

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");
          }
        }

    }