I have a documents that looks like this:
_id: ObjectId('...')
name: Team A
players: [
{
name: Michael
position: Goalkeeper
},
{
name: Frank
position: Defender
},
{
name: Mike
position: Goalkeeper
}
]
I want to make an aggregation that simply returns a list of Goalkeeper names, like this..
Players: [
Michael,
Mike
]
This is what I have so far:
{
$match: { name: "Team A" }
},
{
$project: {
players: {
$filter: {
input: "$players",
cond: {$eq: ["$$this.position", "Goalkeeper"]}
}
},
_id: 0
}
}
This filters the players Array so it only contains goalkeepers. Now I need to somehow take the names of each object out and store it in another Array. How do I achieve that?
CodePudding user response:
Use $reduce
to iterate through the array and use $cond
to conditionally $setUnion
the entry into the accumulator.
db.collection.aggregate([
{
$project: {
players: {
"$reduce": {
"input": "$players",
"initialValue": [],
"in": {
"$cond": {
"if": {
$eq: [
"$$this.position",
"Goalkeeper"
]
},
"then": {
"$setUnion": [
"$$value",
[
"$$this.name"
]
]
},
"else": "$$value"
}
}
}
}
}
}
])
CodePudding user response:
You can use also $reduce/$filter as follow:
db.collection.aggregate([
{
$match: {
name: "Team A"
}
},
{
"$project": {
players: {
"$reduce": {
input: {
"$filter": {
input: "$players",
cond: {
$eq: [
"$$this.position",
"Goalkeeper"
]
}
}
},
"initialValue": [],
"in": {
$concatArrays: [
"$$value",
[
"$$this.name"
]
]
}
}
}
}
}
])
Explained:
- $filter only the Goalkeeper array objects to provide to $reduce.
- $reduce with $concatArrays only the names.
- Project only the $reduce-d player names array.