1

I have a collection called elections and in the elections schema there's a nested array called votes. I'm trying to query an election by id as well as filter the nested votes objects by a userId property. I want the parent election object to always be returned and if the current user hasn't voted in the election the votes property should be an empty array.

This is my query:

Election.findOne({
    _id: electionId,
    'votes.userId': userId
})
.exec()

The issue is that if there aren't any votes with the userId the parent Election object isn't returned as well. Is there any way to filter the votes property but also make it not-required for matching the parent election object.

I come from a background in sql and in the Sequelize here is how I'd do what I want to do:

models.Election.findOne({
    where: {
        id: electionId
    }
    include: {
        model: models.Vote,
        where: {
            userId
        },
        required: false
    }
})
Abir Taheer
  • 2,502
  • 3
  • 12
  • 34

1 Answers1

2

MongoDB has a $filter aggregation

const res = await Election.aggregate([
  { $match: { _id: mongoose.Types.ObjectId(electionId) } },
  { $project: {
      _id: 1,
      votes: { $filter: {
        input: '$votes',
        as: 'userVote',
        cond: { $eq: [ '$$userVote', userId ] },
      }}
  }}
])

Which equates to a plain js filter like

votes.filter(userVote => userVote.userId === userId)

Some other answers to this question on filtering arrays in mongodb are relevant too but the query requirement there is slightly different to your question, more like your initial attempt.

Matt
  • 68,711
  • 7
  • 155
  • 158