2011-12-29 181 views
2

我每小時都將條目添加到模式中,以便跟蹤數天內的增長情況,同時保持當天的當前分數。現在我希望能夠在過去一週內每天取得最近的記錄。結果將是在6天前的午夜或其附近的6條記錄,第7天是當天的最新記錄。查找最近7天當天的最後一個文檔

這裏是我的架構:

var schema = new Schema({ 
    aid: { type: Number } 
, name: { type: String } 
, score: { type: Number } 
, createdAt: { type: Date, default: Date.now() } 
}) 

編輯

我用這種靜態的嘗試,但它拉完全相同的創紀錄的700倍

schema.statics.getLastWeek = function(name, fn) { 
    var oneday = 60 * 60 * 24 
    , now = Date.now() 
    , docs = [] 

    for (var i = 1; i <= 7; i++) { 
    this.where('name', new RegExp(name, 'i')) 
    .where('createdAt') 
    .gte(now - (i * oneday)) 
    .desc('createdAt') 
    .findOne(function(err,doc){ 
     docs.push(doc) 
    }) 
    } 
} 

如果我是使用SQL我會做一個子查詢選擇MAXDATE並將其加入我的主要查詢爲了檢索我想要的結果。無論如何在這裏做到這一點?

回答

6

Kristina Chodorow gives a detailed recipe for this exact task in her book MongoDB: The Definitive Guide

假設我們有跟蹤股票價格的一個網站。從上午10點到下午4點,每隔幾分鐘可獲得 存儲在MongoDB中的最新價格 。現在,作爲報告申請的一部分,我們希望找到過去30天的收盤價。這可以使用組輕鬆完成 。

我對Mongoose不太熟悉,但是我已經嘗試將她的示例調整爲適合您的情況。請注意,我改變了createdAtdefault財產價值的功能,並增加了額外的字段datestamp到您的模式:

var oneday = 24 * 60 * 60; 

var schema = new Schema({ 
    aid: { type: Number } 
, name: { type: String } 
, score: { type: Number } 

    // default: is a function and called every time; not a one-time value! 
, createdAt: { type: Date, default: Date.now } 

    // For grouping by day; documents created on same day should have same value 
, datestamp: { type: Number 
      , default: function() { return Math.floor(Date.now()/oneday); } 
      } 
}); 


schema.statics.getLastWeek = function(name, fn) { 
    var oneweekago = Date.now() - (7 * oneday); 

    ret = this.collection.group({ 
      // Group by this key. One document per unique datestamp is returned. 
      key: "datestamp" 
      // Seed document for each group in result array. 
     , initial: { "createdAt": 0 } 
      // Update seed document if more recent document found. 
     , reduce: function(doc, prev) { 
       if (doc.createdAt > prev.createdAt) { 
       prev.createdAt = doc.createdAt; 
       prev.score = doc.score; 

       // Add other fields, if desired: 
       prev.name = doc.name; 
       } 
      // Process only documents created within past seven days 
     , condition: { "createdAt" : {"$gt": oneweekago} } 
     }}); 

    return ret.retval; 

    // Note ret, the result of group() has other useful fields like: 
    // total "count" of documents, 
    // number of unique "keys", 
    // and "ok" is false if a problem occurred during group() 

); 
+0

