Mongo DB - 使用map reduce或聚合

时间:2016-03-26 03:18:42

标签: django mongodb mongoengine

我在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来执行调用。

2 个答案:

答案 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"
}