MongoDB 聚合管道:在不知道字段值的情况下按字段对数组元素进行分组
MongoDB Aggregation pipeline: group array elements by a field without knowing the field value
我有一组这样形状的足球比赛:
{
_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 上写什么当我在 运行 查询之前不认识团队时的情况。
我相信我明白你在找什么,但如果这是错误的,请告诉我。
正如您所说,您应该能够为此使用聚合管道,包括 $match、$avg 和 $group。
您可以查看 live demo of the query here。
数据库
如果我们有如下数据库结构...
[
{
_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
}
]
查询 1
- 放松玩家
- 匹配只保留 2 个玩家的名字
- 按
match_id
team
分组并将距离推入数组
- 匹配并仅保留大小为 =2 的数组
(这是关键,只有当这些球员在同一场比赛和同一支球队中出场时,尺寸=2)
(如果您在示例代码中看到 id=2,id=3 将被忽略,因为这些球员从未在同一场比赛和同一支球队中打过比赛)
- uwnind 距离(这里我们知道所有这些都是有效的)
- 按
null
分组(所有集合1组)并平均距离
(你不关心什么比赛,什么球队或哪个球员,所以只保留有关 avgDistance 的信息)
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"]}])
查询 2
- local 它使用 reduce,以及 1 名球员在 1 场比赛中,最多 1 次和 1 个团队中最多的事实(即使这不是真的,query1 也有效)
- 减少以保持这两个距离,仅当团队相同且名称是两者之一时才添加距离数组
- 再次只保持距离 count=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"]}])
我有一组这样形状的足球比赛:
{
_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 上写什么当我在 运行 查询之前不认识团队时的情况。
我相信我明白你在找什么,但如果这是错误的,请告诉我。
正如您所说,您应该能够为此使用聚合管道,包括 $match、$avg 和 $group。
您可以查看 live demo of the query here。
数据库
如果我们有如下数据库结构...
[
{
_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
}
]
查询 1
- 放松玩家
- 匹配只保留 2 个玩家的名字
- 按
match_id
team
分组并将距离推入数组 - 匹配并仅保留大小为 =2 的数组
(这是关键,只有当这些球员在同一场比赛和同一支球队中出场时,尺寸=2)
(如果您在示例代码中看到 id=2,id=3 将被忽略,因为这些球员从未在同一场比赛和同一支球队中打过比赛) - uwnind 距离(这里我们知道所有这些都是有效的)
- 按
null
分组(所有集合1组)并平均距离 (你不关心什么比赛,什么球队或哪个球员,所以只保留有关 avgDistance 的信息)
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"]}])
查询 2
- local 它使用 reduce,以及 1 名球员在 1 场比赛中,最多 1 次和 1 个团队中最多的事实(即使这不是真的,query1 也有效)
- 减少以保持这两个距离,仅当团队相同且名称是两者之一时才添加距离数组
- 再次只保持距离 count=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"]}])