2011-12-29 2 views
2

今日の現在のスコアを維持しながら、数日間の成長を追跡するために、1時間ごとにスキーマにエントリを追加しています。今、私は過去1週間の毎日の最新のレコードを取得できるようにしたいと思います。結果は、6日前または6日前の真夜中または6日前の記録であり、7日目は最新のものです。ここで最後の7日間の今日の最後の文書を見つけよう

は私のスキーマです:

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

編集

私は、この静的を使用してみましたが、私がいた場合、それはまったく同じレコードを7回

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日間の終値を求めています。これは、グループを使用して簡単に達成することができます 。

私はモンゴースに慣れていませんが、私はあなたの事例を以下のケースに適用しようとしました。私は、関数に値から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'未定義のエラーを取得し続けていると、それはmongooseが[この記事](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

ああごめん上の私の編集を参照してください。(それは1インデックスだ場合、i - 2を言うためにそれを変更0インデックスを仮定して).lt(now - (now % oneday) - (i - 1) * oneday)に追加

してみてください。 'lt'句を追加するのを忘れました。また、 'Date.now'関数で渡されたスキーマを' createdAt'のデフォルトに編集する必要があります。あなたが現在行っていることは、 'Date.now()'の結果を渡して、すべての文書が同じ 'createdAt'フィールドを持つようにすることです。 – fent

+0

これは動作しません、それはまだ同じレコードを7回返します。そしてそれはその日の最新の記録でもありません。 –

1

解決策は、group()を使用してレコードを1日ごとにグループ化することです。それは空想的で、遅く、ブロックすることができます(同時に何も実行できないことを意味します)。しかし、あなたのレコードセットがあまりにも大きくない場合、それはかなり強力です。

グループ:マングースについてはhttp://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

、私が直接)(それがグループをサポートしているかどうかわからないんだけど、あなたはこのような何か(擬似コードを実行して、ノードMongoDBのネイティブ実装を使用することができます主に):あなたはピアする必要がありますので、 http://j-query.blogspot.com/2011/06/mongodb-performance-group-vs-find-vs.html

は、ネイティブドライバでgroupコマンドについてのドキュメントはありません。

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 
    }) 
} 

いくつかのより多くのグループ間の比較や私のブログから減らすマップここでソースコードを:ソート用も 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という名前は一度も呼ばれませんでしたが、それはあなたの懸念事項の中で最大のものではありません。

関連する問題