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

$rename modifier uses incorrect dotted source path

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Critical - P2 Critical - P2
    • 2.6.5, 2.7.6
    • Affects Version/s: 2.6.4
    • Component/s: Write Ops
    • Labels:
      None
    • ALL
    • Hide
      > db.a.drop()
      > db.a.insert({_id:1,a:{b:1}})
      > db.a.update({_id:1},{$rename:{"a.NONE":"a.NEW"}})
      > db.a.find()
      {_id:1, a:{NEW:{b:1}}} 
      Show
      > db.a.drop() > db.a.insert({_id:1,a:{b:1}}) > db.a.update({_id:1},{$rename:{ "a.NONE" : "a.NEW" }}) > db.a.find() {_id:1, a:{NEW:{b:1}}}

      Issue Status as of Sep 08, 2014

      ISSUE SUMMARY
      A $rename modifier may incorrectly select the wrong source path element resulting in the move of the wrong element. This can happen when the full path of the source field cannot be found, as it doesn't exist, but a prefix does exists. In this case the prefix element will be used instead of the element at the full path location. For example, if the source field "a.b.c" doesn't exist but the field "a" does, $rename incorrectly uses "a" as the source field:

      > db.foo.drop()
      > db.foo.insert({_id : 1, a : {}})
      WriteResult({ "nInserted" : 1 })
      > db.foo.update({}, {$rename : {"a.b.c" : "r"}})
      WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
      > db.foo.find()
      { "_id" : 1, "r" : {  } }
      

      The expected and correct behavior is for this $rename to be "no-op" because the source field doesn't exist, but this bug results in document mutation anyway.

      USER IMPACT
      The document can be changed in unexpected ways, due to this $rename bug. These changes can only be reverted if a backup of original data is available. Repeated modifications with $rename to new fields have been seen to produce constantly growing documents which can result in errors to clients and in replication.

      Also, if $rename changes the wrong field, subsequent queries on the source or destination fields may not return expected results:

      > db.foo.drop()
      > db.foo.insert({_id : 1, a : {x : 1}})
      WriteResult({ "nInserted" : 1 })
      > db.foo.insert({_id : 2, a : {x : 2}})
      WriteResult({ "nInserted" : 1 })
      > db.foo.insert({_id : 3, r : {x : 3}})
      WriteResult({ "nInserted" : 1 })
      > db.foo.update({_id : 1}, {$rename : {"a.b" : "r"}})
      WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
      
      > db.foo.find({a: {x : 1}})
      > db.foo.find({r: {$exists : true}})
      { "_id" : 1, "r" : { "x" : 1 } }
      { "_id" : 3, "r" : { "x" : 3 } }
      

      WORKAROUNDS
      Including the source path as part of the query in addition to the $rename will result in making sure that the document isn't selected, since it doesn't have the full path. For the example above:

      > db.foo.update({_id: 1, "a.b" : {$exists : true}}, {$rename : {"a.b" : "r"}})
      WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
      

      AFFECTED VERSIONS
      MongoDB 2.6 production releases are affected by this issue.

      FIX VERSION
      The fix is included in the 2.6.5 production release.

      RESOLUTION DETAILS
      $rename now operates correctly and a missing source field is a no-op, as expected.

      Original description

      When the full path cannot be found, but part can be, rename incorrectly uses that as the source for the rename operation.

      For example, "a.b.c" essentially turns into "a", for this update due to this bug:

      db.a.save({a:{}})
      db.a.update({}, {$rename:{"a.b.c", "r"}})
      

      The correct behavior is for this rename to be "no-op" because the source field doesn't exist.

            Assignee:
            scotthernandez Scott Hernandez (Inactive)
            Reporter:
            asya.kamsky@mongodb.com Asya Kamsky
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: