mongoose icon indicating copy to clipboard operation
mongoose copied to clipboard

virtual populate that relies on another virtual doesn't return populated values

Open foureight84 opened this issue 7 years ago • 5 comments


userSchema ({
    profile: { type: String, default: ''}
})

companySchema ({
    name: {type: String}
})

companyUserSchema ({
    _company: {type: Schema.Types.ObjectId, ref: 'Company'},
    _user: {type: Schema.Types.ObjectId, ref: 'User'},
    _team_members: [{type: Schema.Types.ObjectId, ref: 'User'}]
})

userSchema.virtual('_company_user', {
    ref: 'CompanyUser',
    localField: '_id',
    foreignField: '_user'
})

userSchema.virtual('team_members', {
    ref: 'User',
    localField: '_company_user._team_members'
    foreignField: '_id'
})

user.team_members returns an array of ObjectIds unpopulated.


user 
{
    profile: 'profile1',
    _company_user: {
        _company: <unpopulated random hash>
        _user: <random hash>
        _team_members: [<unpopulated random hash>]
    },
    team_members: [<unpopulated random hash>] //i was expecting this to be populated
}

foureight84 avatar Jul 05 '18 20:07 foureight84

Thanks for reporting, will investigate ASAP

vkarpov15 avatar Jul 07 '18 19:07 vkarpov15

We currently don't support this, will add for a future release. As a workaround, you need to use deep populate

const assert = require('assert');
const mongoose = require('mongoose');
mongoose.set('debug', true);

const GITHUB_ISSUE = `gh6678`;
const connectionString = `mongodb://localhost:27017/${ GITHUB_ISSUE }`;
const { Schema } = mongoose;

run().then(() => console.log('done')).catch(error => console.error(error.stack));

async function run() {
  await mongoose.connect(connectionString);
  await mongoose.connection.dropDatabase();

  const userSchema = new mongoose.Schema({
    profile: { type: String, default: ''}
  });

  const companySchema = new mongoose.Schema({
    name: {type: String}
  });

  const companyUserSchema = new mongoose.Schema({
    _company: {type: Schema.Types.ObjectId, ref: 'Company'},
    _user: {type: Schema.Types.ObjectId, ref: 'User'},
    _team_members: [{type: Schema.Types.ObjectId, ref: 'User'}]
  });

  companyUserSchema.virtual('team_members', {
    ref: 'User',
    localField: '_team_members',
    foreignField: '_id'
  });

  userSchema.virtual('_company_user', {
    ref: 'CompanyUser',
    localField: '_id',
    foreignField: '_user'
  });

  const Company = mongoose.model('Company', companySchema);
  const User = mongoose.model('User', userSchema);
  const CompanyUser = mongoose.model('CompanyUser', companyUserSchema);

  const company = await Company.create({ name: 'Acme' });
  const user = await User.create({ profile: 'foo' });
  const mapping = await CompanyUser.create({ _company: company._id, _user: user._id, _team_members: [user._id] });

  //const doc = await User.findOne().populate('_company_user team_members');
  const doc = await User.findOne().populate({
    path: '_company_user',
    populate: { path: 'team_members' }
  });
  console.log(require('util').inspect(doc.toObject({ virtuals: true }), { depth: 10 }));
}

vkarpov15 avatar Jul 10 '18 14:07 vkarpov15

when the feature will rollout?

SwapnilSoni1999 avatar Dec 12 '20 07:12 SwapnilSoni1999

@SwapnilSoni1999 we don't have a plan to support this currently. There's a straightforward workaround so this isn't a priority.

vkarpov15 avatar Dec 13 '20 17:12 vkarpov15

const doc = await User.findOne().populate({ path: '_company_user', populate: { path: 'team_members' } });

Am just curious why in the deep nested populate, you did {...,populate: { path: 'team_members' } rather than {...,populate: { path: '_team_members' } , with the _team_members just coming directly from the CompanyUser model field

hane-smitter avatar Jan 25 '22 15:01 hane-smitter