Thanx爲這本書,偉大的閱讀。但是,我一直使用你的例子得到'ret' undefined錯誤,發現貓鼬根據[這篇文章]做了一個不同的方式(http://wmilesn.com/2011/07/code/how-to-map-reduce -with-mongoose-mongodb-express-node-js /)(對於任何正在尋找答案的人) –

0

將另一個屬性添加到名爲dateAdded或其他的模式。

schema.statics.getLastWeek = function(name, fn) { 
var oneday = 60 * 60 * 24 
    , now = Date.now() 
    , docs = [] 

for (var i = 0; i < 7; i++) { 
    this.where('name', new RegExp(name, 'i')) 
    .where('createdAt') 
    .lt(now - (i * oneday)) 
    .gte(now - ((i + 1) * oneday)) 
    .desc('createdAt') 
    .findOne(function(err,doc){ 
    // might not always find one 
    docs.push(doc) 
    }) 
} 
return fn(null, docs) 
} 
+0

我想類似的東西,但沒有工作,見上面 –

+0

我的編輯噢,對不起。忘了添加'lt'條款。而且你還應該編輯你在'Date.now'函數中傳遞的模式默認爲'createdAt'。你現在正在做的是傳遞'Date.now()'的結果,這將使你的所有文檔具有相同的'createdAt'字段。 – fent

+0

這不起作用,它仍然返回相同的記錄7次,它甚至不是當天的最新記錄... –

1

一個解決方案是使用group()按日對記錄進行分組。它很花哨,很慢並且可以阻塞(意味着沒有其他東西可以同時運行),但是如果你的記錄集不是太大,那麼它非常強大。

組:http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

至於貓鼬,我不知道,如果它()直接支持組,但你可以使用節點,MongoDB的本地實施後,做這樣的事情(僞代碼,大多數情況下):羣體之間和

schema.statics.getLastWeek = function(name, cb) { 
    var keys = {} // can't remember what this is for 
    var condition = {} // maybe restrict to last 7 days 
    var initial = {day1:[],day2:[],day3:[],day4:[],day5:[],day6:[],day7:[]} 
    var reduce = function(obj, prev) { 
     // prev is basically the same as initial (except with whatever is added) 
     var day = obj.date.slice(0,-10) // figure out day, however it works 
     prev["day" + day].push(obj) // create grouped arrays 
     // you could also do something here to sort by _id 
     // which is probably also going to get you the latest for that day 
     // and use it to replace the last item in the prev["day" + 1] array if 
     // it's > that the previous _id, which could simplify things later 
    } 
    this.collection.group(keys, condition, initial, reduce, function(err, results) { 
     // console.log(results) 
     var days = results // it may be a property, can't remember 
     var lastDays = {} 
     days.forEach(function(day) { 
      // sort each day array and grab last element 

      lastDays[day] = days[day].sort(function(a, b) { 
      return a.date - b.date // check sort syntax, you may need a diff sort function if it's a string 
      }).slice(-1) // i think that will give you the last one 
     }) 
     cb(lastDays) // your stuff 
    }) 
} 

一些更多的比較映射從我的博客減少: http://j-query.blogspot.com/2011/06/mongodb-performance-group-vs-find-vs.html

沒有關於在本地驅動程序組命令的文檔,所以你必須同行通過源代碼在這裏: https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js

也爲排序,檢查檢查https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort的確切語法

編輯:更好的主意!

只需要一個名爲「lastRequestOfDay」的特殊集合並在當天生成_id。 用每個新請求覆蓋值。這將是超級簡單的查詢和快速寫入,並將始終具有每天寫的最後一個值!

0

嘗試是這樣的:

schema.statics.getLastWeek = function(name, fn) { 
    var oneday = 60 * 60 * 24 
    , now = Date.now() 
    , docs = [] 
    , that = this 

    function getDay(day){ 
    that.where('name', new RegExp(name, 'i')) 
    .where('createdAt') 
    .gte(now - (day * oneday)) 
    .desc('createdAt') 
    .findOne(function(err,doc){ 
    docs.push(doc) 
    }) 

    } 

    for (var i = 1; i <= 7; i++) { 
    getDay(i); 
    } 
} 
0

似乎沒有人試圖爲「關閉到午夜「。 :)我用原始代碼看到的問題是,它檢查的時間大於或等於x天前......這將始終返回最近的時間。不過,我很困惑,爲什麼DeaDEnD的解決方案會返回7次相同的記錄。另外,你從來沒有打過fn,但那不是你最擔心的問題,是嗎?

嘗試增加對.lt(now - (now % oneday) - (i - 1) * oneday)(假設0索引;如果是1索引,改變說i - 2