Core Server
  1. Core Server
  2. SERVER-1475

Arrays are assumed as Objects by $type operator

    Details

    • Type: Bug Bug
    • Status: Open Open
    • Priority: Major - P3 Major - P3
    • Resolution: Unresolved
    • Affects Version/s: 1.4.4
    • Component/s: Querying
    • Labels:
      None
    • Environment:
      Ubuntu 10.04(lucid), x64
    • Backport:
      No
    • Operating System:
      ALL
    • # Replies:
      17
    • Last comment by Customer:
      true

      Description

      Operator $type does not finds array (type=4) elements.

      It tested db, 'aList' field is array. There are records with empty arrays and not empty.
      But request to count elemets by type = array brings zero (0) results;
      >db.mydata.count({ aList: {$type: 4 } }); //type = Array
      0

      Meantime, the following check shows that there are arrays, and som of them are non-empty:
      > var obj = db.mydata.count({ aList: {$type:3} }); //type = Object
      > obj
      33276

      > var zero = db.mydata.count({ aList: {$size: 0 } }); //empty arrays
      > zero
      421251
      > var all = db.mydata.count({ }); //all records
      > all
      454527
      > all - zero // number of non- empty arrays
      33276

      As you see, obj == (all - zero) - all arrays are treated as Objects(type=3)!
      Expected: array fileds should be treated as Arrays (type=4).

        Issue Links

          Activity

          Hide
          Daniel Gottlieb
          added a comment - - edited

          As a caveat, doing an existence test for "a.0" will also match a document such as: {"_id": ..., "a": {"0": "foo", "name": "bar"}}, but I'll venture it's an unlikely edge case.

          test:PRIMARY> db.test.find({$or: [

          {x: []}

          , {'x.0': {$exists: true}}]})

          { "_id" : ObjectId("4f885a1a3b5c50b21c2f5311"), "x" : [ ] } { "_id" : ObjectId("4f885a213b5c50b21c2f5312"), "x" : [ "1" ] }

          { "_id" : ObjectId("4f885b923b5c50b21c2f5314"), "x" :

          { "0" : "foo", "name" : "bar" }

          }

          Show
          Daniel Gottlieb
          added a comment - - edited As a caveat, doing an existence test for "a.0" will also match a document such as: {"_id": ..., "a": {"0": "foo", "name": "bar"}}, but I'll venture it's an unlikely edge case. test:PRIMARY> db.test.find({$or: [ {x: []} , {'x.0': {$exists: true}}]}) { "_id" : ObjectId("4f885a1a3b5c50b21c2f5311"), "x" : [ ] } { "_id" : ObjectId("4f885a213b5c50b21c2f5312"), "x" : [ "1" ] } { "_id" : ObjectId("4f885b923b5c50b21c2f5314"), "x" : { "0" : "foo", "name" : "bar" } }
          Hide
          A. Jesse Jiryu Davis
          added a comment -

          What about adding an $isArray operator to deal with the specific use-case of determining if a field is of array type, while leaving the $type operator's current behavior--matching elements of the array rather than the array itself, consistent with other operators?

          Show
          A. Jesse Jiryu Davis
          added a comment - What about adding an $isArray operator to deal with the specific use-case of determining if a field is of array type, while leaving the $type operator's current behavior--matching elements of the array rather than the array itself, consistent with other operators?
          Hide
          Tobia Conforto
          added a comment -

          Here is an updated workaround to deal with the edge case of a document with key "0":

          > db.test1.find()
          { "_id" : ObjectId("4f88538ef014d20bea9cbb60") }
          { "_id" : ObjectId("4f885397f014d20bea9cbb61"), "a" : 1 }
          { "_id" : ObjectId("4f88539af014d20bea9cbb62"), "a" : [ ] }
          { "_id" : ObjectId("4f88539df014d20bea9cbb63"), "a" : [ 1 ] }
          { "_id" : ObjectId("4f88539ff014d20bea9cbb64"), "a" : [ 1, 2 ] }
          { "_id" : ObjectId("4f885397f014d20bea9cbb65"), "a" : { "0" : "uh" } }
          >
          > db.test1.find({$or: [{"a": {$size: 0}}, {"a": {$elemMatch: {$exists: true}}}]})
          { "_id" : ObjectId("4f88539af014d20bea9cbb62"), "a" : [ ] }
          { "_id" : ObjectId("4f88539df014d20bea9cbb63"), "a" : [  1 ] }
          { "_id" : ObjectId("4f88539ff014d20bea9cbb64"), "a" : [  1,  2 ] }
          >
          

          It tests for a non-empty array using $elemMatch instead of .0
          It's pretty ugly though. A new $isArray operator is probably the best solution.

          Show
          Tobia Conforto
          added a comment - Here is an updated workaround to deal with the edge case of a document with key "0": > db.test1.find() { "_id" : ObjectId("4f88538ef014d20bea9cbb60") } { "_id" : ObjectId("4f885397f014d20bea9cbb61"), "a" : 1 } { "_id" : ObjectId("4f88539af014d20bea9cbb62"), "a" : [ ] } { "_id" : ObjectId("4f88539df014d20bea9cbb63"), "a" : [ 1 ] } { "_id" : ObjectId("4f88539ff014d20bea9cbb64"), "a" : [ 1, 2 ] } { "_id" : ObjectId("4f885397f014d20bea9cbb65"), "a" : { "0" : "uh" } } > > db.test1.find({$or: [{"a": {$size: 0}}, {"a": {$elemMatch: {$exists: true}}}]}) { "_id" : ObjectId("4f88539af014d20bea9cbb62"), "a" : [ ] } { "_id" : ObjectId("4f88539df014d20bea9cbb63"), "a" : [ 1 ] } { "_id" : ObjectId("4f88539ff014d20bea9cbb64"), "a" : [ 1, 2 ] } > It tests for a non-empty array using $elemMatch instead of .0 It's pretty ugly though. A new $isArray operator is probably the best solution.
          Hide
          Thomas Zahn
          added a comment - - edited

          That's a nice work-around, however it doesn't work for nested arrays.
          Consider these arrays:

          mongos> db.test1.find()
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebeef"), "a" : [  7,  10 ] }
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebef0"), "a" : [  1,  2,  3 ] }
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebef1"), "a" : [  [  10,  11 ],  [  20,  21 ] ] }
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebef2"), "a" : [  [  1 ],  [  2 ],  [  3 ] ] }
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebef3"), "a" : [  [ ] ] }
          

          Suppose one now wanted to explicitly find those documents where a specific element of a (e.g. a.0) is again an array, i.e. a 2-dimensional array. Unfortunately, only $size seems to work on a.0, whereas $elemMatch fails:

          mongos> db.test1.find({$or: [{"a.0": {$size: 0}}, {"a.0": {$elemMatch: {$exists: true}}}]})
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebef3"), "a" : [  [ ] ] }
          mongos> db.test1.find({"a.0": {$size: 0}})
          { "_id" : ObjectId("52fcf0e3f067bdb1472ebef3"), "a" : [  [ ] ] }
          mongos> db.test1.find({"a.0": {$elemMatch: {$exists: true}}})
          mongos>
          

          This seems related to the following reported bug in $elemMatch: SERVER-1264.
          So, yes, a new $isArray operator would be great.

          Show
          Thomas Zahn
          added a comment - - edited That's a nice work-around, however it doesn't work for nested arrays. Consider these arrays: mongos> db.test1.find() { "_id" : ObjectId("52fcf0e3f067bdb1472ebeef"), "a" : [ 7, 10 ] } { "_id" : ObjectId("52fcf0e3f067bdb1472ebef0"), "a" : [ 1, 2, 3 ] } { "_id" : ObjectId("52fcf0e3f067bdb1472ebef1"), "a" : [ [ 10, 11 ], [ 20, 21 ] ] } { "_id" : ObjectId("52fcf0e3f067bdb1472ebef2"), "a" : [ [ 1 ], [ 2 ], [ 3 ] ] } { "_id" : ObjectId("52fcf0e3f067bdb1472ebef3"), "a" : [ [ ] ] } Suppose one now wanted to explicitly find those documents where a specific element of a (e.g. a.0 ) is again an array, i.e. a 2-dimensional array. Unfortunately, only $size seems to work on a.0 , whereas $elemMatch fails: mongos> db.test1.find({$or: [{"a.0": {$size: 0}}, {"a.0": {$elemMatch: {$exists: true}}}]}) { "_id" : ObjectId("52fcf0e3f067bdb1472ebef3"), "a" : [ [ ] ] } mongos> db.test1.find({"a.0": {$size: 0}}) { "_id" : ObjectId("52fcf0e3f067bdb1472ebef3"), "a" : [ [ ] ] } mongos> db.test1.find({"a.0": {$elemMatch: {$exists: true}}}) mongos> This seems related to the following reported bug in $elemMatch : SERVER-1264 . So, yes, a new $isArray operator would be great.
          Hide
          aurelien lambert
          added a comment -

          The javascript workaround is as said not efficient, and then non javascript workaround requires an $or at the root, which makes it tricky if one needs to check if several fields are arrays.

          Wouldn't it be more simple if the $all:[] could do it ? It would look like

          > db.test1.find()
          { "a" : [ 0 ] }
          { "a" : [ ] }
          { "a" : 0 }
          > db.test1.find({a : {$all : []}})
          { "a" : [ 0 ] }
          { "a" : [ ] }
          

          Isn't it simple ?

          Show
          aurelien lambert
          added a comment - The javascript workaround is as said not efficient, and then non javascript workaround requires an $or at the root, which makes it tricky if one needs to check if several fields are arrays. Wouldn't it be more simple if the $all:[] could do it ? It would look like > db.test1.find() { "a" : [ 0 ] } { "a" : [ ] } { "a" : 0 } > db.test1.find({a : {$all : []}}) { "a" : [ 0 ] } { "a" : [ ] } Isn't it simple ?

            People

            • Votes:
              13 Vote for this issue
              Watchers:
              18 Start watching this issue

              Dates

              • Created:
                Updated:
                Days since reply:
                7 weeks ago
                Date of 1st Reply: