mongodb-js / mongoose-autopopulate

Always populate() certain fields in your mongoose schemas
Apache License 2.0
221 stars 36 forks source link

Autopopulate doesn't work on virtuals #39

Closed TKasperczyk closed 6 years ago

TKasperczyk commented 6 years ago

The code below defines two mongoose schemas. The client schema has a virtual which is supposed to be autopopulated by the mongoose-autopopulate plugin. Unfortunately it is not - I need to manually add .populate('service.net.radio.accessPoint') to the find query in order to get it populated. What am I doing wrong? I didn't find any documentation related to handling virtuals in mongoose-autopopulate. There was a pull request and this feature was supposedly implemented. Is it currently broken?

const clientSchema = new mongoose.Schema({
    service: {
        net: {
            radio: {
                accessPointName: {
                    type: String,
                    required: false,
                },
                ip: {
                    match: /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/,
                    required: false,
                    type: String,
                    trim: true,
                }
            }
        }
    }
}, {
    toJSON: {
        virtuals: true,
    },
    toObject: {
        virtuals: true,
    },
});
clientSchema.virtual(`service.net.radio.accessPoint`, {
    ref: `settings.accessPoint`,
    localField: `service.net.radio.accessPointName`,
    foreignField: `name`,
    autopopulate: true
});
clientSchema.plugin(require(`mongoose-autopopulate`));
return mongoose.model(`client`, clientSchema);

/*...*/

const accessPointSchema = new mongoose.Schema({
    name: {
        required: true,
        type: String,
        trim: true,
        unique: true
    }
}, {
    collection: `settings.accessPoints`
});
return mongoose.model(`settings.accessPoint`, accessPointSchema);
vkarpov15 commented 6 years ago

Thanks for reporting, I opened up an issue in the mongoose repo so we can track this in mongoose's milestones.

vkarpov15 commented 6 years ago

I can't repro this issue, the below code works correctly with mongoose 5.1.2 and mongoose-autopopulate 0.7.0. Can you modify the below script to reproduce your issue and clarify your versions of mongoose and mongoose-autopopulate?

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

const GITHUB_ISSUE = `gh6522`;
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 clientSchema = new mongoose.Schema({
    service: {
      net: {
        radio: { 
          accessPointName: String
        } 
      }
    }
  }, { toJSON: { virtuals: true }, toObject: { virtuals: true } });

  clientSchema.virtual(`service.net.radio.accessPoint`, {
    ref: `settings.accessPoint`,
    localField: `service.net.radio.accessPointName`,
    foreignField: `name`,
    autopopulate: true
  });
  clientSchema.plugin(require(`mongoose-autopopulate`));
  const Client = mongoose.model(`client`, clientSchema);

  const accessPointSchema = new mongoose.Schema({
    name: {
      required: true,
      type: String,
      trim: true
    }
  }, { collection: `settings.accessPoints` });
  const AccessPoint = mongoose.model(`settings.accessPoint`, accessPointSchema);

  await AccessPoint.create({ name: 'test' });
  await Client.create({ service: { net: { radio: { accessPointName: 'test' } } } });

  const doc = await Client.findOne();
  console.log(doc.toObject().service.net.radio.accessPoint);
}

Output:

$ node gh-6522.js 
Mongoose: settings.accessPoints.insertOne({ _id: ObjectId("5b0981d32ef452707b660567"), name: 'test', __v: 0 })
Mongoose: clients.insertOne({ service: { net: { radio: { accessPointName: 'test' } } }, _id: ObjectId("5b0981d32ef452707b660568"), __v: 0 })
Mongoose: clients.findOne({}, { fields: {} })
Mongoose: settings.accessPoints.find({ name: { '$in': [ 'test' ] } }, { maxDepth: 10, _depth: 1, fields: {} })
[ { _id: 5b0981d32ef452707b660567, name: 'test', __v: 0 } ]
done
^C
$ 
TKasperczyk commented 6 years ago

It turned out my find queries had .lean() appended to them. I didn't expect that it might stop mongoose-autopopulate from triggering. Without lean() it works as expected. Sorry for that, the issue can be closed.