Get empty array when try to read all students with standard and division

Data Base name:studentdata

//Class.js
const mongoose = require(‘mongoose’);

const classSchema = new mongoose.Schema({
standard: {
type :String
},
division: {
type :String
} ,
});

module.exports = mongoose.model(‘Class’, classSchema);


//Student.js
const mongoose = require('mongoose');

const studentSchema = new mongoose.Schema({
  name: {
    type :String
  },
  rollno: {
    type :String
  },
  mobileno:{
    type :String
  },
  classId: mongoose.Schema.Types.ObjectId,// Defines the field as an ObjectId reference
});

module.exports = mongoose.model('Student', studentSchema);

~~``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//studentController.js
const Student = require('../models/Student');//import the Student model
const Class = require('../models/Class'); // Import the Class model
 //Read all students in a class with standard and division

exports.getStudentsByClassStandardDivision = async (req, res) => {
    try {
      const standard= req.params.standard;
      const division=req.params.division;
  
      // Find all students in the specified class based on standard and division
     const students = await Student.find({ 'classId.standard': standard, 'classId.division': division });
    
      res.status(200).json(students);
      
    } catch (error) {  
      console.error('Error getting students by class, standard, and division:', error);
      res.status(500).json({ error: 'Error getting students by class, standard, and division' });
    }
  };

Hello, @Rameesa_Hassan! Welcome to the MongoDB community! :wave:

You get empty array because in your mongoose-find method you query fields, that Student model does not know about.

Moreover, your code and data model can be improved and I will show you how exactly with examples.

To reproduce your case, I am using your model schemas:

const mongoose = require('mongoose');

const studentSchema = new mongoose.Schema({
  name: String,
  rollno: String,
  mobileno: String,
  classId: mongoose.Schema.Types.ObjectId,
});

const classSchema = new mongoose.Schema({
  standard: String,
  division: String,
});

const studentModel = mongoose.model('Student', studentSchema);
const classModel = mongoose.model('Class', studentSchema);

Then I will create few sample documents do to the tests:

const ObjectId = mongoose.Types.ObjectId;

await studentModel.insertMany([
  {
    name: 'Mykola',
    rollno: 'no1',
    mobileno: '+38099',
    classId: new ObjectId('64e4abd5ea1b087ea47d0009'),
  },
  {
    name: 'Sashko',
    rollno: 'no2',
    mobileno: '+38066',
    classId: new ObjectId('64e4abd5ea1b087ea47d0006'),
  }
]);

await classModel.insertMany([
  {
    _id: new ObjectId('64e4abd5ea1b087ea47d0001'),
    standard: 'std-1',
    division: 'A',
  },
  {
    _id: new ObjectId('64e4abd5ea1b087ea47d0002'),
    standard: 'std-2',
    division: 'B',
  }
]);

Okay. Now we are all set! Let’s do some queries!
Let’s see how your student document looks now in the database:

await studentModel.findOne();

Output:

{
  "_id": "64e50e3557a6feae0403312e",
  "name": "Mykola",
  "rollno": "no1",
  "mobileno": "+38066",
  "classId": "64e4abd5ea1b087ea47d0001",
  "__v": 0
}

Notice, that student object does not contain any data about class document, except for its reference of ObjectId type. MongoDB and Mongoose do not know into what collection they have to look to find class data. Moreover, they do not know if any document actually has _id with value 64e4abd5ea1b087ea47d0001.

This command below returns empty array, because zero student documents that have field classId.standard, and zero documents, that have field classId.division. Nothing matched - nothing returned (empty array).

await studentModel.find({ 
  'classId.standard': 'std-2', 
  'classId.division': 'B' 
});

To make your student model aware of the class model (and collection), you need to explicitly mention it in your schema (see more details in the official Mongoose doc):

const studentSchema = new mongoose.Schema({
   /* other fields unchanged */
  classId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Class'
  },
});

Now, with this change if you do that same findOne operation again, you will get the same output - with no class document joined. That’s because you also need to calll populate() method:

await studentModel.findOne({ 
  classId: new ObjectId('64e4abd5ea1b087ea47d0001')
}).populate('classId');

Output:

{
  "_id": "64e50e3557a6feae0403312e",
  "name": "Mykola",
  "rollno": "no1",
  "mobileno": "+38066",
  "classId": {
    "_id": "64e4abd5ea1b087ea47d0001",
    "standard": "std-1",
    "division": "A",
    "__v": 0
  },
  "__v": 0
}

Okay. Now data from class document is joined to student document. That means, that now we can use classId.standard and classId.division like some query above, right? No :smile:.

This is why, under the hood, Mongoose, makes two queries to the database and then merges two results after retrieval. You can check that, if you enable debug mode on your Mongoose connection:

mongoose.set('debug', true);

To get the data your need, you need to run an aggregation pipeline, like this:

await studentModel.aggregate([
  {
    $lookup: {
      from: 'classes',
      localField: 'classId',
      foreignField: '_id',
      as: 'classId'
    }
  },
  {
    $unwind: '$classId'
  },
  {
    // at this stage you can match the document, just like you wanted :)
    $match: {
      'classId.standard': 'std-2',
      'classId.division': 'B'
    }
  }
]);

Output:

[
  {
    "_id": "64e50e3557a6feae0403312f",
    "name": "Sashko",
    "rollno": "no2",
    "mobileno": "+38095",
    "classId": {
      "_id": "64e4abd5ea1b087ea47d0002",
      "standard": "std-2",
      "division": "B",
      "__v": 0
    },
    "__v": 0
  }
]

With MongoDB aggregation you can get the result more efficiently. Avoid using .populate() mehtod :wink:.

Additionally, I can suggest you to add some additional fields to your student schema:

const studentSchema = new mongoose.Schema({
  name: String,
  rollno: String,
  mobileno: String,
  classId: mongoose.Schema.Types.ObjectId,
  classStandard: String, // new field
  classDivision: String,  // new field
});

It is completely OK to have a denormalized data structure in MongoDB. With these changes, you can easily query your data much faster and write queries easier, like this:

await studentModel.find({
  classStandard: 'std-2',
  classDivision: 'B'
});
1 Like

Thank you for your response