MongoDB: how to compare $size of array to another document item?

二次信任 提交于 2019-12-22 10:20:05

问题


MongoDB: how to do something like this, both in mongo console and via JavaScript, Node.js:

db.turnys.find( { users:{$size:seats } } )

turnydb.turnys looks like this:

[
  {
    "gId": "5335e4a7b8cf51bcd054b423",
    "seats": 2,
    "start": "2014-03-30T14:23:29.688Z",
    "end": "2014-03-30T14:25:29.688Z",
    "rMin": 800,
    "rMax": 900,
    "users": [],
    "_id": "533828e1d1a483b7fd8a707a"
  },
  {
    "gId": "5335e4a7b8cf51bcd054b423",
    "seats": 2,
    "start": "2014-03-30T14:23:29.688Z",
    "end": "2014-03-30T14:25:29.688Z",
    "rMin": 900,
    "rMax": 1000,
    "users": [],
    "_id": "533828e1d1a483b7fd8a707b"
  },
...
]

回答1:


There are real problems with the acceptance of using the $where operator in any sort of way. Especially considering some of your "later" questions that have been raised. You need to read the manual page on this operator (as given in the link).

The short points:

  • The $where operator invokes a full collection scan in order to do it's comparisons. This is bad news in "real world" cases since there is no way to "constrain" this search to use an index in any way, and thus reduce the working set.

  • By nature, this operator invokes "arbitrary JavaScript execution". And this means two things. 1.) Every document must be expanded from it's native BSON form into the "JavaScript" object form (at a cost) for each comparison. 2.) You are invoking a "JavaScript" interpreter, that is not native code, for each and every comparison.

One of the answers gave you valid warnings on this, though it did not provide you with an actual answer. So here it is:

As stated, yes you should actually be keeping a "count" of the "array" elements within your document if this is important to you. But whilst you may have seen this as valid, the proper comparison is done as follows.

So let us assume you did use $inc as suggested to keep a total_users field in your document.

db.collection.aggregate([
    { "$project": {
        "gId": 1,
        "alloc": { "$eq": [ "$total_users", "$seats" ] }
    }},
    { "$match": { "alloc": 1 } }
])

Where essentially you can $project all the fields you want in the document, just as long as you make the comparison as shown with the "alloc" field by simply doing a "logical" comparison as shown with the $eq operator.

In future releases ( very soon as of writing ) you even get to find out the $size of an array within the "aggregation" operation. So "instead" of keeping a counter on the "array" members you can do this:

db.collection.aggregate([
    { "$project": {
        "gId": 1,
        "alloc": { "$eq": [ { "$size": "$users" }, "$seats" ] }
    }},
    { "$match": { "alloc": 1 } }
])

But naturally that will come with a "cost", so still the best option is to keep that "counter" on your document, depending on your needs.

While both approaches here do *still** result in a "collection scan" (in this context since there was no other $match condition), the end result will be many times faster than the "JavaScript" execution that was shown in other answers.

Use the "native code" methods as a best practice solution.




回答2:


While you could use $where as proposed by others in order to do cross field comparisons, I'd not recommend it if possible:

No indexes can be used for this search. Mongo will scan every document and execute a JavaScript expression against each one to do the cross-field comparison. This will be an expensive and slow operation.

Coming in the next version of MongoDB (2.6), you'll be able to to use the $size operator in an aggregation which could compare two fields.

But, for the best performance, I'd recommend you store a materialized boolean that would store whether the number of elements in the users array is equal to the number stored in seats. You can easily store the number of current users using $inc (positive and negative) if you don't want to fetch the entire array in order to get the count and do the comparison against the field "seats".

If you must use $where, I'd recommend you try to do some filtering if possible that might help avoid a full table scan. Maybe date ranges, or checking whether the users array is empty, etc.




回答3:


You can use a $where query:

db.turnys.find({ $where: "this.users.length == this.seats" })



回答4:


You can use the $where operator with a String that contians a JS expression or using directly a JavaScript function:

> db.turnys.find(function() { return this.users.length == this.seats })



回答5:


You Could use any one of the following ways

db.flight.find({"$where":"function() { return this.users.length == this.seats}" })

or

db.turnys.find({ $where: "this.users.length == this.seats" })

or

db.turnys.find(function() { return this.users.length == this.seats })

These all will serve your purpose but the thing is efficiency and "$where" queries should not be used unless strictly necessary: they are much slower than regular queries, so try and avoid queries with "$where".



来源:https://stackoverflow.com/questions/22747449/mongodb-how-to-compare-size-of-array-to-another-document-item

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