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

positional operator projection inclusion can mess up array based non positional operator

    XMLWordPrintable

    Details

    • Backwards Compatibility:
      Fully Compatible
    • Operating System:
      ALL
    • Sprint:
      Query 2019-11-04, Query 2019-11-18
    • Case:

      Description

      A $slice and a positional inclusion on different fields do not work correctly together:

      c.save( { a:[ 1, 2, 3 ], z:[ 11, 12, 13 ] } );
       
      // The first two elements of 'a' are returned.
      printjson( c.find( { z:13 }, { a:{ $slice:2 } } ).toArray() );
       
      // The third element of 'a' is returned (incorrectly), because the third element of 'z' is matched.
      printjson( c.find( { z:13 }, { a:{ $slice:2 }, 'z.$':1 } ).toArray() );
      

      There's also a case where projecting a field of a subobject nested within an array works incorrectly if a positional projection is used on another field. Array field selection should be limited to inclusion fields with a positional operator.

      > c.save( { a:[ { b:1, c:2 }, { b:3, c:4 } ], z:[ 11, 12, 13 ] } )
       
      // Expected projection for a.b:1.
      > c.find( { z:12 }, { 'a.b':1 } )
      { "_id" : ObjectId("503aeb6760273eae2b9d57f7"), "a" : [ { "b" : 1 }, { "b" : 3 } ] }
       
      // In this case, the positional match is applied to the 'a' array as well as the 'z' array.
      > c.find( { z:12 }, { 'a.b':1, 'z.$':1 } )
      { "_id" : ObjectId("503aeb6760273eae2b9d57f7"), "a" : [ { "b" : 3, "c" : 4 } ], "z" : [ 12 ] }
       
      // In this case, the query matched 'z' index 2, which does not exist in the 'a' array, and an error is triggered.
      > c.find( { z:13 }, { 'a.b':1, 'z.$':1 } )
      error: { "$err" : "positional operator element mismatch", "code" : 16353 }
      

      There are other cases where a nested inclusion may conflict with a positional inclusion. In cases like that we should probably uassert:

      > c.save( { a:[ { b:1, c:2 }, { b:3, c:4 } ] } )
      > c.find( { 'a.b':3 }, { 'a.b':1 } )
      { "_id" : ObjectId("503af2d560273eae2b9d57f9"), "a" : [ { "b" : 1 }, { "b" : 3 } ] }
       
      // In this case the a.$ currently supersedes the a.b.  But we might want to uassert instead.
      > c.find( { 'a.b':3 }, { 'a.b':1, 'a.$':1 } )
      { "_id" : ObjectId("503af2d560273eae2b9d57f9"), "a" : [ { "b" : 3, "c" : 4 } ] }
      

        Attachments

          Issue Links

            Activity

              People

              • Votes:
                2 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: