假设我在数据库中有以下文档结构:
{ name: String,
subDocs: [{
index: Number,
value: Number
}]
}
所以典型的文档看起来像:
{ name: 'Document 1',
subDocs: [
{ index: 0, value: 30 },
{ index: 1, value: 40 },
{ index: 2, value: 10 },
{ index: 3, value: 20 },
{ index: 4, value: 700 },
{ index: 5, value: 40 }
]
}
现在,我想找到包含值为A = 10和B = 40的subDocs的所有文档。但是数组中项目的出现必须满足以下要求A.index <B.index。因此,基本上具有值A的项必须出现在集合中比B更早的位置。因此上面的对象满足要求而不是这个,因为值不会出现在顺序中:
{ name: 'Document 2',
subDocs: [
{ index: 0, value: 40 },
{ index: 1, value: 70 },
{ index: 2, value: 10 },
{ index: 3, value: 20 },
{ index: 4, value: 700 }
]
}
我们可以用Mongoose实现它而不牺牲这种查询的性能吗?
如果你想在查询中使用那种约束,那么你基本上有两个选项,具体取决于你的MongoDB版本支持的内容:
您最好在任何正常查询条件下“使用”$expr
来实际选择有效文档:
var A = 10, B = 40;
Model.find({
"subDocs.value": { "$all": [A, B] },
"$expr": {
"$lt": [
{ "$arrayElemAt": [
"$subDocs.index",
{ "$indexOfArray": [ "$subDocs.value", A ]}
]},
{ "$arrayElemAt": [
"$subDocs.index",
{ "$indexOfArray": [ "$subDocs.value", B ]}
]}
]
}
})
或匹配“最后”事件:
Model.find({
"subDocs.value": { "$all": [A, B] },
"$expr": {
"$lt": [
{ "$arrayElemAt": [
"$subDocs.index",
{ "$subtract": [
{ "$subtract": [{ "$size": "$subDocs.value" }, 1 ] },
{ "$indexOfArray": [ { "$reverseArray": "$subDocs.value" }, A ] }
]}
]},
{ "$arrayElemAt": [
"$subDocs.index",
{ "$subtract": [
{ "$subtract": [{ "$size": "$subDocs.value" }, 1 ] },
{ "$indexOfArray": [ { "$reverseArray": "$subDocs.value" }, B ] }
]}
]}
]
}
})
同样的事情,但没有本机运算符,您需要使用$where
的JavaScript评估:
var A = 10, B = 40;
Model.find({
"subDocs.value": { "$all": [A, B] },
"$where": `this.subDocs.find( e => e.value === ${A}).index
< this.subDocs.find( e => e.value === ${B}).index`
})
或匹配“最后”事件:
Model.find({
"subDocs.value": { "$all": [10,40] },
"$where": `let arr = this.subDocs.reverse();
return arr.find( e => e.value === ${A}).index
> arr.find( e => e.value === ${B}).index`
})
如果您在聚合管道中需要它,那么您将使用$redact
和类似的逻辑来代替第一个示例:
var A = 10, B = 40;
Model.aggregate([
{ "$match": { "subDocs.value": { "$all": [A, B] } } },
{ "$redact": {
"$cond": {
"if": {
"$lt": [
{ "$arrayElemAt": [
"$subDocs.index",
{ "$indexOfArray": [ "$subDocs.value", A ]}
]},
{ "$arrayElemAt": [
"$subDocs.index",
{ "$indexOfArray": [ "$subDocs.value", B ]}
]}
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
可以说“比较逻辑”实际上并不是“查询运算符表达式”本身的原生,因此“最佳”可以应用于索引的唯一部分是在所有情况下使用$all
查询运算符。剩下的基本逻辑实际上应用“在”主要表达式被评估之后和“除了”之外,以便没有返回任何结果,除了那些满足$expr
或$where
表达式的结果。
每个的基本逻辑基本上是从“第一个”数组成员中提取"index"
属性的值,该成员实际匹配"value"
属性中的相应值。如果这是“小于”,那么条件是true
,这满足了返回的文件。
因此,请注意,“计算的评估”与查询运算符的效率相匹配,并且不与其他能够访问“索引”的查询运算符条件“组合”使用,则将启动“完整集合扫描”。
但是总体结果肯定比将所有匹配项返回到第一个查询条件然后在“从数据库返回”之后的光标上拒绝它们更有效。
另请参阅$arrayElemAt
,$indexOfArray
,$lt
和Array.find()
的JavaScript文档