我在MongoDB集合中有一系列文档,如下所示:
{ 'time' : '2016-03-28 12:12:00', 'value' : 90 },
{ 'time' : '2016-03-28 12:13:00', 'value' : 82 },
{ 'time' : '2016-03-28 12:14:00', 'value' : 75 },
{ 'time' : '2016-03-28 12:15:00', 'value' : 72 },
{ 'time' : '2016-03-28 12:16:00', 'value' : 81 },
{ 'time' : '2016-03-28 12:17:00', 'value' : 90 },
etc....
任务是 - 垃圾保留值为80时,查找值entering
低于80且exiting
高于80
{ 'time' : '2016-03-28 12:14:00', 'result' : 'enter' },
{ 'time' : '2016-03-28 12:16:00', 'result' : 'exit' },
是否可以使用map reduce或聚合查询来产生这样的结果? 我试图通过排序结果循环,但它是非常昂贵的处理和内存 - 我需要做一系列这样的检查。
PS。我正在使用Django和mongoengine来执行调用。
答案 0 :(得分:2)
我不确定单独使用MongoDB聚合框架是否可行,因为正如@BlakesSeven所述,后续文档之间没有链接/连接。并且您需要此连接来检查新值是否低于或高于所需的阈值,而不是之前的文档中的值与之前的值相比。
这是一个天真的 pure-python (因为它用Django和MongoEngine标记)解决方案,循环遍历排序结果,保持阈值跟踪变量,并在它变得越来越高时捕获80( col
是您的收藏参考):
THRESHOLD = 80
cursor = col.find().sort("time")
first_value = next(cursor)
more_than = first_value["value"] >= THRESHOLD
for document in cursor:
if document["value"] < THRESHOLD:
if more_than:
print({"time": document["time"], "result": "enter"})
more_than = False
else:
if not more_than:
print({"time": document["time"], "result": "exit"})
more_than = True
对于提供的样本数据,它会打印:
{'time': '2016-03-28 12:14:00', 'result': 'enter'}
{'time': '2016-03-28 12:16:00', 'result': 'exit'}
作为旁注和替代解决方案..如果您可以控制这些记录的插入方式,当您将文档插入此集合时,您可以检查最新的value
是什么,将其与阈值并将result
设置为单独的字段。然后,查询进入和退出阈值点将变得如此简单:
col.find({"result" : {$exists : true}})
您可以将此方法命名为“事先标记阈值”。这可能只有从查询/搜索性能角度来看才有意义,如果你打算经常这样做的话。
答案 1 :(得分:1)
借助聚合框架和游标迭代,您可以轻松实现文档转换。
示例:
db.collection.aggregate([
{$project:
{
value:1,
"threshold":{$let:
{
vars: {threshold: 80 },
in: "$$threshold"
}}
}
},
{$match:{value:{$ne: "$threshold"}}},
{$group:
{
_id:"$null",
low:{
$max:{
$cond:[{$lt:["$value","$threshold"]},"$value",-1]
}
},
high:{
$min:{
// 10000000000 is a superficial value.
// need something greater than values in documents
$cond:[{$gt:["$value","$threshold"]},"$value",10000000000]
}
},
threshold:{$first:"$threshold"}
}
}
])
聚合框架将返回包含两个值的文档。
{
"_id" : null,
"low" : NumberInt(75),
"high" : NumberInt(81),
"threshold" : NumberInt(80)
}
我们可以轻松找到符合退货条件的文件。例如在NodeJS中,我们可以轻松地做到这一点。假设变量 result
包含聚合查询的结果。
result.forEach(function(r){
var documents = [];
db.collection.find({$or:[{"value": r.low},{"value": r.high}]}).forEach(function(doc){
var _doc = {};
_doc.time = doc.time;
_doc.result = doc.value < r.threshold ? "enter" : "exit";
documents.push(_doc);
});
printjson(documents);
});
如您所述,如果您的输入文件是(样本)
{ 'time' : '2016-03-28 12:12:00', 'value' : 90 },
{ 'time' : '2016-03-28 12:13:00', 'value' : 82 },
{ 'time' : '2016-03-28 12:14:00', 'value' : 75 },
{ 'time' : '2016-03-28 12:15:00', 'value' : 72 },
{ 'time' : '2016-03-28 12:16:00', 'value' : 81 },
{ 'time' : '2016-03-28 12:17:00', 'value' : 90 },
etc....
上面的解决方案中的查询将发出:
{
"time" : "2016-03-28 12:14:00",
"result" : "enter"
},
{
"time" : "2016-03-28 12:16:00",
"result" : "exit"
}