gpt4 book ai didi

mongodb - 聚合两个数组的结果

转载 作者:可可西里 更新时间:2023-11-01 09:35:20 26 4
gpt4 key购买 nike

我对MongoDB还不太熟悉,我正在尝试对“matches”集合进行一些统计,如下所示:

{
team1: {
players: ["player1", "player2"],
score: 10
},
team2: {
players: ["player3", "player4"],
score: 5
}
},
{
team1: {
players: ["player1", "player3"],
score: 15
},
team2: {
players: ["player2", "player4"],
score: 21
}
},
{
team1: {
players: ["player4", "player1"],
score: 21
},
team2: {
players: ["player3", "player2"],
score: 9
}
},
{
team1: {
players: ["player1"],
score: 5
},
team2: {
players: ["player3"],
score: 10
}
}

我希望每个玩家都能得到游戏的赢、输和赢/输比率。我是新来的聚合函数和有困难的事情进行。有人能告诉我正确的方向吗?

最佳答案

处理一个结构中的多个数组并不是一个简单的聚合任务,特别是当您的结果确实希望考虑这两个数组的组合时。
幸运的是,这里有一些操作和/或技术可以提供帮助,还有一个事实,即每场比赛都包含一组每个队/比赛和结果的独特球员。
最简化的方法是使用MongoDB 2.6及以上的特性,将数组有效地“组合”为一个数组进行处理:

db.league.aggregate([
{ "$project": {
"players": {
"$concatArrays": [
{ "$map": {
"input": "$team1.players",
"as": "el",
"in": {
"player": "$$el",
"win": {
"$cond": {
"if": { "$gt": [ "$team1.score", "$team2.score" ] },
"then": 1,
"else": 0
}
},
"loss": {
"$cond": {
"if": { "$lt": [ "$team1.score", "$team2.score" ] },
"then": 1,
"else": 0
}
}
}
}},
{ "$map": {
"input": "$team2.players",
"as": "el",
"in": {
"player": "$$el",
"win": {
"$cond": {
"if": { "$gt": [ "$team2.score", "$team1.score" ] },
"then": 1,
"else": 0
}
},
"loss": {
"$cond": {
"if": { "$lt": [ "$team2.score", "$team1.score" ] },
"then": 1,
"else": 0
}
}
}
}}
]
}
}},
{ "$unwind": "$players" },
{ "$group": {
"_id": "$players.player",
"win": { "$sum": "$players.win" },
"loss": { "$sum": "$players.loss" }
}},
{ "$project": {
"win": 1,
"loss": 1,
"ratio": { "$divide": [ "$win", "$loss" ] }
}},
{ "$sort": { "_id": 1 } }
])

