Updating embedded documents in mongodb

To update an embeded document in an array when using mongo you will use the positional selector.

The positional selector, when used in conjunction with update has two rules

  • the positional $ operator acts as a placeholder for the first element that matches the query document, and
  • the array field must appear as part of the query document.

This means we need to include our array search in the update's selection statement... and we can only update one doc at a time.

I managed to fat-finger a migration and wanted to correct the mistake. My schema looked something like this:

{
  "_id" : ObjectId("53ac2b3e65703d14066cde7c"),
  "message" : "Hello",
  "tags" : [
    {
      "type" : "venue",
      "value" : ObjectId("4b3cb474f964a520548625e3")
    },
    {
      "type" : "room",
      "venue" : ObjectId("539ef247b0b761402b1ff09b")
    }
  ]     
}

Notice the second tag has venue instead of value. Oops. Here was the migration I used.


db.messages.find().forEach(function(message) {
  var roomid = message.tags[1].venue;
  db.messages.update({ _id: message._id, "tags.type": "room" }, {
    $set: { "tags.$.value": roomid },
    $unset: { "tags.$.venue": 1 }
  })
})

This migration uses looks for an array value with type:'room'.

It then sets the value property and removes the venue property for the matching positional element.