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

Support $elemMatch with DBRefs that may contain additional properties

    XMLWordPrintable

Details

    • Bug
    • Status: Closed
    • Major - P3
    • Resolution: Fixed
    • 2.5.4
    • 2.6.0-rc0
    • Querying
    • None
    • Fully Compatible
    • ALL

    Description

      tl;dr:

      • DBRefs are objects whose first two fields must be $ref and $id (in that order). An optional $db field, if present, must appear third. Other fields may follow (they can't have a $ prefix, of course).
      • $elemMatch should have consistent behavior, regardless of whether its argument or the value it is matching against look like DBRef objects.

      The current implementation simply treats an $elemMatch on a DBRef as an equality match, which doesn't seem correct. Consider the following, which uses the latest nightly:

      > db.runCommand('buildInfo').gitVersion
      8bad5a8633eeca3bcbb975aabf268db9921f2c57
       
      > db.people.insert({_id: 1})
      Insert WriteResult({ "ok" : 1, "n" : 1 })
       
      > db.people.insert({_id: 2, friends: [ {$ref: "people", $id: 1, $db: "test", _doctrine_class_name: "Documents\People" } ]})
      Insert WriteResult({ "ok" : 1, "n" : 1 })
       
      > db.people.insert({_id: 3, friends: [ {$ref: "people", $id: 2, $db: "test" } ]})
      Insert WriteResult({ "ok" : 1, "n" : 1 })

      First, I create three documents in the test.people collection. The middle document has an extra field in its DBRef object, which is something Doctrine ODM has done for years to discriminate references to super-classes. Based on the DBRef docs, they're only objects that required $ref and $id as the first two fields with an optional $db to follow. There was never a restriction on additional fields, even though the shell likes to hide them (as it does $db).

      > db.people.find()
      { "_id" : 1 }
      { "_id" : 2, "friends" : [ DBRef("people", 1) ] }
      { "_id" : 3, "friends" : [ DBRef("people", 2) ] }

      The example below demonstrates the shortcoming of treating $elemMatch as an equality comparison, and relying on _isDBRefDocument() for the detection:

      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $db: "test" }}})
      2014-01-06T11:45:33.768-0500 error: {
      	"$err" : "Can't canonicalize query: BadValue unknown operator: $ref",
      	"code" : 17287
      } at src/mongo/shell/query.js:131
      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $id: 1 }}})
      null
      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $id: 1, $db: "test" }}})
      null
      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $id: 1, $db: "test", _doctrine_class_name: "Documents\People" }}})
      2014-01-06T11:46:09.520-0500 error: {
      	"$err" : "Can't canonicalize query: BadValue unknown operator: $ref",
      	"code" : 17287
      } at src/mongo/shell/query.js:131

      In the first and last example, we get the original error because $elemMatch doesn't recognize its argument as a DBRef and apply the patch logic. But even with the patch, the equality comparison fails to match anything in the middle two examples, since the DBRef in the database does have an extra _doctrine_class_name field within.

      Repeating the test with the third example document, which doesn't include the extra Doctrine field:

      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $db: "test" }}})
      2014-01-06T11:46:37.305-0500 error: {
      	"$err" : "Can't canonicalize query: BadValue unknown operator: $ref",
      	"code" : 17287
      } at src/mongo/shell/query.js:131
      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $id: 2 }}})
      null
      > db.people.findOne({friends: { $elemMatch: { $ref: "people", $id: 2, $db: "test" }}})
      { "_id" : 3, "friends" : [ DBRef("people", 2) ] }

      We still have the same shortcomings, although the equality match in the last example does happen to work. I'm still not keen on that behavior because this is nothing like a real $elemMatch evaluation.

      Attachments

        Issue Links

          Activity

            People

              benety.goh@mongodb.com Benety Goh
              jmikola@mongodb.com Jeremy Mikola
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: