我想编写一个查询,根据对文档中数组中存储的两个项的计算,从MongoDB返回文档。
在这种情况下,我想返回数组中最后一项少于同一数组中倒数第二项的所有文档。
MongoDB可以实现吗?你能指出我正确的方向吗?文件?
任何指针都非常感激。
答案 0 :(得分:1)
在现代MongoDB版本(3.2及更高版本)中,此处的最佳操作是使用$redact
根据条件和$arrayElemAt
执行“逻辑过滤器”以从阵列中获取单个值。
给出样本:
{ "_id": 1, "data": [1,2,3] },
{ "_id": 2, "data": [3,2,1] }
然后查询:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$lt": [
{ "$arrayElemAt": [ "$data", -1 ] },
{ "$arrayElemAt": [ "$data", -2 ] }
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
如果文档在数组成员中的子文档中包含“属性”,则应用$map
以便仅提取要检查的属性的值以进行比较。还有$let
的一些帮助,所以你不需要重复表达。
作为样本:
{
"_id": 1,
"data": [
{ "name": "c", "value": 1 },
{ "name": "b", "value": 2 },
{ "name": "a", "value": 3 }
]
},
{
"_id": 2,
"data": [
{ "name": "a", "value": 3 },
{ "name": "b", "value": 2 },
{ "name": "c", "value": 1 }
]
}
查询:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$let": {
"vars": {
"data": {
"$map": {
"input": "$data",
"as": "el",
"in": "$$el.value"
}
}
},
"in": {
"$lt": [
{ "$arrayElemAt": [ "$$data", -1 ] },
{ "$arrayElemAt": [ "$$data", -2 ] }
]
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
获取“属性”值非常重要,因为与其他数组元素相比,Object
的词汇比较不一定与条件匹配。
对于较旧版本的MongoDB或作为替代版本,您可以使用$where
来评估条件:
db.collection.find(function() {
return this.data.pop().value < this.data.pop().value
})
这确实使用JavaScript评估来确定结果,该结果的运行速度比聚合框架的本机编码运算符慢。因此,虽然表达式很容易编写,但这并不是最有效的方法。
虽然在早期版本中使用聚合框架是“可能的”,但你真的不应该这样做。该过程将涉及通过“重新分组”数组从数组中获取$last
元素,然后过滤掉比较以获得“下一个”$last
元素。这对性能来说通常不是一个好主意:
db.collection.aggregate([
// Unwind array
{ "$unwind": "$data" },
// Group back and get $last
{ "$group": {
"_id": "$_id",
"data": { "$push": "$data" },
"lastData": { "$last" "$data" }
}},
// Unwind again
{ "$unwind": "$data" },
// Compare to mark the last element
{ "$project": {
"data": 1,
"lastData": 1,
"seen": { "$eq": [ "$lastData", "$data" ] }
}},
// Filter the previous $last from the list
{ "$match": { "seen": false } },
// Group back and compare values
{ "$group": {
"_id": "$_id",
"data": { "$push": "$data" },
"lastData": { "$last": "$lastData" },
"secondLastData": { "$last": "$data" },
"greater": {
"$last": { "$lt": [ "$data.value", "$lastData.value" ] }
}
}},
// Filter to return only true
{ "$match": { "greater": true } }
])
这是一个非常丑陋的过程,而$where
在这种情况下更清洁,更高效。因此,只有在需要对匹配该条件的数据执行“进一步”聚合操作时,才能在早期的MongoDB版本中使用它。
因此,这里令人信服的案例是获取最新版本,并在“单一”管道阶段使用$redact
进行逻辑比较。每个聚合流水线阶段都会为结果的整体处理时间增加“成本”,因此“少就是多”一如既往。
答案 1 :(得分:0)
如果您有MongoDB 3.2,则可以使用聚合框架来获取符合条件的文档ID。一旦你有了ID,你就可以根据需要迭代进行处理。
示例:
db.collection.aggregate([
{$project:{
cond :{$cond:[{
$gt:[{
$slice:["$v", -2,1]
}, {
$slice:["$v", -1,1]
}]
}, true, false]
}}
},
{$match:{cond: true}}
])
我的收藏中有以下文件:
{
"_id" : ObjectId("57094622b08be16cf12fcf6f"),
"v" : [
1.0,
2.0,
3.0,
4.0,
8.0,
7.0
]
}
{
"_id" : ObjectId("5709462bb08be16cf12fcf70"),
"v" : [
1.0,
2.0,
3.0,
4.0,
8.0,
10.0
]
}
根据您的问题陈述,您要选择数组中最后一个元素小于倒数第二个的文档,因此应选择包含_id = ObjectId("57094622b08be16cf12fcf6f")
的文档
运行聚合查询将产生。
{
"_id" : ObjectId("57094622b08be16cf12fcf6f"),
"cond" : true
}
这是我们所希望的。
正如我上面提到的,您可以迭代返回的信息,并可以采取您想要的任何操作,包括获取完整文档。
注意:如果您的文档很简单,您可以投影字段,并且不需要光标迭代来获取完整文档。但是,在我的示例中,我假设文档很复杂,并且没有文档属性或属性的前期信息。