3

Supose a database containing something like that

{  
   "grades":[  
      {  
         "grade":"A",
         "score":2
      },
      {  
         "grade":"A",
         "score":6
      },

   ],
   "name":"Morris Park Bake Shop"
},
{  
   "grades":[  
      {  
         "grade":"A",
         "score":8
      },
      {  
         "grade":"B",
         "score":23
      }
   ],
   "name":"Wendy'S"
}

How can I apply a filter that will just return the restaurants where ALL grades are "A"?

If I try db.restaurants.find({ "grades.grade" : "A" } ), the way it works is that it search for ANY grade inside my element.

I tried using aggregate with unwind to, but it do the same thing, it opens grades, filter, and returns any match of restaurant...

  • Possible duplicate of [Check if every element in array matches condition](https://stackoverflow.com/questions/23595023/check-if-every-element-in-array-matches-condition) – s7vr Sep 23 '17 at 16:34

1 Answers1

2

In your situation I would do something like this :

db.getCollection('test').aggregate([
{$unwind:"$grades"},
    { $group: { 
        _id: '$_id', 
        grades : { $first: '$grades' },  
        all_grades: { $sum: 1 },
        all_grades_that_match: { $sum: { $cond: [ { $eq: [ '$grades.grade', "A" ] }, 1, 0 ] } },     
        name: { $first: '$name' }     
    }},
   { $project: {
        _id: 1,
        name: 1,
        grades: 1,
        arrays_equal: { $cond: [ { $eq: [ '$all_grades', '$all_grades_that_match' ] }, 1, 0 ] }
    }},
    { $match: { 'arrays_equal' : 1 } } 
])

The group operation will count the total number of grades and the number of grades that match you query, the projection will compare those two results to see if they are equal, finally, the match operation will only keep the ones where arrays_equal is true

Nicolas Ducom
  • 238
  • 2
  • 13