Home > front end >  How do I make sure that an array field always comes out even if its empty in MongoDB?
How do I make sure that an array field always comes out even if its empty in MongoDB?

Time:12-14

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

  • Related