I wonder what can I do to enable inequalities on the size of arrays in mongodb queries.
For example, I have the following two queries:
> db.userStats.count({sessions:{$size:1}})
1381
> db.userStats.count({sessions:{$size:{$gt:0}}})
0
If $gt
worked as I intend the result would be different for the second query, leading to a number that is at least 1381.
What is wrong with the second query and how can I fix it to achieve the result I want?
thanks
You cannot do that as is actually referenced in the documentation for $size
. So that operator alone evaluates a "literal" result and cannot be use in conjunction with "range operators" such as you are asking.
Your only real options are to ask for a "size" that is "not equal to 0" by negating the condition with the $not
operator. Which is perfectly valid:
db.userStats.count({ "sessions": { "$not": { "$size": 0 } } });
Or otherwise test with the other incarnation of $size
in the aggregation framework, as long as your MongoDB version is 2.6 or greater:
db.userStats.aggregate([
{ "$group": {
"_id": null,
"count": {
"$sum": {
"$cond": [
{ "$gt": [ { "$size": "$sessions", 0 } ] },
1,
0
]
}
}
}}
])
Possibly also with the $where
form of JavaScript evaluation:
db.userStats.count(function() { return this.sessions.length > 0 });
But likely slower than the last version.
Or in fact you can just do this with "dot notation", and the $exists
operator:
db.userStats.count({ "sesssions.0": { "$exists": true } });
As the general idea is that if there is an element at the index 0
then the array has some length.
It all depends on your approach, but any of these forms gets the right result.
But for the "best" performance from MongoDB, don't use any of these methods. Instead, keep the array "length" as a property of the document you are inspecting. This you can "index" and the queries issued can actually access that index, which none of the above can do.
Maintain it like this:
db.userStats.update(
{ "_id": docId, "sessions": { "$ne": newItem } },
{
"$push": { "sessions": newItem },
"$inc": { "countSessions": 1 }
}
)
Or to remove:
db.userStats.update(
{ "_id": docId, "sessions": newItem },
{
"$pull": { "sessions": newItem },
"$inc": { "countSessions": -1 }
}
)
Then you can just query on "countSesssions" which can also index for best performance:
db.userStats.find({ "countSessions": { "$gt": 0 } })
And that is the "best" way to do it.
$size does not accept a range of value. See http://docs.mongodb.org/manual/reference/operator/query/size/
To select $size for a range of value, for example from 1 to 3, you may use $or
db.userStats.count( { $or: [ {sessions:{$size:1}}, {sessions:{$size:2}}, {sessions:{$size:3}} ] } )
Certainly, $or is a working but not good solution. Do as MongoDB suggests:
To select documents based on fields with different numbers of elements, create a counter field that you increment when you add elements to a field.
来源:https://stackoverflow.com/questions/26159975/mongodb-query-size-with-gt-returns-always-0