我知道这个问题很老,但我在回答similar new question 后在谷歌上找到了它。所以我认为这应该得到同样的待遇。
您可以改用aggregate 来避免$where 对性能的影响:
db.example.aggregate([
// Use an index, which $where cannot to narrow down
{$match: { "comments.by": "Abe" }},
// De-normalize the Array
{$unwind: "$comments"},
// The order of the array is maintained, so just look for the $last by _id
{$group: { _id: "$_id", comments: {$last: "$comment"} }},
// Match only where that $last comment by `by.Abe`
{$match: { "comments.by": "Abe" }},
// Retain the original _id order
{$sort: { _id: 1 }}
])
这应该围绕 $where 运行,因为我们能够首先缩小有“Abe”评论的文档的范围。正如警告的那样,$where 将测试集合中的每个文档,并且永远不会使用索引,即使有要使用的索引。
当然,您也可以使用technique described here 维护原始文档,因此一切都可以像find() 一样工作。
对于任何发现这个的人来说都是值得深思的。
现代 MongoDB 版本更新
现代版本添加了 $redact 管道表达式以及 $arrayElemAt(后者是 3.2 的版本,所以这将是这里的最小版本),它们结合起来将允许逻辑表达式检查最后一个元素未处理 $unwind 阶段的数组:
db.example.aggregate([
{ "$match": { "comments.by": "Abe" }},
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$arrayElemAt": [ "$comments.by", -1 ] },
"Abe"
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
这里的逻辑是通过比较$arrayElemAt 获取数组-1 的最后一个索引来完成的,该索引通过$map 转换为"by" 属性中的值的数组。这允许将单个值与所需参数 "Abe" 进行比较。
在 MongoDB 3.6 及更高版本中使用 $expr 甚至更现代一点:
db.example.find({
"comments.by": "Abe",
"$expr": {
"$eq": [
{ "$arrayElemAt": [ "$comments.by", -1 ] },
"Abe"
]
}
})
这将是迄今为止匹配数组中最后一个元素的最高效的解决方案,并且实际上有望在大多数情况下取代 $where 的使用,尤其是在这里。