我正在为我的家人创建一个大学足球博彩应用程序。 这是我的模式:
await
我需要能够计算每个用户的每周得分(即他们每周正确预测的足球比赛的数量)和他们的累积得分(即每个用户整体正确预测的比赛数量)
我仍然是MongoDB和Mongoose的新手,所以我不确定如何处理这个问题。由于游戏文档永远不会超过200条记录,我认为这两个分数应该从存储在数据库中的数据中导出或计算出来。
以下是我到目前为止所考虑的可能解决方案:
任何建议都将受到赞赏。
答案 0 :(得分: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"
<对展平的文档进行分组/ p>
{
"$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),然后返回第二个参数,其中评估为真(然后)或第三个参数,其中为false(否则) )。这使得返回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
累加器运算符计算另一个聚合,即总分。在同一个管道中,您可以使用 $push
运算符汇总每周分数列表,该运算符会为每个组返回一组表达式值。
这里需要注意的一点是,在执行管道时,MongoDB会将运营商相互管道化。 &#34;管&#34;这里采用Linux的含义:运算符的输出成为以下运算符的输入。每个运算符的结果是一个新的文档集合。所以Mongo按如下方式执行上述管道:
collection | $unwind | $group | $group => result
现在,当您在Mongoose中运行聚合管道时,结果将有一个_id
键,这是用户ID,您需要在此字段中填充结果,即Mongoose将执行&#34; join& #34;在用户集合上,并在结果中使用用户架构返回文档。
作为旁注,为了帮助理解管道或调试它,如果您得到意外结果,只需使用第一个管道运算符运行聚合。例如,在mongo shell中运行聚合为:
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
]
}
}
}
}
])
重复这些步骤,直到进入最后的管道步骤。