2014-04-04 92 views
2

我試圖根據10秒的間隔計算我的數據庫中文檔的頻率。使用mongodb聚合框架計算頻率

這是我的數據庫對象看起來怎麼樣:

[ 
    { 
    created_at: "2014-03-31T22:30:48.000Z", 
    id: 450762158586880000, 
    _id: "5339ec9808eb125965f2eae1" 
    }, 
    { 
    created_at: "2014-03-31T22:30:48.000Z", 
    id: 450762160407597060, 
    _id: "5339ec9808eb125965f2eae2" 
    }, 
    { 
    created_at: "2014-03-31T22:30:49.000Z", 
    id: 450762163482017800, 
    _id: "5339ec9908eb125965f2eae3" 
    }, 
    { 
    created_at: "2014-03-31T22:30:49.000Z", 
    id: 450762166367707140, 
    _id: "5339ec9908eb125965f2eae4" 
    }, 
    { 
    created_at: "2014-03-31T22:30:50.000Z", 
    id: 450762167412064260, 
    _id: "5339ec9a08eb125965f2eae5" 
    } 
] 

我已成功地顯示在給定的時間間隔的頻率,但我想獲得,每10秒。所以最好我的JSON看起來像:

[ 
    { 
    time_from: "2014-03-31T22:30:48.000Z", 
    time_to: "2014-03-31T22:30:58.000Z", 
    count: 6 
    }, 
    { 
    time_from: "2014-03-31T22:30:58.000Z", 
    time_to: "2014-03-31T22:31:08.000Z", 
    count: 3 
    }, 
    { 
    time_from: "2014-03-31T22:31:08.000Z", 
    time_to: "2014-03-31T22:31:18.000Z", 
    count: 10 
    }, 
    { 
    time_from: "2014-03-31T22:31:18.000Z", 
    time_to: "2014-03-31T22:31:28.000Z", 
    count: 1 
    }, 
    { 
    time_from: "2014-03-31T22:31:28.000Z", 
    time_to: "2014-03-31T22:31:38.000Z", 
    count: 3 
    } 
] 

這是我迄今所做的:

exports.findAll = function (req, res) { 
    db.collection(collection_name, function (err, collection) { 
     collection.find().toArray(function (err, items) { 
      collection.find().sort({"_id": 1}).limit(1).toArray(function (err, doc) { 
       var interval = 100000; // in milliseconds 
       var startTime = doc[0].created_at; 
       var endTime = new Date(+startTime + interval); 

       collection.aggregate([ 
        {$match: {"created_at": {$gte: startTime, $lt: endTime}}}, 
        {$group: {"_id": 1, "count":{$sum: 1}}} 
       ], function(err, result){ 
        console.log(result); 
        res.send(result); 
       }); 
      }); 
     }) 
    }); 
}; 

,這是這個結果:

[ 
    { 
    _id: 1, 
    count: 247 
    } 
] 

編輯:

collection.aggregate([ 
        { $group: { 
         _id: { 
          year: { '$year': '$created_at'}, 
          month: {'$month': '$created_at'}, 
          day: {'$dayOfMonth': '$created_at'}, 
          hour: {'$hour': '$created_at'}, 
          minute: {'$minute': '$created_at'}, 
          second: {'$second': '$created_at'} 
         }, 
         count: { $sum : 1 } 
        } } 
       ], function (err, result) { 
        console.log(result); 
        res.send(result); 
       }); 

其結果是:

[ 
    { 
    _id: { 
     year: 2014, 
     month: 3, 
     day: 31, 
     hour: 22, 
     minute: 37, 
     second: 10 
    }, 
    count: 6 
    }, ... 

新的進展,現在我怎麼會顯示它在10秒的時間間隔?

回答

1

如果它只是約10秒的時間間隔內得到的東西,你可以做一個小的數學,並通過總運行此:所以

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "year": { "$year": "$created_at" }, 
      "month":{ "$month": "$created_at" }, 
      "day": { "$dayOfMonth": "$created_at" }, 
      "hour": { "$hour": "$created_at" }, 
      "minute": { "$minute": "$created_at" }, 
      "second": { "$subtract": [ 
       { "$second": "$created_at" }, 
       { "$mod": [ 
        { "$second": "$created_at" }, 
        10 
       ]} 
      ]} 
     }, 
     "count": { "$sum" : 1 } 
    }} 
]) 

,打破下來,以10秒的間隔一分鐘他們在哪裏出現了一個10碼的數學模型。

我認爲這是合理的,並且因爲它使用聚合,所以會是跑得最快的。如果你真的需要如從初次匹配時間是跑10秒鐘內你的序列,那麼你可以用MapReduce的過程:

首先映射器:

var mapper = function() { 

    if (this.created_at.getTime() > (last_date + 10000)) { 
     if (last_date == 0) { 
      last_date = this.created_at.getTime(); 
     } else { 
      last_date += 10000; 
     } 
    } 

    emit(
     { 
      start: new Date(last_date), 
      end: new Date(last_date + 10000) 
     }, 
     this.created_at 
    ); 

} 

所以這是要發出10秒時間間隔內的日期,開始第一次約會,然後增加間隔每次的東西被發現超出範圍

現在你需要一個減速機:

var reducer = function (key, values) { 
    return values.length; 
}; 

非常簡單。只返回傳入的數組的長度

因爲MapReduce的工作方式是這樣,任何不是有一個以上的值不會傳遞到減速,因此與敲定清理它。

var finalize = function (key, value) { 
    if (typeof(value) == "object") { 
     value = 1; 
    } 
    return value; 
}; 

然後只需運行它即可獲得結果。請注意,在映射器中使用的是通過一個全局變量的「範圍」一節:

db.collection.mapReduce(
    mapper, 
    reducer, 
    { 
     "out": { "inline": 1 }, 
     "scope": { "last_date": 0 }, 
     "finalize": finalize 
    } 
) 

每種方法很可能會給略微不同的結果,但就是這一點。這取決於你真正想使用哪一個。


考慮您的評論,你既可以「考察」無論從語句的輸出和「填補空白」編程,因爲它是。我通常更喜歡這個選項,但它不是我的程序,我不知道你試圖從這個查詢中檢索的系列有多大。

在服務器端,您可以修補的「映射」做一些像這樣的:

var mapper = function() { 

    if (this.created_at.getTime() > (last_date + 10000)) { 

     if (last_date == 0) { 
      last_date = this.created_at.getTime(); 
     } else { 
      // Patching for empty blocks 
      var times = Math.floor( 
       (this.created_at.getTime() - last_date)/10000 
      ); 

      if (times > 1) { 
       for (var i=1; i < times; i++) { 
        last_date += 10000; 
        emit(
         { 
          start: new Date(last_date), 
          end: new Date(last_date + 10000) 
         }, 
         0 
        ); 
       } 
      } 
      // End patch 
      last_date += 10000; 
     } 
    } 

    emit(
     { 
      start: new Date(last_date), 
      end: new Date(last_date + 10000) 
     }, 
     this.created_at 
    ); 

} 
+0

這是90%的什麼我正在尋找,但我想獲得價值= 0,沒有數據。聚合只是跳過這個地方,即使文檔沒有在間隔中創建,mapReduce似乎也會使值= 1。有任何想法嗎?再次感謝您發佈的內容! – knowbody

+0

@knowbody我幾乎只是有一個評論,但實際的內容被添加到解釋和解決的答案。 –

+0

再次感謝你。這對我幫助很大! – knowbody