7

Schema Definitions

Team.js

var TeamSchema = new Schema({
    // Team Name.
    name: String,
    lead: String,
    students :type: [{
      block : Number,
      status : String,
      student : {
        type: Schema.ObjectId,
        ref: 'Student'
    }]
});

Student.js

var StudentSchema = new Schema({
   name: String,
   rollNo : Number,
   class : Number
});

How I can populate "student" to get output, as below:

team

{
    "__v": 1,
    "_id": "5252875356f64d6d28000001",
    "students": [
        {
            "__v": 1,
            "_id": "5252875a56f64d6d28000002",
             block : 1,
             status : joined,
            "student": {
                "name": Sumeeth
                "rollNo" : 2
                "class" : 5
            }
        },
        {
            "__v": 1,
            "_id": "5252875a56f64d6d28000003",
            block : 1,
            status : joined,
            "student": {
                "name": Sabari
                "rollNo" : 3
                "class" : 4
            }
        }
    ],
    "lead": "Ratha",   
}

This is JS I use to get the document using Mongoose:

Team.findOne({
    _id: req.team._id
})            
.populate('students')
.select('students')
.exec(function(err, team) {
    console.log(team);
    var options = {
        path: 'students.student',
        model: 'Student'
    };
    Student.populate(team.students,options,function(err, students) {
        console.log(students);
        if (err) {
            console.log(students);
            res.send(500, {
                message: 'Unable to query the team!'
            });
        } else {
            res.send(200, students);
        }
    });
});

In my console output I get the following:

{ _id: 53aa434858f760900b3f2246,
  students
   [ { block : 1
       status: 'joined'
       _id: 53aa436b58f760900b3f2249 },
     { block : 1
       status: 'joined'
       _id: 53aa436b58f760900b3f2250 }]
}

And the expected output is:

  { _id: 53aa434858f760900b3f2246,
      students
       [ { block : 1
           status: 'joined'
           student :{
              "name": Sumeeth
              "rollNo" : 2
              "class" : 5
           } 
         },
         { block : 1
           status: 'joined'
           student :{
               "name": Sabari
              "rollNo" : 3
              "class" : 4
           } 
        }
     ]
    }

Some one please help me where I am wrong. How should I make use of .populate, so that , I can get the entire student object and not only its id.

Reference : Populate nested array in mongoose

Matt
  • 33,328
  • 25
  • 83
  • 97
sabari
  • 2,595
  • 5
  • 28
  • 44

4 Answers4

4

I have been facing same issue. I have use this code for my rescue :

Team.findOne({_id: req.team._id})
.populate({ path: "students.student"})
.exec(function(err, team) {
       console.log(team);
});
Nivs
  • 376
  • 2
  • 15
3

Here is a simplified version of what you want.

Basic data to set up, first the "students":

{ 
   "_id" : ObjectId("53aa90c83ad07196636e175f"), 
   "name" : "Bill",
   "rollNo" : 1,
   "class" : 12 
},
{ 
    "_id" : ObjectId("53aa90e93ad07196636e1761"),
    "name" : "Ted",
    "rollNo" : 2,
    "class" : 12
}

And then the "teams" collection:

{ 
    "_id" : ObjectId("53aa91b63ad07196636e1762"),
    "name" : "team1",
    "lead" : "me",
    "students" : [ 
        { 
            "block" : 1,
            "status" : "Y",
            "student" : ObjectId("53aa90c83ad07196636e175f")
        },
        { 
            "block" : 2,
            "status" : "N",
            "student" : ObjectId("53aa90e93ad07196636e1761")
        }
    ]
}

This is how you do it:

var async = require('async'),
    mongoose = require('mongoose');
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/team');

var teamSchema = new Schema({
  name: String,
  lead: String,
  students: [{
    block: Number,
    status: String,
    student: {
      type: Schema.ObjectId, ref: 'Student'
    }
  }]
});

var studentSchema = new Schema({
  name: String,
  rollNo: Number,
  class: Number
});

var Team = mongoose.model( "Team", teamSchema );
var Student = mongoose.model( "Student", studentSchema );

Team.findById("53aa91b63ad07196636e1762")
  .select('students')
  .exec(function(err, team) {
    console.log( team );

    async.forEach(team.students, function(student,callback) {
      Student.populate(
        student,
        { "path": "student" },
        function(err,output) {
          if (err) throw err;
          callback();
        }
      );
    },function(err) {
      console.log( JSON.stringify( team, undefined, 4 ) );
    });

  });

And it gives you the results:

{
    "_id": "53aa91b63ad07196636e1762",
    "students": [
        {
            "block": 1,
            "status": "Y",
            "student": {
                "_id": "53aa90c83ad07196636e175f",
                "name": "Bill",
                "rollNo": 1,
                "class": 12
            }
        },
        {
            "block": 2,
            "status": "N",
            "student": {
                "_id": "53aa90e93ad07196636e1761",
                "name": "Ted",
                "rollNo": 2,
                "class": 12
            }
        }
    ]
}

You really do not need the "async" module, but I am just "in the habit" as it were. It doesn't "block" so therefore I consider it better.

So as you can see, you initial .populate() call does not do anything as it expects to "key" off of an _id value in the foreign collection from an array input which this "strictly speaking" is not so as the "key" is on "student" containing the "foreign key".

I really did cover this in a recent answer here, maybe not exactly specific to your situation. It seems that your search did not turn up the correct "same answer" ( though not exactly ) for you to draw reference from.

Community
  • 1
  • 1
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
1

You are overthinking it. Let Mongoose do the work for you.

Team.findOne({
    _id: req.team._id
})            
.populate({path:'students'})
.exec(function(err, team) {
    console.log(team);
});

This will return students as documents rather than just the ids.

Matt
  • 33,328
  • 25
  • 83
  • 97
1

TL DR

const team = await Team.findById(req.team._id)
  .populate("students");

team.students = await Student.populate(team.students, {path: "student"});

Context

Reading from all the answers I went testing everything and just Neil Lun's answer worked for me. The problem is it was on the path to a cb hell. So I cracked my head a little and 'refactored' to an elegant one-liner.

const foundPost = await Post.findById(req.params.id)
  .populate("comments")
  .populate("author");

foundPost.comments = await User.populate(foundPost.comments, {path: "author"}); 

My initial problem:

{
  title: "Hello World",
  description: "lorem",
  author: {/* populated */},
  comments: [ // populated
    {text: "hi", author: {/* not populated */ }}
  ]
};

How my models basically are:

User = {
  author,
  password
};

Post = {
  title,
  description,
  author: {}, //ref User
  comments: [] // ref Comment
};

Comment = {
  text,
  author: {} // ref User
};

The output after problem solved:

{
  comments: [
    {
      _id: "5dfe3dada7f3570b60dd977f",
      text: "hi",
      author: {_id: "5df2f84d4d9fcb228cd1df42", username: "jo", password: "123"}
    }
  ],
  _id: "5da3cfff50cf094c68aa2a37",
  title: "Hello World",
  description: "lorem",
  author: {
    _id: "5df2f84d4d9fcb228cd1aef6",
    username: "la",
    password: "abc"
  }
};
Tales
  • 354
  • 4
  • 5