2016-09-13 25 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操作,計算每個用戶每週分數(即它們每星期正確預測足球比賽的數量)的邏輯通過三元運算符$cond,需要一個邏輯條件進行,因爲它是第一個參數(if),然後返回評估爲true的第二個參數(then)或第三個參數爲false(else)。這使得真/假返回到1和0至饋送到$sum分別爲:

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

所以,如果在文檔中被處理的"$userPicks.chosenTeam"場是一樣的"$winner"領域,$cond操作者進料總和值1總計爲零值。

第二組管道:

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

從以前的管道,並將它們進一步組由user字段取的文件,並計算另一骨料即總分,使用$sum累加器運算符。在同一管道中,您可以使用運算符將​​每週得分列表聚合,該運算符將爲每個組返回一組表達式值數組。

在這裏需要注意的一件事是執行一個管道時,MongoDB將管道操作符互相管道。這裏的「管道」採用Linux的含義:操作員的輸出成爲後面的操作員的輸入。每個操作員的結果都是一個新的文檔集合。所以蒙戈執行上述的管道如下:

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

現在,當您運行貓鼬聚合管道,其結果將有_id關鍵是用戶ID,您需要填寫此字段即結果Mongoose將對用戶集合執行「加入」,並返回結果中包含用戶模式的文檔。爲了幫助理解流水線或調試它,如果您得到意想不到的結果,請使用第一個流水線運算符運行聚合。例如,在運行蒙戈聚集殼牌:

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

謝謝!使用聚合比我的其他任何想法都要乾淨得多 –

相關問題