Search code examples
node.jsmongoosemongoose-schemamongoose-populate

Mongoose cannot do deep populate


I've:

  1. User model (from UserSchema)
  2. Specialty model (from SpecialtySchema)
  3. Session model (from SessionSchema)

User has reference field specialities :

specialities: [{ type: Schema.Types.ObjectId, ref: 'Specialty' }]

Session has reference field provider :

provider: { type: Schema.Types.ObjectId, ref: 'Session' }

I want to get list of sessions with populated provider (User) and deep populated specialities over provider.

I'm doing following:

Session
  .find()
  .populate({
    path: 'provider',
    model: 'User',
    populate: {
      path: 'specialities',
      model: 'Specialty',
    }
  })

However the results always return an array of IDS (when I try to access them by provider.specialities instead of the populated version.

Is specialities a keyword?

I don't know why it's like not populating at all.


Solution

  • Quick solution is to use mongoose-deep-populate

    In every schema attach it as plugin:

    const deepPopulate = require('mongoose-deep-populate')(mongoose);
    SessionSchema.plugin(deepPopulate, {
      whitelist: ['provider', 'provider.specialities']
    });
    

    and then use it like this:

    const Session = mongoose.model('Session');
    cons user = await Session.findById(id here)
                             .deepPopulate(['provider', 'provider.specialities']);
    



    Due to possible performance issues I don't recommend to do deep populate.

    Better to try to re-think Your data.

    Also I cannot imagine use case for it.

    Maybe You just want to show user list with specialities?

    What about just to do 2 api calls from frontend:

    1st for taking sessions and taking provider ids (ex.: GET /sessions), 
    
    2nd for taking users with populated specialities (ex.: GET /users?ids=first-id,second-id...)
    

    or:

    let sessions = await Session.find();
    let providerIds = sessions.map(session => session.provider);
    let users = await User.find({_id: providerIds}).populate('specialities');
    
    sessions = sessions.map(session => {
      session.provider = users.find(user => user._id == session.provider);
      return session;
    });