This is an example of a document I have in my teams
collection:
_id: ObjectId('...')
name: Team A
players: [
{
name: Michael
position: Goalkeeper
},
{
name: Frank
position: Defender
}
]
In my application I need to get out the list of players byt filtered on one position. So for example to get Goalkeepers I say:
{
'$match': {
'_id': ObjectId('...'),
'name': 'Team A'
}
}, {
'$unwind': {
'path': '$players'
}
}, {
'$match': {
'players.position': 'Goalkeeper'
}
}, {
'$group': {
'_id': null,
'players': {
'$addToSet': '$players'
}
}
}, {
'$project': {
'_id': 0
}
}
So this aggregation ends up returning the following:
{
players: [
{
name: Michael
position: Goalkeeper
}
]
}
Which is perfect, exactly what I want. However if I want to get a list of Strikers instead, from the same document, I simply get an empty array back like this:
[]
How do I make it return the following, when - and only when - there is no position match:
{
players: []
}
CodePudding user response:
If you want to match only one team, simply use $filter
:
db.collection.aggregate([
{$match: {_id: ObjectId("63920f965d15e98e3d7c450c")}},
{$project: {
players: {
$filter: {
input: "$players",
cond: {$eq: ["$$this.position", "Striker"]}
}
},
_id: 0
}}
])
See how it works on the playground example
But if you want to match more than one group, you can use $reduce
to flatten the array:
db.collection.aggregate([
{$project: {
players: {$filter: {
input: "$players",
cond: {$eq: ["$$this.position", "Striker"]}
}},
_id: 0
}},
{$group: {
_id: 0, players: {$push: "$players"}
}},
{$project: {
_id: 0,
players: {
$reduce: {
input: "$players",
initialValue: [],
in: {$concatArrays: ["$$value", "$$this"]}
}
}
}}
])
See how it works on the playground example