2016-09-13 9 views
1

私は家族のために大学のサッカーの賭けのアプリを作成しています。ここ は私のスキーマです:MongoosJS:派生/計算された値の最適なアプローチ

const GameSchema = new mongoose.Schema({ 
    home: { 
     type: String, 
     required: true 
    }, 
    opponent: { 
     type: String, 
     required: true 
    }, 
    homeScore: Number, 
    opponentScore: Number, 
    week:{ 
     type: Number, 
     required: true 
    }, 
    winner: String, 
    userPicks: [ 
     { 
      user: { 
       type: mongoose.Schema.Types.ObjectId, 
       ref: 'User' 
      }, 
      choosenTeam: String 
     } 
    ] 
}); 

const UserSchema = new mongoose.Schema({ 
    name: String 
}); 

私は、各ユーザーの週間スコアを計算できるようにする必要がある(すなわち、それらは毎週正しく予測するサッカーゲームの数)とその累積スコア(各ユーザーが予測したゲーム数、すなわち、

私はMongoDBとMongooseにはまだまだ新しいので、この問題の対処方法は不明です。ゲームのドキュメントは200レコードを超えることはないので、両方のスコアはデータベースに格納されているデータから導出または計算する必要があります。ここで

は、私がこれまで考えてきた可能な解決策です:

  • は、両方の得点仮想属性ではなく、これは、複数のユーザー
  • のために働くだろうかわから文書に属性を永続作るが、週のゲームの結果がデータベースに保存されるときに、ミドルウェアを使用してスコアを再計算します。
  • 静的メソッドを使用してスコアを計算します。

アドバイスをいただければ幸いです。

+0

こんにちは、機械学習のこの部分ですか?どのようにホームスコアを計算したいですか?それは前の得点からの増分ですか? – vdj4y

答えて

1

集計フレームワークを使用して集計を計算することができます。これは、Map/Reduceの一般的な集約操作のためのより速い代替手段です。 MongoDBでは、パイプラインは、データレコードを処理して計算結果を返すためにコレクションに適用される一連の特殊演算子で構成されます。集計操作では、複数の文書の値をグループ化し、グループ化されたデータに対してさまざまな操作を実行して単一の結果を返すことができます。詳細は、documentationをご覧ください。

は、所望の結果を取得するために、次のパイプラインを実行して検討して上記パイプラインで

var pipeline = [ 
    { "$unwind": "$userPicks" }, 
    { 
     "$group": { 
      "_id": { 
       "week": "$week", 
       "user": "$userPicks.user" 
      }, 
      "weeklyScore": { 
       "$sum": { 
        "$cond": [ 
         { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
         1, 0 
        ] 
       } 
      }   
     } 
    }, 
    { 
     "$group": { 
      "_id": "$_id.user", 
      "weeklyScores": { 
       "$push": { 
        "week": "$_id.week", 
        "score": "$weeklyScore" 
       } 
      }, 
      "totalScores": { "$sum": "$weeklyScore" } 
     } 
    } 
]; 

Game.aggregate(pipeline, function(err, results){ 
    User.populate(results, { "path": "_id" }, function(err, results) { 
     if (err) throw err; 
     console.log(JSON.stringify(results, undefined, 4)); 
    }); 
}) 

を、最初のステップは、非常に来る$unwindオペレータ

{ "$unwind": "$userPicks" } 

ありますデータが配列として格納されるときに便利です。アンワインド演算子がリストデータフィールドに適用されると、アンワインドが適用されるリストデータフィールドのすべての要素ごとに新しいレコードが生成されます。基本的にデータを平坦化します。

これは、次のパイプラインのステージのために必要な操作、$groupパイプライン演算子は、に似ている、あなたのグループフィールドweek"userPicks.user"

{ 
     "$group": { 
      "_id": { 
       "week": "$week", 
       "user": "$userPicks.user" 
      }, 
      "weeklyScore": { 
       "$sum": { 
        "$cond": [ 
         { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
         1, 0 
        ] 
       } 
      }   
     } 
    } 

により平坦化文書$groupステップですSQLのGROUP BY節。 SQLでは、集約関数を使用しない限り、GROUP BYは使用できません。同じように、MongoDBでも集約関数を使用する必要があります。集計関数hereの詳細を読むことができます。それは最初の引数だとして、この$group動作で

は、(彼らは毎週正しく予測するサッカーの試合の数IE)各ユーザの毎週のスコアを計算するためのロジックは、三項演算論理条件をとる$condを介して行われます(if)を返し、評価が真(then)である第2引数または第3引数(false)を返します。

"$cond": [ 
    { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
    1, 0 
] 

ので、文書内、"$userPicks.chosenTeam"フィールドは"$winner"フィールドと同じで処理されている場合$condオペレータフィード:これは、1と0に真/偽を返すが、それぞれ$sumにフィードすることができますそれ以外の場合は値1を加算してゼロ値を加算します。

第二群のパイプライン:

{ 
    "$group": { 
     "_id": "$user", 
     "weeklyScores": { 
      "$push": { 
       "week": "$_id.week", 
       "score": "$weeklyScore" 
      } 
     }, 
     "totalScores": { "$sum": "$weeklyScore" } 
    } 
} 

は、前のパイプラインからの文書を取得し、グループにそれらをさらにuserフィールドによって、および$sumアキュムレータ演算子を使用して、別の集約すなわち合計スコアを算出します。同じパイプライン内で、$push演算子を使用すると、各グループの式の値の配列を返すことで、週単位のスコアのリストを集計できます。

パイプラインを実行するときに、MongoDBは演算子をパイプでパイプすることに注意してください。ここで "Pipe"はLinuxの意味をとります。演算子の出力は、次の演算子の入力になります。各演算子の結果は、新しい文書集合です。 Mongoのは、上記のパイプラインを実行して、次のように:

collection | $unwind | $group | $group => result 

を今、あなたはマングースにおけるアグリゲーションパイプラインを実行すると、結果がユーザーIDです_idキーを持つことになりますし、あなたがこの分野、すなわち上の結果を移入する必要がありますMongooseはユーザーコレクションに対して「結合」を実行し、ユーザースキーマを持つ文書を結果に返します。パイプラインを理解することを支援するか、またはそれをデバッグする

側の注意点として

は、あなただけの最初のパイプライン演算子と集約を実行し、予期しない結果を取得する必要があります。たとえば、としてのmongoシェルで集約を実行します。

db.games.aggregate([ 
    { "$unwind": "$userPicks" } 
]) 

userPicks配列が適切に解体されたかどうかを確認するために、結果を確認してください。それが期待される結果を与える場合は、次を追加します。

db.games.aggregate([ 
    { "$unwind": "$userPicks" }, 
    { 
     "$group": { 
      "_id": { 
       "week": "$week", 
       "user": "$userPicks.user" 
      }, 
      "weeklyScore": { 
       "$sum": { 
        "$cond": [ 
         { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
         1, 0 
        ] 
       } 
      }   
     } 
    } 
]) 

あなたが最後のパイプラインステップに得るまでの手順を繰り返します。

+0

私はまだMongoDB/Mongoose初心者です。これが何をしているのかを教えてください。 –

+0

@SierraGreggいくつかの説明を追加しました – chridam

+1

ありがとうございました!集約を使うことは、他のアイデアよりもはるかにクリーンです –

関連する問題