Mongoose populate nested array

lostintranslation picture lostintranslation · Jan 27, 2015 · Viewed 29.3k times · Source

Assuming the following 3 models:

var CarSchema = new Schema({
  name: {type: String},
  partIds: [{type: Schema.Types.ObjectId, ref: 'Part'}],
});

var PartSchema = new Schema({
  name: {type: String},
  otherIds: [{type: Schema.Types.ObjectId, ref: 'Other'}],
});

var OtherSchema = new Schema({
  name: {type: String}
});

When I query for Cars I can populate the parts:

Car.find().populate('partIds').exec(function(err, cars) {
  // list of cars with partIds populated
});

Is there a way in mongoose to populate the otherIds in the nested parts objects for all the cars.

Car.find().populate('partIds').exec(function(err, cars) {
  // list of cars with partIds populated
  // Try an populate nested
  Part.populate(cars, {path: 'partIds.otherIds'}, function(err, cars) {
    // This does not populate all the otherIds within each part for each car
  });
});

I can probably iterate over each car and try to populate:

Car.find().populate('partIds').exec(function(err, cars) {
  // list of cars with partIds populated

  // Iterate all cars
  cars.forEach(function(car) {
     Part.populate(car, {path: 'partIds.otherIds'}, function(err, cars) {
       // This does not populate all the otherIds within each part for each car
     });
  });
});

Problem there is that I have to use a lib like async to make the populate call for each and wait until all are done and then return.

Possible to do without looping over all cars?

Answer

Sven picture Sven · Jan 27, 2015

Update: Please see Trinh Hoang Nhu's answer for a more compact version that was added in Mongoose 4. Summarized below:

Car
  .find()
  .populate({
    path: 'partIds',
    model: 'Part',
    populate: {
      path: 'otherIds',
      model: 'Other'
    }
  })

Mongoose 3 and below:

Car
  .find()
  .populate('partIds')
  .exec(function(err, docs) {
    if(err) return callback(err);
    Car.populate(docs, {
      path: 'partIds.otherIds',
      model: 'Other'
    },
    function(err, cars) {
      if(err) return callback(err);
      console.log(cars); // This object should now be populated accordingly.
    });
  });

For nested populations like this, you have to tell mongoose the Schema you want to populate from.