mongo $slice query reverse index out of range

一个人想着一个人 提交于 2019-12-30 10:08:05

问题


The following query in mongo, behaves strange :

db.items.findOne({},{ "List": { "$slice": [ skip, 3 ] }})

First: Instead of returning one object with ["_id","List"] keys only, it returns a full object.

Second: if skip is negative and |skip| is higher than list.length then it returns the first three elements as though skip==0

I would expect for:

{
       "_id" : ObjectId("542babf265f5de9a0d5c2928"),
       "List" : [
                1,
                2,
                3,
                4,
                5
        ]
        "other" : "not_important"
}

query:

db.items.findOne({},{ "List": { "$slice": [-10, 3 ] }})

to get:

{
       "_id" : ObjectId("542babf265f5de9a0d5c2928"),
       "List" : []
}

instead, I get:

{
       "_id" : ObjectId("542babf265f5de9a0d5c2928"),
       "List" : [
                1,
                2,
                3
        ]
        "other" : "not_important"
}

Why?

I use mongoDB 2.4.10


回答1:


Second: if skip is negative and |skip| is higher than list.length then it returns the first three elements as though skip==0

Yes. That is how the javascript Array.prototype.slice() method works, which is internally used by mongodb.

According to the ECMAScript® Language Specification,

If relativeStart is negative, let k be max((len + relativeStart),0); else let k be min(relativeStart, len).

In your case relativeStart is -10, k = max((-10+5),0), k = 0; (where, 5 is the length of your array).

Hence k or skip will always be 0, in these cases.

First: Instead of returning one object with ["_id","List"] keys only, it returns a full object.

Yes, the projection operator works that way. Unless a inclusion or exclusion is explicitly specified in the projection parameter, the whole document is retrieved with the projection operators such as $slice,$elemmatch being applied.

db.items.findOne({},{"_id":1,"List": { "$slice": [-10, 3 ] }})

would return:

{ "_id" : ObjectId("542babf265f5de9a0d5c2928"), "List" : [ 1, 2, 3 ] }

The second parameter to the findOne() method is not only for simple projection purpose, fields are not projected, only if any one of the field names have a value of 0 or 1 against them. If not the whole document is returned. If any field has a projection operator to be applied, it would be applied and projected.

The projection mechanism seems to happen in the below manner, whenever the $slice operator is involved.

  • By default all the fields would be included for projection.
  • By Default all the fields whose values are derived based on the projection operator, $slice, if truthy, are always displayed, irrespective of the below.

Steps taking place for exclusion or inclusion.

  • The list of fields specified in the projection parameter are accumulated in their specified order.
  • For only the first field encountered with value '0' or '1': If the field has a value '0' - then it is excluded, and all the remaining fields are marked to be included. If a field has '1' - then it is included, and all the remaining fields are marked to be excluded.
  • For all the subsequent fields, they are excluded or included based on their values.



回答2:


Whilst this behavior is by design for the $slice operator, it is possible since MongoDB 3.2 to evaluate this and alter the result with the aggregation operator for $slice:

Given the example documents:

{ "_id" : ObjectId("5922846dbcf60428d0f69f6e"), "a" : [ 1, 2, 3, 4 ] }
{ "_id" : ObjectId("5922847cbcf60428d0f69f6f"), "a" : [ 5, 6 ] }

If given a conditional expression to test against the length of the array with $size and only perform the $slice when the reverse index was greater than or equal to that length, or otherwise return an empty array:

db.collection.aggregate([
  { "$project": {
    "a": {
      "$cond": {
        "if": { "$gte": [ { "$size": "$a" }, 4 ] },
        "then": { "$slice": [ "$a", -4, 2 ] },
        "else": { "$literal": [] },
      }
    }
  }}
])

Then of course you get:

{ "_id" : ObjectId("5922846dbcf60428d0f69f6e"), "a" : [ 1, 2 ] }
{ "_id" : ObjectId("5922847cbcf60428d0f69f6f"), "a" : [ ] }

So that is how you could get MongoDB to return a "slice" that acts in this way.



来源:https://stackoverflow.com/questions/26203217/mongo-slice-query-reverse-index-out-of-range

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!