目前最有效的方法是使用 $where 的 JavaScript 评估,因为您可以简单地找到最后一个数组元素的值并以编程方式对其进行测试。
附样本文件:
{ "a": [1,2,3] },
{ "a": [1,2,4] },
{ "a": [1,2,5] }
查询:
db.collection.find(function() { var a = this.a.pop(); return ( a > 2 ) & ( a < 5 ) })
或者简单地用$where作为一个字符串来进行评估:
Model.find(
{
"$where": "var a = this.a.pop(); return ( a > 2 ) && ( a < 5 )"
},
function(err,results) {
// handling here
}
);
这是一种非常简单的方法,并且在为“非规范化”和处理数组而创建的聚合框架中没有“开销”,例如 $unwind。那里效率不高。
然而,在“未来”中,它将会。正如目前在开发版本中可用的那样,聚合框架有一个 $slice 运算符。此运算符将允许轻松访问“最后一个”数组元素以进行测试。
由于聚合框架运算符是在“本机代码”中而不是 JavaScript 来解释的,因此单个管道阶段会变得比 JavaScript 形式更有效。虽然这个清单在提交时看起来更长:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": { "$slice": ["$a",-1] },
"as": "el",
"in":{
"$and": [
{ "$gt": [ "$$el", 2 ] },
{ "$lt": [ "$$el", 5 ] }
]
}
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
已经存在的$redact操作符在这里用比较表达式来“逻辑过滤”。根据 true/false 匹配条件,它分别从结果中“保留”或“修剪”文档。
$slice 运算符本身在它的聚合框架形式中仍然会不合时宜地返回一个数组,尽管在这种情况下是一个单元素数组。这就是为什么使用$map 将每个元素“转换”为true/false 条件,而$anyElementTrue 运算符将“数组”简化为$cond 所表示的单一响应。
所以当它发布时,这将是最有效的方法。但在那之前,请坚持使用 JavaScript,因为它是目前进行此评估的最快方式。
两个查询表单都只返回示例的前两个文档:
{ "a": [1,2,3] },
{ "a": [1,2,4] }