My current approach is:
var v = 'Value';
Collection.find({arrayToLookIn: v}).forEach(function(obj) {
if (obj.arrayToLookIn.indexOf(v) !== obj.arrayToLookIn.length - 1) {
// do stuff
}
}
I was wondering if there's a way to specify such a rule in the find()
call and do this without the inner check?
I've looked through https://docs.mongodb.org/manual/tutorial/query-documents/#match-an-array-element but didn't spot what I seek.
First question, please be gentle :)
You want$where
, which can use JavaScript evaluation to match the document. So here you ask the evaluating code to test each array element, but not the last one:
Collection.find({
"arrayToLookIn": v,
"$where": function() {
var array = this.arrayToLookIn;
array.pop(); // remove last element
return array.some(function(el) { return el == 'Value' });
}
})
Note that as it is JavaScript sent to the server the "Value" needs to be specified in that code rather than using a variable. You can optionally contruct the JavaScript code as a "string" to join in that variable as a literal and submit that as the argument to $where
.
Note that I'm leaving in the basic equality match, as $where
cannot match using an index like that can, and therefore it's job is to "filter" out the results where the match is on the last element, and not test every single document to find whether it is even there at all.
For the curious, as of the present MongoDB 3.0 release series there is not a really efficient way to do this with the aggregation framework, so the JavaScript evalution is the better option.
You would presently need to do something silly like find the last element in a $group
after $unwind
and then $match
out the value after another $unwind
. It's not efficient and prone to error where the value exists more than once.
Future releases will have a $slice
operator which could be used like this with $redact
:
Collection.aggregate([
// Still wise to do this as mentioned earlier
{ "$match": { "arrayToLookIn": v } },
// Then only return if the match was not in the last element
{ "$redact": {
"$cond": {
"if": {
"$setIsSubset": [
[v],
{ "$slice": [
"$arrayToLookIn",
0,
{ "$subtract": [ { "$size": "$arrayToLookIn" }, 1 ] }
]}
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
Where $setIsSubset
does the comparison of the array which has had it's last entry removed via $slice
by only returning elements from 0
to the $size
minus 1
.
And that should be more efficient than $where
as it uses native coded operations for the comparison, when the next release that has that $slice
for aggregation becomes available.
Not to mention $unwind
also has an option to include the index position in future releases as well. But it's still not a great option even with that addition.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.