Mongoose: populate not running getter on localField

Created on 25 Jun 2018  路  5Comments  路  Source: Automattic/mongoose

Do you want to request a feature or report a bug?
maybe bug

What is the current behavior?
I'm migrating from parse-server to mongoose. Parser server save pointer in this shape Model$_id. So in schema definition I have set a getter to transform Model$xxxxx => xxxxx.
But when using this field to populate, the getter does not run (so mongoose look for _id by Model$xxxxx, not xxxxx as expected)

I have tried to add a new virtual field as opose to using getter and use new virtual field to populate, it also not work.

If the current behavior is a bug, please provide the steps to reproduce.

What is the expected behavior?

Please mention your node.js, mongoose and MongoDB version.
[email protected]

confirmed-bug

Most helpful comment

@trungtin I think I was able to duplicate this with the following code:

6618.js

#!/usr/bin/env node
'use strict';

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/gh-6618');
const conn = mongoose.connection;
const Schema = mongoose.Schema;

const userSchema = new Schema({
  name: String
});

const schema = new Schema({
  referrer: {
    type: String,
    get: function(val) {
      return val.slice(6);
    }
  }
});

schema.virtual('referrerUser', {
  ref: 'user',
  localField: 'referrer',
  foreignField: '_id',
  justOne: false
});

const User = mongoose.model('user', userSchema);
const Test = mongoose.model('test', schema);

const user = new User({ name: 'billy' });
const test = new Test({ referrer: 'Model$' + user.id });

async function run() {
  await conn.dropDatabase();
  await user.save();
  await test.save();
  let pop = await Test.findOne().populate('referrerUser');
  console.log(pop);
  return conn.close();
}

run().catch((e) => { console.error(e.message); return conn.close();});

Output:

issues: ./6618.js
Cast to ObjectId failed for value "Model$5b358cd8dee63f0ab27fa77a" at path "_id" for model "user"
issues:

Is this the behavior you are describing?

All 5 comments

@trungtin We're happy to help. Can you provide a code sample that shows your schema, model creation, doc insertion, and relevant queries? This will make it easier to identify the issue. Thanks!

@lineus Thank you. here are more information
User table

_id | referrer
--- | ---
xxx | undefined
yyy | _User$xxx

const userSchema = new Schema({
  _id: String,
  referrer: {
    type: String,
    get: this.referrer && this.referrer.slice(6)
  }
})

userSchema.virtual('referrerUser', {
  ref: '_User',
  localField: 'referrer', <= it search _User table for for `_User$xxx` not `xxx`
  foreignField: '_id',
  justOne: false
});

work around:

userSchema.virtual('referrerUser', {
  ref: '_User',
  localField: (doc) => {
     if (doc.referrer) {
       doc.referrer_temp = doc.referrer.slice(6)
     }
     return 'referrer_temp'
   },
  foreignField: '_id',
  justOne: false
});

@trungtin I think I was able to duplicate this with the following code:

6618.js

#!/usr/bin/env node
'use strict';

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/gh-6618');
const conn = mongoose.connection;
const Schema = mongoose.Schema;

const userSchema = new Schema({
  name: String
});

const schema = new Schema({
  referrer: {
    type: String,
    get: function(val) {
      return val.slice(6);
    }
  }
});

schema.virtual('referrerUser', {
  ref: 'user',
  localField: 'referrer',
  foreignField: '_id',
  justOne: false
});

const User = mongoose.model('user', userSchema);
const Test = mongoose.model('test', schema);

const user = new User({ name: 'billy' });
const test = new Test({ referrer: 'Model$' + user.id });

async function run() {
  await conn.dropDatabase();
  await user.save();
  await test.save();
  let pop = await Test.findOne().populate('referrerUser');
  console.log(pop);
  return conn.close();
}

run().catch((e) => { console.error(e.message); return conn.close();});

Output:

issues: ./6618.js
Cast to ObjectId failed for value "Model$5b358cd8dee63f0ab27fa77a" at path "_id" for model "user"
issues:

Is this the behavior you are describing?

Re: #6844, this change was unintentionally backwards breaking for some people, so we're going to have to put this behavior behind an option. You will need to set the getters option to true to run getters on your populate's localField in Mongoose 5.2.10:

schema.virtual('foo', {
  ref: 'Bar',
  localField: 'baz',
  foreignField: '_id',
  getters: true // Opt in to calling getters on 'baz'
});
await BlogPost.findOne().populate({ path: 'author', options: { getters: true } });

totally agree with this. Thank you

Was this page helpful?
0 / 5 - 0 ratings