Search code examples
node.jsmongodbmongoosemongoose-populate

Mongoose populate nested array


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?


Solution

  • 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.