Mongodb – How to query matching MongoDB documents and return a subset of fields from an array of embedded documents

mongodbnosql

I have documents similar to the following:

db.etudiants.insert(
    {
        nom:'emile',
        prenom:'zola',
        cours_reussis:[
            { 
                intitule: ['PAI'],
                Periodes: NumberInt(60),
                note: 60,
                date_reussite: NumberInt(2014),
                dispense: true,
                nom_ecole:'EPFC'
            },
            {
                intitule:'TGP',
                Periodes: NumberInt(40),
                note:75,
                date_reussite: NumberInt(2013),
                dispense: true,
                nom_ecole: 'HEB'
            },
            {
                intitule: 'SO2',
                Periodes: NumberInt(60),
                note: 70,
                date_reussite: NumberInt(2015),
                dispense: true,
                nom_ecole: 'ISFCE'
            },
            {
                intitule: 'IBD',
                Periodes: NumberInt(60),
                note: 80,
                date_reussite: NumberInt(2015),
                dispense:true,
                nom_ecole:'ISFCE'
            }
        ]
    }
)

How can query matching documents and return nom_ecole: 'ISFCE' and year (date_reussite) from the cours_reussis array?

Best Answer

If you want to return a subset of matching array elements in a MongoDB document, the best approach to do so is using the Aggregation Framework. The Aggregation Framework processes data similar to a Unix pipeline: the output of each aggregation stage is passed as input to the next:

db.etudiants.aggregate([
    // Find relevant documents: this can take advantage of an index, if applicable
    { $match: {
        'cours_reussis.nom_ecole': 'ISFCE',
    }},

    // Expand cours_reussis array into a stream of documents
    { $unwind: '$cours_reussis' },

    // Filter based on matching array elements
    { $match: {
        'cours_reussis.nom_ecole': 'ISFCE',
    }},

    // Project desired subset of fields
    { $project: {
        '_id': "$_id",
        'nom': '$nom',
        'prenom': '$prenom',
        'cours_reussis' : { 'nom_ecole': 1, date_reussite: 1 }
    }},

    // Regroup data as per the original document _id
    { $group: {
        _id: "$_id",
        nom: { $first: "$nom" },
        prenom: { $first: "$prenom" },
        cours_reussis: { $push: "$cours_reussis" }
    }}
])

You can debug the aggregation pipeline by starting with a single stage and then adding on additional stages one at a time assuming you are seeing the expected output.

Sample output from MongoDB 3.2:

{
  "_id": ObjectId("58462f40f8475aba9f391216"),
  "nom": "emile",
  "prenom": "zola",
  "cours_reussis": [
    {
      "date_reussite": 2015,
      "nom_ecole": "ISFCE"
    },
    {
      "date_reussite": 2015,
      "nom_ecole": "ISFCE"
    }
  ]
}