Closed makinde closed 4 years ago
@vkarpov15 Here's a broader description of what I'm facing.
I have a model with a virtual populate field. It also has a getter. When I do a lean query, I want that getter to be run and the corresponding value returned. Here's the relevant code snippet.
const firstJoined = schema.virtual('firstJoined', {
ref: 'OrgMembership',
localField: '_id',
foreignField: 'profile',
justOne: true,
match: {
role: { $in: PARTICIPATING_ROLES },
},
options: {
perDocumentLimit: 1,
sort: 'validFrom',
},
});
// Add getter in this way, see: https://github.com/Automattic/mongoose/issues/5835
// to be fixed in mongoose 6.
firstJoined.getters.unshift(function firstJoinedGetter() {
if (!this.firstJoined) return undefined;
const { validFrom } = this.firstJoined;
// If the earliest membership is in the future, you haven't joined yet!
if (validFrom && validFrom > new Date()) return undefined;
// Okay, use validFrom. If it is null, then treat them as joining when this
// profile was created for them. If that wasn't fetched, give up.
return validFrom || this.createdAt || undefined;
});
This lies somewhere between mongoose-lean-virtuals
and mongoose-lean-getters
. I chose this lib since this is technically about a getter being executed.
My first stab was to run all the getters on virtual fields, but that was breaking those values being returned that were populated but did not have getters. So I'm trying to detect if there are getters added by the developer by checking the length of virtualType.getters.length > 1
. I'm not sure this is totally the right approach. If you give me a pointer or two, I can make changes that you suggest so this can land.
Ah, figured out that this PR (https://github.com/Automattic/mongoose/pull/8775) needs to land to avoid the hacks of only applying when there is more than one getter.
@makinde I think this belongs in mongoose-lean-virtuals, because Mongoose treats virtuals as a separate concept from getters. Plus it is much easier to make these changes in mongoose-lean-virtuals, see: https://github.com/vkarpov15/mongoose-lean-virtuals/commit/d240f3c1ce404ce7bf87a0c886a47fa38cb50b7b .
With your changes in Automattic/mongoose#8775, I can confirm the below script works:
'use strict';
const mongoose = require('mongoose');
mongoose.set('useFindAndModify', false);
const { Schema } = mongoose;
run().catch(err => console.log(err));
async function run() {
await mongoose.connect('mongodb://localhost:27017/test', {
useNewUrlParser: true,
useUnifiedTopology: true
});
await mongoose.connection.dropDatabase();
const childSchema = Schema({ name: String, parentId: 'ObjectId' });
const Child = mongoose.model('Child', childSchema);
const parentSchema = Schema({ name: String });
parentSchema.virtual('children', {
ref: 'Child',
localField: '_id',
foreignField: 'parentId'
});
parentSchema.virtual('children').getters.unshift(function(v) {
console.log('Children virtual getter called!');
return v;
});
parentSchema.plugin(require('mongoose-lean-virtuals'));
const Parent = mongoose.model('Parent', parentSchema);
const p = await Parent.create({ name: 'Darth Vader' });
const c = await Child.create({ name: 'Luke Skywalker', parentId: p });
const doc = await Parent.findOne().populate('children').lean({ virtuals: true });
console.log(doc);
}
Thanks for looking at this. Moving the discussion to the thread on vkarpov15/mongoose-lean-virtuals@d240f3c
Previously, only getters on "real" schema fields we added. Leaving off getter for virtual and virtually populated fields. This adds those back.