Reputation: 95
I have 2 collections -
Student collection (sample student document)
{
'id': '123',
'name': 'john',
'age': 25,
'fav_colors': ['red', 'black'],
'marks_in_subjects': [
{
'marks': 90,
'subject_id': 'abc'
},
{
'marks': 92,
'subject_id': 'def'
}
]
}
Subjects collection (2 sample documents)
{
'id': 'abc',
'name': 'math'
},
{
'id': 'def',
'name': 'physics'
}
When I query for student document for id: '123', I want the resulting output as:
{
'id': '123',
'name': 'john',
'age': 25,
'fav_colors': ['red', 'black'],
'marks_in_subjects': [
{
'marks': 90,
'subject_id': 'abc',
'subject_name': 'math'
},
{
'marks': 92,
'subject_id': 'def',
'subject_name': 'physics'
}
]
}
Now, I read the MongoDB aggregation pipelines and operators document, still, I am clueless as to how to achieve this. The doubt persists because I am not even sure if this is possible with the help of mongo aggregation pipelines since JOIN happens here for every element of the array field in the student document.
It would be really helpful if anyone can help here. Thanks
Upvotes: 3
Views: 7138
Reputation: 36144
$match
you conditions$unwind
deconstruct marks_in_subjects
array$lookup
with subjects
collection$addFields
to get first element name
from return subject$group
by id
and reconstruct marks_in_subjects
array and also add your required field of root document using $first
operatordb.students.aggregate([
{ $match: { id: "123" } },
{ $unwind: "$marks_in_subjects" },
{
$lookup: {
from: "subjects",
localField: "marks_in_subjects.subject_id",
foreignField: "id",
as: "marks_in_subjects.subject_name"
}
},
{
$addFields: {
"marks_in_subjects.subject_name": {
$arrayElemAt: ["$marks_in_subjects.subject_name.name", 0]
}
}
},
{
$group: {
_id: "$id",
name: { $first: "$name" },
age: { $first: "$age" },
fav_colors: { $first: "$fav_colors" },
marks_in_subjects: { $push: "$marks_in_subjects" }
}
}
])
Second option without $unwind
stage,
$match
you conditions$lookup
with subjects
collection$addFields
to get subject name from subjects
$map
to iterate loop of marks_in_subjects
array$reduce
to iterate loop of subjects
array and check condition if subject_id
match then return subject name
$mergeObjects
to merge current object of marks_in_subjects
and new field subject_name
$unset
to remove subjects
array because its not needed nowdb.students.aggregate([
{ $match: { id: "123" } },
{
$lookup: {
from: "subjects",
localField: "marks_in_subjects.subject_id",
foreignField: "id",
as: "subjects"
}
},
{
$addFields: {
marks_in_subjects: {
$map: {
input: "$marks_in_subjects",
as: "m",
in: {
$mergeObjects: [
"$$m",
{
subject_name: {
$reduce: {
input: "$subjects",
initialValue: "",
in: {
$cond: [{ $eq: ["$$this.id", "$$m.subject_id"]}, "$$this.name", "$$value"]
}
}
}
}
]
}
}
}
}
},
{ $unset: "subjects" }
])
Upvotes: 4
Reputation: 57105
Demo - https://mongoplayground.net/p/H5fHpfWz5VH
db.Students.aggregate([
{
$unwind: "$marks_in_subjects" // break into individual documents
},
{
"$lookup": { // get subject details
"from": "Subjects",
"localField": "marks_in_subjects.subject_id",
"foreignField": "id",
"as": "subjects"
}
},
{
$set: { // set name
"marks_in_subjects.name": "subjects.0.name" // pick value from 0 index
}
},
{
$group: { // join document back by id
_id: "$_id",
marks_in_subjects: { $push: "$marks_in_subjects" }
}
}
])
Upvotes: 3