我有一系列足球比赛,形状是这样的:
{
_id: ObjectId("SomeUniqueMatchID"),
players: [
{ team: "Arsenal", name: "Saka", distanceRan: 8590},
{ team: "Arsenal", name: "Aubameyang", distanceRan: 9230}
// ...remaining players for both teams
],
timestamp: 129380193,
// other match info
}
我想查询奥巴梅扬和萨卡在一起比赛时的平均距离,无论他们在哪支球队,只要他们在同一支球队比赛。
我了解球员场上的$unwind
,然后是$match
,最后使用$group
来计算平均值,但在运行查询之前,当我不知道球队时,我不知道在$match条件上写什么。
Query1
- 放松玩家
- 比赛只保留两名球员的名字
- 按
match_id
team
分组并按数组推送距离 - 只匹配并保留大小为2的数组(这是关键,size=2,仅当这些球员同时参加了同一场比赛和同一支球队时(
(如果您在示例中看到代码id=2,id=3将被忽略,因为这些球员从未同时参加过同一场赛和同一个球队( - uwnind距离(这里我们知道所有这些都是有效的距离(
- 按
null
分组(所有集合1组(并平均距离(你不在乎哪场比赛、哪支球队或哪名球员,所以只保留关于平均距离的信息(
此处测试代码
aggregate(
[{"$unwind": {"path": "$players"}},
{"$match":
{"$expr":
{"$or":
[{"$eq": ["$players.name", "Saka"]},
{"$eq": ["$players.name", "Aubameyang"]}]}}},
{"$group":
{"_id": {"_id": "$_id", "team": "$players.team"},
"distances": {"$push": "$players.distanceRan"}}},
{"$match": {"$expr": {"$eq": [{"$size": "$distances"}, 2]}}},
{"$unwind": {"path": "$distances"}},
{"$group": {"_id": null, "avgDistanceRan": {"$avg": "$distances"}}},
{"$unset": ["_id"]}])
Query2
- local它使用reduce,以及1场比赛中有1名球员,最多1次,最多1个团队(query1有效,即使这不是真的(
- 为了保持这两个距离,我们只在团队相同且名称为二者之一的情况下添加距离数组
- 再次只保持距离计数=2(以确保同一球队的同一场比赛中的两名球员(
- 展开并按null分组,平均值如上
此处测试代码
aggregate(
[{"$set":
{"players":
{"$reduce":
{"input": "$players",
"initialValue": {"team": null, "distances": []},
"in":
{"$cond":
[{"$and":
[{"$or":
[{"$eq": ["$$this.name", "Saka"]},
{"$eq": ["$$this.name", "Aubameyang"]}]},
{"$or":
[{"$eq": ["$$value.team", null]},
{"$eq": ["$$value.team", "$$this.team"]}]}]},
{"team": "$$this.team",
"distances":
{"$concatArrays":
["$$value.distances", ["$$this.distanceRan"]]}},
"$$value"]}}}}},
{"$match": {"$expr": {"$eq": [{"$size": "$players.distances"}, 2]}}},
{"$unwind": {"path": "$players.distances"}},
{"$group":
{"_id": null, "avgdistanceRan": {"$avg": "$players.distances"}}},
{"$unset": ["_id"]}])
我相信我理解你在寻找什么,但如果这是错误的,请告诉我。
正如您所说,您应该能够使用$match、$avg和$group的聚合管道。
您可以在这里查看查询的实时演示。
数据库
如果我们有以下数据库结构。。。
[
{
_id: ObjectId("123456789101819202122232"),
players: [
{
team: "Arsenal",
name: "Saka",
distanceRan: 8590
},
{
team: "Arsenal",
name: "Aubameyang",
distanceRan: 9230
}
],
timestamp: 129380193,
},
{
_id: ObjectId("123456789101819202999999"),
players: [
{
team: "Arsenal",
name: "Saka",
distanceRan: 7777
},
{
team: "NotArsenal",
name: "Aubameyang",
distanceRan: 9999
}
],
timestamp: 129399999,
}
]
查询
我们可以使用以下查询来计算平均值。
db.collection.aggregate([
{
$unwind: "$players"
},
{
$match: {
$or: [
{
"players.name": "Saka",
"players.team": "Arsenal"
},
{
"players.name": "Aubameyang",
"players.team": "Arsenal"
}
]
}
},
{
$group: {
_id: "$_id",
averageDistanceRan: {
$avg: "$players.distanceRan"
},
"players": {
$push: "$players"
}
}
},
{
"$match": {
"$expr": {
"$eq": [
{
"$size": "$players"
},
2
]
}
}
},
{
$project: {
_id: "$_id",
averageDistanceRan: 1
}
}
])
结果
这将给我们…
[
{
"_id": ObjectId("123456789101819202122232"),
"averageDistanceRan": 8910
}
]