此列表使用的是MongoDB 3.2中的 $concatArrays,但考虑到每个游戏的玩家列表是“唯一的”,因此是一个“集合”,因此Acutal运算符也可以很容易地替换为 $setUnion。两个运算符基本上都是基于内部操作的输出将一个数组与另一个数组连接起来。
对于这些内部操作,我们使用 $map,它对每个数组(“team1/team2”)进行在线处理,并为每个玩家计算游戏结果是否为“赢/输”。这使得接下来的阶段更容易。
尽管MongoDB的3.2版和2.6版都引入了运算符,使处理数组变得更容易,但一般的原则是,如果要“聚合”数组中的数据,则首先处理 $unwind。这将公开之前映射的每个游戏中的每个“玩家”数据。
现在,只需要使用 $group来汇集每个玩家的结果,而 $sum用于每个总字段。为了得到总结果的“比率”,使用 $project处理以在结果值之间引入 $divide,然后选择 $sort每个播放器的结果键。
旧的解决方案
在MongoDB 2.6之前,处理数组的唯一工具是 $unwind。所以同样的原则在这里起作用:
用“赢/输”来“映射”每个数组。
将每场游戏的内容合并成一个“独立列表”
基于通用“player”字段的内容求和
唯一真正不同的方法是,我们将在这里看到的每个游戏的“不同列表”将是“在”分离映射数组之后,而不是只返回每个“游戏/玩家”组合的一个文档:
db.league.aggregate([
{ "$unwind": "$team1.players" },
{ "$group": {
"_id": "$_id",
"team1": {
"$push": {
"player": "$team1.players",
"win": {
"$cond": [
{ "$gt": [ "$team1.score", "$team2.score" ] },
1,
0
]
},
"loss": {
"$cond": [
{ "$lt": [ "$team1.score", "$team2.score" ] },
1,
0
]
}
}
},
"team1Score": { "$first": "$team1.score" },
"team2": { "$first": "$team2" }
}},
{ "$unwind": "$team2.players" },
{ "$group": {
"_id": "$_id",
"team1": { "$first": "$team1" },
"team2": {
"$push": {
"player": "$team2.players",
"win": {
"$cond": [
{ "$gt": [ "$team2.score", "$team1Score" ] },
1,
0
]
},
"loss": {
"$cond": [
{ "$lt": [ "$team2.score", "$team1Score" ] },
1,
0
]
}
}
},
"type": { "$first": { "$const": ["A","B" ] } }
}},
{ "$unwind": "$team1" },
{ "$unwind": "$team2" },
{ "$unwind": "$type" },
{ "$group": {
"_id": {
"_id": "$_id",
"player": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1.player",
"$team2.player"
]
},
"win": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1.win",
"$team2.win"
]
},
"loss": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1.loss",
"$team2.loss"
]
}
}
}},
{ "$group": {
"_id": "$_id.player",
"win": { "$sum": "$_id.win" },
"loss": { "$sum": "$_id.loss" }
}},
{ "$project": {
"win": 1,
"loss": 1,
"ratio": { "$divide": [ "$win", "$loss" ] }
}},
{ "$sort": { "_id": 1 } }
])

所以这是有趣的一部分:
    { "$group": {
"_id": {
"_id": "$_id",
"player": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1.player",
"$team2.player"
]
},
"win": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1.win",
"$team2.win"
]
},
"loss": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1.loss",
"$team2.loss"
]
}
}
}},

这基本上消除了每个游戏在不同数组上每个 $unwind产生的任何重复。因为当您 $unwind一个数组时,您将得到每个数组成员的整个文档的副本。如果您随后 $unwind另一个包含的数组,那么您刚刚“解绕”的内容也将为这些数组成员中的每个成员再次“复制”。
幸运的是,这是好的,因为任何球员只列出一次每场比赛,所以每个球员只有一套结果每场比赛。另一种写入阶段的方法是使用 $addToSet处理到另一个数组中:
    { "$group": {
"_id": "$_id",
"players": {
"$addToSet": {
"$cond": [
{ "$eq": [ "$type", "A" ] },
"$team1",
"$team2"
]
}
}
}},
{ "$unwind": "$players" }

但由于这会产生另一个“数组”,所以更希望将结果作为单独的文档保存,而不是再次使用 $unwind进行处理。
所以这实际上是“将结果连接到一个单独的列表中”,在这种情况下,由于我们缺少将“team1”和“team2”同时“连接”在一起的运算符,数组被分离,然后根据正在处理的当前“a”或“b”值有条件地“组合”。
最后的“加入”会查看许多数据的“副本”,但对于每个参与的玩家来说,基本上仍然只有“每场比赛一个不同的玩家记录”,而且由于我们在“复制”发生之前就计算出了数字,所以实际上只需要先从每场比赛中选择其中一个。
相同的最终结果,由他们总结每个球员和计算的总和。
结论
因此,您通常可以在这里得出这样的结论:在这两种情况下,所涉及的大部分工作都是为了将这两个数据数组转换成一个数组,或者实际上是将每个游戏玩家的单个文档转换成一个数组,以便实现对总数的简单聚合。
考虑到需要从这些数据源汇总汇总,您可能会认为“that”可能是比当前格式更好的数据结构。
注意: $const操作符没有文档记录,但是自从MongoDB 2.2引入聚合框架后就已经存在了。它提供的功能与 $literal(在mongodb 2.6中引入的)完全相同,实际上在代码库中是“完全”相同的,新的定义只是指向旧的定义。
它在这里的列表中使用,因为预期的MongoDB目标(2.6之前的版本)不会有 $literal,而另一个列表更适合MongoDB 2.6及更高版本。当然要应用 $setUnion

关于mongodb - 聚合两个数组的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35541127/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com