Abir Taheer
Abir Taheer

Reputation: 2783

How to filter a nested array in mongoose in a findOne without the nested object being required

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
    }
})

Upvotes: 1

Views: 1403

Answers (1)

Matt
Matt

Reputation: 74831

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.

Upvotes: 3

Related Questions