admin管理员组

文章数量:1424377

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);
      }
    }
  );
};

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);
      }
    }
  );
};
Share Improve this question asked Jun 26, 2022 at 2:40 Jonan Mathew GeorgeJonan Mathew George 921 silver badge8 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

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/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/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/p/vdPVbce6WkX

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.

本文标签: javascriptMongoose get objects which match an object item from an array of objectsStack Overflow