skip to Main Content

I would like to find a single document matching the courseID but inside the document only objects from the materials array whose moduleNo matches the one I give. But the query I currently use seems to return the correct document but also returns all the objects in materials array. Any help would be appreciated.

My schema,

const materialSchema = new mongoose.Schema({
  courseID: String,
  materials: [
    {
      moduleNo: Number,
      moduleMaterial: String,
    },
  ],
});

My code/query,

 exports.getMaterials = (req, res) => {
  const { courseID, moduleNo } = req.query;
  Material.findOne(
    { courseID, "materials.moduleNo": moduleNo },
    (err, result) => {
      if (err) {
        console.error(err);
      } else {
        res.json(result);
      }
    }
  );
};

2

Answers


  1. Use the $elemMatch operator, something lik this:

    exports.getMaterials = (req, res) => {
      const { courseID, moduleNo } = req.query;
      Material.findOne(
        { courseID },
        {"materials": { $elemMatch: {moduleNo: moduleNo}},
        (err, result) => {
          if (err) {
            console.error(err);
          } else {
            res.json(result);
          }
        }
      );
    };
    

    Update: To return all matching elements in the array, you will have to use an aggregation pipeline, having $filter stage, to filter out array elements. Like this:

    exports.getMaterials = (req, res) => {
      const { courseID, moduleNo } = req.query;
      Material.aggregate(
        [
      {
        $match: {
          courseID: courseID
        }
      },
      {
        "$project": {
          courseID: 1,
          materials: {
            "$filter": {
              "input": "$materials",
              "as": "material",
              "cond": {
                "$eq": [
                  "$$material.moduleNo",
                  moduleNo
                ]
              }
            }
          }
        }
      }
    ]
      );
    };
    

    Here’s the playground link.

    Login or Signup to reply.
  2. Way 1 : Use $elemMatch operator in project field

    The $elemMatch operator limits the contents of an array field from
    the query results to contain only the first element matching the
    $elemMatch condition

    Result : Returns only one matching array element

    syntax :

    db.collection.find(query,projection)
    
    db.collection.find({
         "field": field_value                  
      },{
        "array_name":{
           $elemMatch:{"key_name": value }
        },
        field:1,
        field_2:1,
        field_3:0
    })
    

    https://mongoplayground.net/p/HKT1Gop32Pq

    Way 2 : Array Field Limitations array.$ in project field
    *

    Result : Returns only one matching array element

    db.collection.find({
         "field": field_value,
         "array_name.key_name": value       
      },{
            "array_name.$":1,
            field:1,
            field_2:1,
            field_3:0
     });
    

    https://mongoplayground.net/p/Db0azCakQg9

    Update : Using MongoDB Aggregation

    Result : Returns multiple matching array elements

    db.collection.aggregate([
      {
        "$unwind": "$materials"
      },
      {
        "$match": {
          "courseID": "Demo",
          "materials.moduleNo": 1
        }
      }
    ]) 
    

    will return output as :

    [
      {
        "_id": ObjectId("5a934e000102030405000000"),
        "courseID": "Demo",
        "materials": {
          "moduleMaterial": "A",
          "moduleNo": 1
        }
      },
      {
        "_id": ObjectId("5a934e000102030405000000"),
        "courseID": "Demo",
        "materials": {
          "moduleMaterial": "B",
          "moduleNo": 1
        }
      }
    ]
    

    And If you want to format output :

    db.collection.aggregate([
      {
        "$unwind": "$materials"
      },
      {
        "$match": {
          "courseID": "Demo",
          "materials.moduleNo": 1
        }
      },
      {
        "$group": {
          "_id": {
            "courseID": "$courseID",
            "moduleNo": "$materials.moduleNo"
          },
          "materials": {
            "$push": "$materials.moduleMaterial"
          }
        }
      },
      {
        "$project": {
          "_id": 0,
          "courseID": "$_id.courseID",
          "moduleNo": "$_id.moduleNo",
          "materials": "$materials"
        }
      }
    ])
    

    will return result as :

    [
      {
        "courseID": "Demo",
        "materials": [
          "A",
          "B"
        ],
        "moduleNo": 1
      }
    ]
    

    https://mongoplayground.net/p/vdPVbce6WkX

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search