1

There are two popular and similar questions to mine, but the difference is that those only have to worry about deep populating associations for one object, whereas mine is about N objects.

Suppose I have 3 models defined as such (left out some attributes for clarity):

identity: 'room',
attributes: {
      LocationId : { type: 'integer',
                  primaryKey: true,
                  required: true,
                  autoIncrement: true },
      DisplayName : { type: 'string',
                      unique: true },

      FloorId : { model: 'Floor' }
 }

  identity: 'floor',
  attributes: {
      FloorId : { type: 'integer',
                  primaryKey: true },
      FloorName : { type: 'string' },

      BuildingId : { model: 'Building' },
      rooms: {collection:'room', via:'FloorId'}
  }


identity: 'building',
attributes: {
      BuildingId : { type: 'integer',
                  primaryKey: true },
      BuildingName : { type: 'string' },

      floors: {collection:'floor', via:'BuildingId'}
 }

The end goal is to have an array of objects that has this basic structure:

[{
    "LocationId": 555,
    "DisplayName": 'SomeCoolName',
    "Floor" : { 
           "FloorId": 1337,
           "FloorName": '5',
           "Building": {
                "BuildingId": 4321,
                "BuildingName": 'HQ'
            }
     }
},  {...}]

I've not got far due to not knowing the BlueBird library promises as well as I should:

showWithAssetGeo: function(req, res) {
    room.find( { assetCount: { '>': 0 } } )
        .populate('FloorId')
        .each(function(room){
            var Building = Building.find({ id: _.pluck(room.FloorId, 'BuildingId') })
            .then(function(Building) {return Building;});
            return [room, Building];
        })
        .spread(function(room, Building) {
            //Something to combine it all?
        })
        .catch (function(err) {
            if (err) { res.badRequest('reason' + err); }
         }
}

UPDATE: Had to tweak the answer marked below. Here is the final working code.

Community
  • 1
  • 1
schumacherj
  • 1,284
  • 5
  • 18
  • 33
  • I am almost positive that i should use .map() instead of .each(). My callback to get the Building object doesn't need to be done in a sequential manner and would slow everything down. Right? – schumacherj Sep 15 '15 at 20:43

1 Answers1

2

You need to make sure to execute the find by calling then or exec (each won't do it).

Seems like you're trying to map across all the floors and then bring those promises back to one. Promise.all() is the way to do that.

Try something like the below:

showWithAssetGeo: function(req, res) {
  room.find( { assetCount: { '>': 0 } } )
    .populate('FloorId')
    .then(function(rooms) {
      return Promise.all(rooms.map(function(room) {
        return Building.findOne({id: room.FloorId.BuildingId})
          .then(function(building) {
            room.FloorId.building = building;
          });
      })
    })
    .then(function(deeplyPopulatedRooms) {
      res.json(deeplyPopulatedRooms);
    })
    .catch(function(error) {
      if (err) { res.badRequest('reason' + err); }
    });
}

However, it would probably be more performant to pluck all the id's of the possible buildings and do one find for all id's. But the above should work and seems to be consistent with the approach you were taking before.

davepreston
  • 1,260
  • 1
  • 10
  • 20