1
votes

I'm using MongoDB 4.2 and I'm looking for a way to remove all elements from a subarray if it doesn't match a certain condition using an aggregation stage that is compatible with a change streams. The compatible stages are:

  • $addFields
  • $match
  • $project
  • $replaceRoot
  • $replaceWith
  • $redact
  • $set
  • $unset

For example consider that we have a collection, users, containing documents in this format:

{ "name" : "John Doe", 
  "access": [
                { "level" : "Gold", "rating" : 3.2 }, 
                { "level" : "Silver", "rating" : 2.1 }, 
                { "level" : "Gold", "rating" : 4.2 } 
             ] 
}

I'd like to use one, or a combination, of the compatible aggregation stages to remove elements in the "access" array that doesn't match a filter such as { $elemMatch : { "level" : "Gold" } }. I'd like the resulting document to look like this:

{ "name" : "John Doe", 
  "access": [
                { "level" : "Gold", "rating" : 3.2 }, 
                { "level" : "Gold", "rating" : 4.2 } 
             ] 
}

Is it possible to do this in MongoDB?

1

1 Answers

1
votes

You can use $addFields / $set together with $filter

db.collection.aggregate({
  $set: {
    access: {
      $filter: {
        input: "$access",
        cond: { $eq: ["$$this.level", "Gold"] } // your condition expression
      }
    }
  }
})

Mongo Playground

If you want to update existing documents, you can do this with the update pipeline as follows

db.test.updateMany({
  access: { $elemMatch: { level: { $ne: "Gold" } } }, // find elements that does not match your condition
  [{
    $set: {
      access: {
        $filter: {
          input: "$access",
          cond: { $eq: ["$$this.level", "Gold"] } // your condition expression
        }
      }
    }
  }]
)