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

$ Positional Operator Only Updating First Sub-Document

    • Type: Icon: Bug Bug
    • Resolution: Duplicate
    • Priority: Icon: Critical - P2 Critical - P2
    • None
    • Affects Version/s: 2.6.7, 3.0.0-rc6
    • Component/s: 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):

      Unable to find source-code formatter for language: console. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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):

      Unable to find source-code formatter for language: console. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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):

      Unable to find source-code formatter for language: console. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      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): Unable to find source-code formatter for language: console. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml 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): Unable to find source-code formatter for language: console. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml 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): Unable to find source-code formatter for language: console. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml 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" }

      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.

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

              Created:
              Updated:
              Resolved: