Reputation: 1415
I have a class schema as below
const AttendanceSchema = new mongoose.Schema({
date: Date,
records: [{
studentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
attendance: Boolean
}]
})
var ClassSchema = new mongoose.Schema({
title: String,
description: String,
attendances: [AttendanceSchema]
});
Here I have attendance object to maintain attendance of class students. So each and everyday, whenever we take attendance from class, we can $push a object with date and attendance record details. One Class object will look like this
{
title: "Class Name",
description: "Description",
attendances: [
{
_id: "60ed395fb06f1254b0771ce1",
date: "2021-07-13T06:57:00.000+00:00",
records: [
{
attendance: true,
_id: "60ed395fb06f1254b0771ce2",
studentId: "60e6c440c133a9b590f510d5"
},
{
attendance: false,
_id: "60ed395fb06f1254b0771ce3",
studentId: "60e6c4cb7be1ce4b647c546f"
}
]
},
{
_id: "60ed395fb06f1254b0771ce4",
date: "2021-07-14T06:57:00.000+00:00"
records: [
{
attendance: true,
_id: "60ed395fb06f1254b0771ce5",
studentId: "60e6c440c133a9b590f510d5"
},
{
attendance: true,
_id: "60ed395fb06f1254b0771ce6",
studentId: "60e6c4cb7be1ce4b647c546f"
}
]
}
]
}
Here you can see each record object contains a object with object id. I want to get the students details with the same format Desired output
title: "Class Name"
description: "Description"
attendances: [
{
_id: 60ed395fb06f1254b0771ce1
date: 2021-07-13T06:57:00.000+00:00
records: [
{
attendance: true
_id: 60ed395fb06f1254b0771ce2
studentId: 60e6c440c133a9b590f510d5
_studentDetail: {
name: "Name"
detail: ...
}
},
{
attendance: false
_id: 60ed395fb06f1254b0771ce3
studentId: 60e6c4cb7be1ce4b647c546f
}
]
},
...
]
If I use $lookup, I can only get the student detail in separate object. How can I get it?
Upvotes: 1
Views: 67
Reputation: 49985
Since your data structure is deeply nested you need to extract all your studentIds
before running $lookup. The $reduce operator along with $addFields can be used for it.
Once you get all the students from second collection you have two arrays in each document: students
and attendances
. Then you need to merge them by running multiple $map operators along with $mergeObjects and $filter to get matching student for each record
:
db.Attendance.aggregate([
{
$addFields: {
students: {
$reduce: {
input: "$attendances",
initialValue: [],
in: { $setUnion: [ "$$value", "$$this.records.studentId" ] }
}
}
}
},
{
$lookup: {
from: "Students",
localField: "students",
foreignField: "_id",
as: "students"
}
},
{
$project: {
title: 1,
description: 1,
attendances: {
$map: {
input: "$attendances",
as: "a",
in: {
$mergeObjects: [
"$$a",
{
records: {
$map: {
input: "$$a.records",
as: "r",
in: {
attendance: "$$r.attendance",
_id: "$$r._id",
student: {
$arrayElemAt: [
{ $filter: { input: "$students", cond: { $eq: [ "$$this._id", "$$r.studentId" ] } } }
, 0
]
}
}
}
}
}
]
}
}
}
}
}
])
Upvotes: 1