Khanetor
Khanetor

Reputation: 12302

How to get embedded document in an array in MongoDB (with Mongoose)

I have a BSON object like this saved in MongoDB:

{
  "title": "Chemistry",
  "_id": "532d665f89ae4ae703b29730",
  "__v": 0,
  "sections": [
  {
    "week": 1,
    "_id": "532d665f89ae4ae703b29731",
    "assignments": [
    {
      "created_date": "2014-03-22T10:30:55.621Z",
      "_id": "532d665f89ae4ae703b29733",
      "questions": []
    },
    {
      "created_date": "2014-03-22T10:30:55.621Z",
      "_id": "532d665f89ae4ae703b29732",
      "questions": []
    }
    ],
    "materials": []
  }
  ],
  "instructor_ids": [],
  "student_ids": []
}

What I wish to do is to retrieve the 'assignment' with _id 532d665f89ae4ae703b29731. It is an element in the assignments array, which, in turn, is an element in the sections array.

I am able to retrieve the entire document with the query

{ 'sections.assignments._id' : assignmentId }

However, what I want is just the assignment subdocument

{
"created_date": "2014-03-22T10:30:55.621Z",
"_id": "532d665f89ae4ae703b29733",
"questions": []
}

Is there a way to accomplish such query? Should I resolve to have assignment in a different collection?

Upvotes: 0

Views: 1380

Answers (3)

SirageDb
SirageDb

Reputation: 43

As of mongoose version 6.x, the accepted answer is not valid any more because $elemMatch cannot be used any more on nested documents, instead, aggregate should be used.

if you want ti use an _id to find the document you should convert the _id you get as argument to native mongoDb _id format otherwise it will be constructed as a string and an error will occur.

    const native_id = mongoose.Types.ObjectId(id);
    const assignment = await <your_model_here>.aggregate([
      { $unwind: "$sections" },
      { $unwind: "$sections.assignments" },
      { $match: { "sections.assignments._id": native_id } },
      { $project: { _id: true, sections: "$sections.assignments" } }
    ]
    )
    console.log(assignment) // you have what you want

Upvotes: 2

yyarden
yyarden

Reputation: 86

you can do a aggregate query like this :

db.collection.aggregate(
    {$unwind: "$sections"},
    {$unwind: "$sections.assignments"},
    {$match: {"sections.assignments._id": "532d665f89ae4ae703b29731"}},
    {$project: {_id: false, assignments: "$sections.assignments"}}
)

However, I recommends you to think about creating more collections, like you said. More collections seems to me a better solution then this query.

Upvotes: 1

Anand Jayabalan
Anand Jayabalan

Reputation: 12944

To retrieve a subset of the elements of an array, you'll need to use the $elemMatch projection operator.

db.collection.find(
    {"sections.assignments._id" : assignmentId}, 
    {"sections.assignments":{$elemMatch:{"_id":assignmentId}}}
)

Note:

If multiple elements match the $elemMatch condition, the operator returns the first matching element in the array.

Upvotes: 0

Related Questions