Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-17159

$ Positional Operator Only Updating First Sub-Document

    XMLWordPrintable

Details

    • Bug
    • Status: Closed
    • Critical - P2
    • Resolution: Duplicate
    • 2.6.7, 3.0.0-rc6
    • None
    • Write Ops
    • None
    • ALL
    • Hide

      Consider the following document:

      {
        "_id" : 1,
        "name" : "Aphex Twin",
        "albums" : [
          {
            "_id" : 1,
            "name" : "Selected Ambient Works 85–92",
            "tracks" : [
              { "_id" : 1, "name" : "Xtal" },
              { "_id" : 2, "name" : "Tha" },
              { "_id" : 3, "name" : "Pulsewidth" }
            ]
          }
        ]
      }

      I want to update the track with the name "Tha" to "**Tha" for demonstration purposes. Using update with $set, I tell Mongo to find the document with _id of 1, an album sub document _id of 1, an album tracks sub-document with _id of 2 and update it's name. I will show the behaviour from the console:

      3.0.0-rc6 (Incorrectly updates the first track name and not the matching second):

      MongoDB shell version: 3.0.0-rc6
      connecting to: issue-test
      > db.bands.findOne();
      {
      	"_id" : 1,
      	"name" : "Aphex Twin",
      	"albums" : [
      		{
      			"_id" : 1,
      			"name" : "Selected Ambient Works 8592",
      			"tracks" : [
      				{
      					"_id" : 1,
      					"name" : "Xtal"
      				},
      				{
      					"_id" : 2,
      					"name" : "Tha"
      				},
      				{
      					"_id" : 3,
      					"name" : "Pulsewidth"
      				}
      			]
      		}
      	]
      }
      > db.bands.update({ "_id" : 1, "albums._id" : 1, "albums.0.tracks._id" : 2 }, { "$set" : { "albums.0.tracks.$.name" : "**Tha" }});
      WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
      > db.bands.findOne();
      {
      	"_id" : 1,
      	"name" : "Aphex Twin",
      	"albums" : [
      		{
      			"_id" : 1,
      			"name" : "Selected Ambient Works 8592",
      			"tracks" : [
      				{
      					"_id" : 1,
      					"name" : "**Tha"
      				},
      				{
      					"_id" : 2,
      					"name" : "Tha"
      				},
      				{
      					"_id" : 3,
      					"name" : "Pulsewidth"
      				}
      			]
      		}
      	]
      }

      2.6.7 (Incorrectly updates the first track name and not the matching second):

      MongoDB shell version: 2.6.7
      connecting to: issue-test
      > db.bands.findOne();
      {
      	"_id" : 1,
      	"name" : "Aphex Twin",
      	"albums" : [
      		{
      			"_id" : 1,
      			"name" : "Selected Ambient Works 8592",
      			"tracks" : [
      				{
      					"_id" : 1,
      					"name" : "Xtal"
      				},
      				{
      					"_id" : 2,
      					"name" : "Tha"
      				},
      				{
      					"_id" : 3,
      					"name" : "Pulsewidth"
      				}
      			]
      		}
      	]
      }
      > db.bands.update({ "_id" : 1, "albums._id" : 1, "albums.0.tracks._id" : 2 }, { "$set" : { "albums.0.tracks.$.name" : "**Tha" }});
      WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
      > db.bands.findOne();
      {
      	"_id" : 1,
      	"name" : "Aphex Twin",
      	"albums" : [
      		{
      			"_id" : 1,
      			"name" : "Selected Ambient Works 8592",
      			"tracks" : [
      				{
      					"_id" : 1,
      					"name" : "**Tha"
      				},
      				{
      					"_id" : 2,
      					"name" : "Tha"
      				},
      				{
      					"_id" : 3,
      					"name" : "Pulsewidth"
      				}
      			]
      		}
      	]
      }

      2.4.12 (Correctly updates the matching sub-document):

      MongoDB shell version: 2.4.12
      connecting to: issue-test
      > db.bands.findOne();
      {
      	"_id" : 1,
      	"name" : "Aphex Twin",
      	"albums" : [
      		{
      			"_id" : 1,
      			"name" : "Selected Ambient Works 8592",
      			"tracks" : [
      				{
      					"_id" : 1,
      					"name" : "Xtal"
      				},
      				{
      					"_id" : 2,
      					"name" : "Tha"
      				},
      				{
      					"_id" : 3,
      					"name" : "Pulsewidth"
      				}
      			]
      		}
      	]
      }
      > db.bands.update({ "_id" : 1, "albums._id" : 1, "albums.0.tracks._id" : 2 }, { "$set" : { "albums.0.tracks.$.name" : "**Tha" }});
      > db.bands.findOne();
      {
      	"_id" : 1,
      	"albums" : [
      		{
      			"_id" : 1,
      			"name" : "Selected Ambient Works 8592",
      			"tracks" : [
      				{
      					"_id" : 1,
      					"name" : "Xtal"
      				},
      				{
      					"_id" : 2,
      					"name" : "**Tha"
      				},
      				{
      					"_id" : 3,
      					"name" : "Pulsewidth"
      				}
      			]
      		}
      	],
      	"name" : "Aphex Twin"
      }

      Show
      Consider the following document: { "_id" : 1, "name" : "Aphex Twin", "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 85–92", "tracks" : [ { "_id" : 1, "name" : "Xtal" }, { "_id" : 2, "name" : "Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ] } I want to update the track with the name "Tha" to "**Tha" for demonstration purposes. Using update with $set, I tell Mongo to find the document with _id of 1, an album sub document _id of 1, an album tracks sub-document with _id of 2 and update it's name. I will show the behaviour from the console: 3.0.0-rc6 (Incorrectly updates the first track name and not the matching second): MongoDB shell version: 3.0.0-rc6 connecting to: issue-test > db.bands.findOne(); { "_id" : 1, "name" : "Aphex Twin", "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 8592", "tracks" : [ { "_id" : 1, "name" : "Xtal" }, { "_id" : 2, "name" : "Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ] } > db.bands.update({ "_id" : 1, "albums._id" : 1, "albums.0.tracks._id" : 2 }, { "$set" : { "albums.0.tracks.$.name" : "**Tha" }}); WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.bands.findOne(); { "_id" : 1, "name" : "Aphex Twin", "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 8592", "tracks" : [ { "_id" : 1, "name" : "**Tha" }, { "_id" : 2, "name" : "Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ] } 2.6.7 (Incorrectly updates the first track name and not the matching second): MongoDB shell version: 2.6.7 connecting to: issue-test > db.bands.findOne(); { "_id" : 1, "name" : "Aphex Twin", "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 8592", "tracks" : [ { "_id" : 1, "name" : "Xtal" }, { "_id" : 2, "name" : "Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ] } > db.bands.update({ "_id" : 1, "albums._id" : 1, "albums.0.tracks._id" : 2 }, { "$set" : { "albums.0.tracks.$.name" : "**Tha" }}); WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.bands.findOne(); { "_id" : 1, "name" : "Aphex Twin", "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 8592", "tracks" : [ { "_id" : 1, "name" : "**Tha" }, { "_id" : 2, "name" : "Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ] } 2.4.12 (Correctly updates the matching sub-document): MongoDB shell version: 2.4.12 connecting to: issue-test > db.bands.findOne(); { "_id" : 1, "name" : "Aphex Twin", "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 8592", "tracks" : [ { "_id" : 1, "name" : "Xtal" }, { "_id" : 2, "name" : "Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ] } > db.bands.update({ "_id" : 1, "albums._id" : 1, "albums.0.tracks._id" : 2 }, { "$set" : { "albums.0.tracks.$.name" : "**Tha" }}); > db.bands.findOne(); { "_id" : 1, "albums" : [ { "_id" : 1, "name" : "Selected Ambient Works 8592", "tracks" : [ { "_id" : 1, "name" : "Xtal" }, { "_id" : 2, "name" : "**Tha" }, { "_id" : 3, "name" : "Pulsewidth" } ] } ], "name" : "Aphex Twin" }

    Description

      In the 2.6.x series and higher (tested up to 3.0.0-rc6), a regression in the behaviour of the positional operator ($) has occurred causing it to only ever update the first sub-document, not the matching sub-document when nested more than one level deep. In essence, it is behaving as a "0" and not a "$" in the update syntax. In 2.4.12 this was working as expected, but not since. See the steps to reproduce for a detailed example.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              durran.jordan@mongodb.com Durran Jordan
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: