Mongoose and population nested Mongoose and population nested mongoose mongoose

Mongoose and population nested


JavaScript response only. You do the translation :)

This doesn't really work in this context for actual reasons that escape me at the moment, but in order to do what you want you need to call the "Model" form of .populate() instead:

Agency.findOne({"agencyUsers": idUser}, '_id agencyUsers agencySites agencyDevices')    .populate([      { "path": "agencyUsers", "match": { "actif": true}, "select": "_id userPin" },      {        "path": "agencySites",         "match": { "actif": true },         "select": "_id siteName siteBuildings siteSectors siteUsers"      },      {        "path": "agencyDevices",         "match": { "actif": true},         "select": "_id deviceMac"      }]).exec(function(err,doc) {    if (err) {        deffered.reject(err);    } else {        async.waterfall([           function(callback) {               Agency.populate( doc, { "path": "agencyAgencies.siteAgencies" },callback);           },           function(doc,callback) {               User.populate( doc, { "path": "agencyAgencies.siteUsers" },callback);           },           function(doc,callback) {               Sector.populate( doc, { "path": "agencyAgencies.siteSectors" },callback);           },           function(doc,callback) {               Building.populate( doc, { "path": "agencyAgencies.siteBuildings" },callback);           },           function(doc,callback) {               Module.populate( doc, { "path": { "agencyAgencies.siteModules" },callback);           }        ],function(err,res) {           if (err) {               deffered.reject(err);           } else {               Floor.populate(res,{ "path": "agencyAgencies.siteBuildings.buildingFloors" },function(err,res) {                   if (err) {                       deffered.reject(err);                   } else {                       deffered.resolve(res);                   }               });           }        });    }});

My use of "async.waterfall" notwithstanding, just trying to avoid "indentation creep" by each embedded iteration.

So as you can see it is actually necessary to call .populate() in this way for each specific model type. Also there was the necessary call inside the "again nested" "Building" model reference also needs to be called after "that" document is populated. So it is that "call stack" that is part of the nested populate problem you mentioned earlier.

With such heavy referencing you might do well to consider schema redesigns and/or embedding a lot of this data. It looks very "relational" and as such you probably are not getting the full MongoDB benefit by doing it this way.


@Neil Lunn's answer is correct, I just wanted to shine some light on co incase you were interested in cleaning up your code a bit:

co(function* {  var agency = yield Agency.findOne({'agencyUsers': idUser}, '_id agencyUsers agencySites agencyDevices')    .populate(      { 'path': 'agencyUsers', 'match': { 'actif': true }, 'select': '_id userPin' },      { 'path': 'agencySites', 'match': { 'actif': true }, 'select': '_id siteName siteBuildings siteSectors siteUsers' },      { 'path': 'agencyDevices', 'match': { 'actif': true }, 'select': '_id deviceMac' })  agency = yield Agency.populate(agency, 'agencyAgencies.siteAgencies')  agency = yield User.populate(agency, 'agencyAgencies.siteUsers')  agency = yield Sector.populate(agency, 'agencyAgencies.siteSectors')  agency = yield Building.populate(agency, 'agencyAgencies.siteBuildings')  agency = yield Module.populate(agency, 'agencyAgencies.siteModules')  return agency}).then(agency => {   Floor.populate(agency, 'agencyAgencies.siteBuildings.buildingFloors', function (err, res) {     if (err)       deferred.reject(err)     deferred.resolve(agency)   })}).catch(deferred.reject)


Since version ^4.1.x, deep population is actually a thing in mongoose, so doing something like this is valid :-

Post.find({}).populate({   path: 'user',   select: 'id name',   populate: {      path: 'friends',      select: 'id name,'      populate: {          path: 'friends',          select: 'id name',      }   }})

populate can take either an object or an array of objects as a value, which means you can populate sibling attributes that reference different collections. for more information, check this post: http://frontendcollisionblog.com/mongodb/2016/01/24/mongoose-populate.html