Home > Mobile >  Nodejs mongoose get results from multiple collections in one query
Nodejs mongoose get results from multiple collections in one query

Time:10-01

I'm very new to Nodejs and MongoDB, I have 3 collections one for chapter,one for lecture and one for asset every course have chapters, every chapter has array of lectures and every lecture have assets so I want to get chapters by courseId which already done, and inside chapter to get its lectures and in every lecture to get its assets

Course:

const mongoose = require('mongoose');
var Double = require('@mongoosejs/double');

const courseSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
    },
    requirements: {
        type: String,
    },
    
    code: {
        type: String,
        required: true,
    },
    coverPhoto: {
        type: String,
        required: false,
    },
    
    description: {
        type: String
    },

    instructor:{
        type:mongoose.Schema.Types.ObjectId,
        ref:'User',
        required:true
    },

    category:{
        type:mongoose.Schema.Types.ObjectId,
        ref:'Category',
        required:true
    },

    learns: [{
        type: String
    }],
    subCategory:{
        type:mongoose.Schema.Types.ObjectId,
        ref:'SubCategory',
        required:true
    },


    isCoaching: {
        type: Boolean,
        default: false,
    },

    isFree: {
        type: Boolean,
        default: false,
    },

    price: {
        type: Double,
        default: 0,
    },
    rating: {
        type: Double,
        default: 0,
    },
    isPublished: {
        type: Boolean,
        default: false,
    },

    dateCreated: {
        type:Date,
        default:Date.now,
    },
});

exports.Course = mongoose.model('Course', courseSchema);
exports.courseSchema = courseSchema;

Chapter:

    const mongoose = require('mongoose');

    const chapterSchema = new mongoose.Schema({
        course:{
            type:mongoose.Schema.Types.ObjectId,
            ref:'Course',
            required:true
        },
        title: {
            type: String,
            required: true,
        },
        sort_order: {
            type: Number,
            default: 1,
        },
        is_published: {
            type: Boolean,
            default: true,
        },

    });
    exports.Chapter = mongoose.model('Chapter', chapterSchema);
    exports.chapterSchema = chapterSchema;

Lecture:

const mongoose = require('mongoose');

const lectureSchema = new mongoose.Schema({
    chapter:{
        type:mongoose.Schema.Types.ObjectId,
        ref:'Chapter',
        required:true
    },

    title: {
        type: String,
        required: true,
    },
    sort_order: {
        type: Number,
        default: 1,
    },

    is_published: {
        type: Boolean,
        default: true,
    },


});

exports.Lecture = mongoose.model('Lecture', lectureSchema);
exports.lectureSchema = lectureSchema;

Asset:

const mongoose = require('mongoose');

const assetSchema = new mongoose.Schema({
    lecture:{
        type:mongoose.Schema.Types.ObjectId,
        ref:'Lecture',
        required:true
    },
    title: {
        type: String,
        required:true
    },
    asset_type: {
        type: String
    },
    description: {
        type: String,
        require:true
    },

    file_url: {
        type: String,
        require:true
    },
    page_number: {
        type: Number,
        default:1
    },
    time_estimation: {
        type: String,
        require:true
    },

    is_external: {
        type: Boolean,
        default: false,
    },

    is_published: {
        type: Boolean,
        default: true,
    },


});


exports.Asset = mongoose.model('Asset', assetSchema);
exports.assetSchema = assetSchema;

Get Chapters of a course

router.get(`/`, async (req, res) => {
    let course_filter = {};
    if (req.query.course) {
        course_filter = {course:req.query.course};
    }
    const chapterList = await Chapter.find(course_filter).populate('lecture').sort('sort_order');
    if (!chapterList) {
        res.status(500).json({ success: false });
    }
    res.send(chapterList);
});

CodePudding user response:

using simple aggregation:

const chapterList = await Chapter.aggregate([{
    $lookup: {
    from: 'lectures',
    localField: '_id',
    foreignField: 'chapter',
    as: 'lectures'
    }}]);

CodePudding user response:

You have to nest the populate in another populate:

router.get(`/`, async (req, res) => {
  let course_filter = {};
  if (req.query.course) {
    course_filter = { course: req.query.course };
  }
  const chapterList = await Chapter.find(course_filter)
    .populate({ path: 'lectures', populate: { path: 'assets' } })
    .sort('sort_order');
  if (!chapterList) {
    res.status(500).json({ success: false });
  }
  res.send(chapterList);
});

You have to ensure that you have set a virtual for 'assets' prop in lectureSchema accordingly. I assume you have also done it for your 'Chapter' schema.

If not, you have do add the following:

virtual for Chapter schema:

chapterSchema.virtual('lectures', {
  ref: Lecture.collection.collectionName,
  localField: '_id',
  foreignField: 'chapter'
});

virtual for Lecture schema:

lectureSchema.virtual('assets', {
  ref: Asset.collection.collectionName,
  localField: '_id',
  foreignField: 'lecture'
});
  • Related