Home > Blockchain >  mongodb query agregate map many to many
mongodb query agregate map many to many

Time:01-27

I have two collections:

  1. Courses
  2. Students

I want to get details of students that are assigned to courses in related courses object.

E.g here are my collections and data

Courses:

[
    {
        _id: 1,
        title: "Maths",
        studentGroups: [
            {
                group: 'A',
                students: [1, 5]

            },
            {
                group: 'B',
                students: [3]

            }
        ]
    },
    {
        _id: 2,
        title: "Chemistry",
        studentGroups: [
            {
                group: 'C',
                students: [2]

            },
            {
                group: 'B',
                students: [4]

            }
        ]
    }
]

Students:

[
    {
        _id:  1,
        name: 'Henry',
        age: 15
    },
    {
        _id:  2,
        name: 'Kim',
        age: 20
    },
    {
        _id:  3,
        name: 'Michel',
        age: 14
    },
    {
        _id:  4,
        name: 'Max',
        age: 16
    },
    {
        _id:  5,
        name: 'Nathan',
        age: 19
    }
]

Now I want this response:

[
    {
        _id: 1,
        title: "Maths",
        studentGroups: [
            {
                group: 'A',
                students: [
                    {
                        _id:  1,
                        name: 'Henry',
                        age: 15
                    },
                    {
                        _id:  5,
                        name: 'Nathan',
                        age: 19
                    }
                ]

            },
            {
                group: 'B',
                students: [
                    {
                        _id:  3,
                        name: 'Michel',
                        age: 14
                    }
                ]

            }
        ]
    },
    {
        _id: 2,
        title: "Chemistry",
        studentGroups: [
            {
                group: 'C',
                students: [
                    {
                        _id:  2,
                        name: 'Kim',
                        age: 20
                    }
                ]

            },
            {
                group: 'B',
                students: [
                    {
                        _id:  4,
                        name: 'Max',
                        age: 16
                    }
                ]
            }
        ]
    }
]

What I have tried gives my the correct result but it is not a good approach, I need to achieve this by single aggregation query

My solutions:

var courses = Courses.find({}).fetch();

courses.forEach(c => {
    if (c.studentGroups && c.studentGroups.length) {
        c.studentGroups.forEach(s => {
            s.students = Students.find({_id: {$in: s.students}}).fetch()
        })
    }
})

Can anyone suggest me a better solution with single aggregation query?

CodePudding user response:

First $unwind the studentGroups. Then, $lookup from students. Finally, regroup the results using $group`.

db.courses.aggregate([
  {
    "$unwind": "$studentGroups"
  },
  {
    "$lookup": {
      "from": "students",
      "localField": "studentGroups.students",
      "foreignField": "_id",
      "as": "studentGroups.students"
    }
  },
  {
    $group: {
      _id: "$_id",
      title: {
        $first: "$title"
      },
      studentGroups: {
        $push: "$studentGroups"
      },
      description: {
        $first: "$description"
      },
      timeline: {
        $first: "$timeline"
      }
    }
  }
])

Mongo Playground

  • Related