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

$mod results are inconsistent with indexing enabled on Infinity, -Infinity, NaN

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • 3.1.7
    • Affects Version/s: 2.2.7, 2.4.14, 2.6.10, 3.1.5
    • Component/s: Index Maintenance, Querying
    • Minor Change
    • ALL
    • Hide

      In the shell:

      > db.test.insert({ a : Infinity })
      WriteResult({ "nInserted" : 1})
      > db.test.insert({ a: -Infinity })
      WriteResult({ "nInserted" : 1 })
      > db.test.insert({ a: NaN })
      WriteResult({ "nInserted" : 1 })
      > db.test.find({ a : { $mod: [10, -8] }})
      { "_id" : ObjectId("559edbdb37a9ccb7245bb7f9"), "a" : Infinity }
      { "_id" : ObjectId("559edbdc37a9ccb7245bb7fa"), "a" : -Infinity }
      { "_id" : ObjectId("559edbdf37a9ccb7245bb7fb"), "a" : NaN }
      > db.test.ensureIndex({ a: 1})
      {
              "createdCollectionAutomatically" : false,
              "numIndexesBefore" : 1,
              "numIndexesAfter" : 2,
              "ok" : 1
      }
      > db.test.find({ a : { $mod: [10, -8] }})
      > db.test.dropIndex({ a : 1 })
      { "nIndexesWas" : 2, "ok" : 1 }
      > db.test.find({ a : { $mod: [10, -8] }})
      { "_id" : ObjectId("559edbdb37a9ccb7245bb7f9"), "a" : Infinity }
      { "_id" : ObjectId("559edbdc37a9ccb7245bb7fa"), "a" : -Infinity }
      { "_id" : ObjectId("559edbdf37a9ccb7245bb7fb"), "a" : NaN }
      
      
      
      Show
      In the shell: > db.test.insert({ a : Infinity }) WriteResult({ "nInserted" : 1}) > db.test.insert({ a: -Infinity }) WriteResult({ "nInserted" : 1 }) > db.test.insert({ a: NaN }) WriteResult({ "nInserted" : 1 }) > db.test.find({ a : { $mod: [10, -8] }}) { "_id" : ObjectId( "559edbdb37a9ccb7245bb7f9" ), "a" : Infinity } { "_id" : ObjectId( "559edbdc37a9ccb7245bb7fa" ), "a" : -Infinity } { "_id" : ObjectId( "559edbdf37a9ccb7245bb7fb" ), "a" : NaN } > db.test.ensureIndex({ a: 1}) { "createdCollectionAutomatically" : false , "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.test.find({ a : { $mod: [10, -8] }}) > db.test.dropIndex({ a : 1 }) { "nIndexesWas" : 2, "ok" : 1 } > db.test.find({ a : { $mod: [10, -8] }}) { "_id" : ObjectId( "559edbdb37a9ccb7245bb7f9" ), "a" : Infinity } { "_id" : ObjectId( "559edbdc37a9ccb7245bb7fa" ), "a" : -Infinity } { "_id" : ObjectId( "559edbdf37a9ccb7245bb7fb" ), "a" : NaN }

      The $mod operator in find queries currently casts values to long long when matching, using the unsafe BSONElement.numberLong(...). This means NaN, -Infinity, and Infinity are all converted to the minimum long long, although the behavior is undefined. Behavior for doubles out of range of long long is also undefined, although I haven't checked precisely what does occur.

      If $mod is going to convert to long long, it might be appropriate to not match any values outside the range of long long, as all of them will have undefined value.

      This would solve the indexing problem, and we could scan the interval of NumberLong s instead of NumberDouble.

      This issue was introduced because BSONElement.appendMinForType and BSONElement.appendMaxForType appends the maximum and minimum double value, not Infinity. Elsewhere in src/mongo/db/query/index_bounds_builder.cpp, appendMin/MaxForType is not used, in favor of explicitly using Infinity. In practice the only time this function is called is for the $mod and $type match expressions.

            Assignee:
            david.hatch David Hatch
            Reporter:
            david.hatch David Hatch
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: