2011-08-16 9 views
6

*基本的には、過去1時間のスコアでオブジェクトを注文しようとしています。MongoDB MapReduceのアップデート方法

私は自分のデータベース内のオブジェクトの時間単位の合計を生成しようとしています。各オブジェクトには投票が埋め込まれています。

{ 
    _id: ObjectId 
    score: int 
    hourly-score: int <- need to update this value so I can order by it 
    recently-voted: boolean 
    votes: { 
     "4e4634821dff6f103c040000": { <- Key is __toString of voter ObjectId 
      "_id": ObjectId("4e4634821dff6f103c040000"), <- Voter ObjectId 
      "a": 1, <- Vote amount 
      "ca": ISODate("2011-08-16T00:01:34.975Z"), <- Created at MongoDate 
      "ts": 1313452894 <- Created at timestamp 
     }, 
     ... repeat ... 
    } 
} 

この質問は実際に私は数日前Best way to model a voting system in MongoDB

どのように私は(私はできますか?)次の操作を実行するためにMapReduceのコマンドを実行します尋ねた質問に関連している:オブジェクト・スキーマは次のようになります:

  1. のみ過去1時間に作成された票の合計を計算し、最近投票= trueまたは時間ごとのスコア> 0
  2. を持つオブジェクト上で実行します。
  3. hourly-score =上記で計算された合計と最近投票された= falseを更新します。

私もdb.getMongo()を実行して、スレーブDB上のMapReduceを実行することができるhereを読み取る。setSlaveOk()M/Rコマンドの前に。スレーブでreduceを実行してマスターDBを更新できますか?

Mongo MapReduceではインプレース更新が可能ですか?

答えて

10

これは間違いなく可能です。

1. マップフェーズに渡されるオブジェクトのセットをフィルタリングするmap-reduceとともにクエリを指定できます。 mongoシェルでは、これは(mを仮定し、rはそれぞれ、あなたのマッパーと減速関数の名前です)のようになります。

> db.coll.mapReduce(m, r, {query: {$or: [{"recently-voted": true}, {"hourly-score": {$gt: 0}}]}}) 

2. ステップ#1はあなたが持つすべての文書にあなたのマッパーを使用できるようになります最後の1時間に少なくとも1つの投票(またはrecently-votedがtrueに設定されています)。ただし、すべての投票が最後の1時間に行われたわけではありません。つまり、あなたのマッパーでリストをフィルタリングする必要がある、とだけあなたがカウントしたいそれらの票を発します:

function m() { 
    var hour_ago = new Date() - 3600000; 
    this.votes.forEach(function (vote) { 
    if (vote.ts > hour_ago) { 
     emit(/* your key */, this.vote.a); 
    } 
    }); 
} 

そして減らすために:

function r(key, values) { 
    var sum = 0; 
    values.forEach(function(value) { sum += value; }); 
    return sum; 
} 

3. を時間ごとのスコア表を更新するには出力された値と出力された値(存在する場合)の両方で減速機を呼び出すmap-reduceにreduceOutputオプションを使用することができます。そのパスの結果は出力コレクションに保存されます。これは次のようになります。再減少出力に加えて

> db.coll.mapReduce(m, r, {query: ..., out: {reduce: "output_coll"}}) 

、あなたは新しく作成されたもので、出力コレクション内の文書を上書きしますどのmergeを使用する(ただし、作成した_id Sとは異なる_idで任意の文書を残すことができ実質的にドロップアンド作成操作でデフォルトであるreplace)を使用するか、結果を直接シェルまたはドライバに返す{inline: 1}を使用してください。 {inline: 1}を使用する場合、結果は1つのドキュメントに許容されるサイズ(最近のMongoDBリリースでは16MB)に収まる必要があります。

(4.) セカンダリ(「スレーブ」)でmap-reduceジョブを実行できますが、セカンダリは書き込みを受け付けない(セカンダリにする)ので、インライン出力を使用するときにのみ行うことができます。

+1

手順3まではすべてが意味があります。私は、時間単位の合計を減らしてコレクション内の関連オブジェクトを更新する方法を理解していません。私の質問にスキーマのコレクションのコメントがあり、関連するすべてのコメントの時間別スコアを新しい縮小合計に更新したいとします。 Hourly-scoreはコメントオブジェクトのパラメータであり、別のコレクションのものではありません。どうすればいい? – Marc

+0

あなたは 'hourly-score'フィールドを投票量の合計(fields' votes.a')とmap-reduceの出力に更新しますか? map-reduceを使ってこれを行うことはできませんが、間違いなくmap-reduceの出力を使用して任意のコレクションを更新する後処理ステップを実行できます。 – dcrosta

+0

Hmm ok。リレーショナルデータベースからは、これはかなり簡単な作業です。基本的に私がしたいのは、最後の1時間にオブジェクトを点数順に並べ替えることです。リレーショナルでは、スコアを別のテーブルに保存します。次に、オブジェクトテーブルから選択し、スコア表に参加し、過去1時間のスコアとその合計で順位を合計します。おそらくどちらか。モンゴーはこれだけではうまくいきません。私は私のオブジェクト/投票を間違って整理していますか? – Marc