How to group results after using $unwing followed by $match?

Viewed 25

I will use the example from here:

{
 _id: 1,
 zipcode: 63109,
 students: [
              { name: "john", school: 102, age: 10 },
              { name: "jess", school: 102, age: 11 },
              { name: "jeff", school: 108, age: 15 }
           ]
}
{
 _id: 2,
 zipcode: 63110,
 students: [
              { name: "ajax", school: 100, age: 7 },
              { name: "achilles", school: 100, age: 8 },
           ]
}

{
 _id: 3,
 zipcode: 63109,
 students: [
              { name: "ajax", school: 100, age: 7 },
              { name: "achilles", school: 100, age: 8 },
           ]
}

{
 _id: 4,
 zipcode: 63109,
 students: [
              { name: "barney", school: 102, age: 7 },
           ]
}

I wanted to run the following aggregation pipeline to get all the matching results in the students array (because with $elemMatch I get only the firest matching element):

db.zip.aggregate(
  {$match: {zipcode: 63109}},
  {$unwind: "$students"},
  {$match: {"students.school": 102}}
)

and what I get is this:

[{
        "_id": 1,
        "zipcode": 63109,
        "students": {
            "name": "john",
            "school": 102,
            "age": 10
        }
    }, {
        "_id": 1,
        "zipcode": 63109,
        "students": {
            "name": "jess",
            "school": 102,
            "age": 11
        }
    }, {
        "_id": 4,
        "zipcode": 63109,
        "students": {
            "name": "barney",
            "school": 102,
            "age": 7
        }
    }
]

But the desired output I need is the same "structure" of the original document. i.e.:

[{
        "_id": 1,
        "zipcode": 63109,
        "students": [{
                "name": "john",
                "school": 102,
                "age": 10
            }, {
                "_id": 1,
                "zipcode": 63109,
                "students": {
                    "name": "jess",
                    "school": 102,
                    "age": 11
                }

            }
        ]
    } {
        "_id": 4,
        "zipcode": 63109,
        "students": [{
                "name": "barney",
                "school": 102,
                "age": 7
            }
        ]
    }
]

What do I need to add to the aggregation pipeline?

1 Answers

As @AlexBlex noticed, you can use a $filter for this:

db.zip.aggregate([
  {$match: {zipcode: 63109}},
  {$set: {
      students: {
        $filter: {
          input: "$students",
          cond: {$eq: ["$$this.school", 102]}}
      }
    }
  },
  {$match: {"students.0": {$exists: true}}}
])

See how it works on the playground example

Related