Open KamilTheDev opened 2 months ago
The key detail is that virtuals aren't included until they are accessed. In this case, exampleVirtual would only execute when you explicitly use clan.user.exampleVirtual;
, or when you execute a function that accesses exampleVirtual
, like toObject()
or toJSON()
. So you have a couple of options:
exampleVirtual
to return undefined
if !this.$populated('inventory')
exampleVirtual
when serializing using clan.toObject({ virtuals: { pathsToSkip: ['user.exampleVirtual'] } })
Do either of those help?
- Adjust
exampleVirtual
toreturn undefined
if!this.$populated('inventory')
Yes, if I were to check if inventory
is defined before proceeding in exampleVirtual
, there won't be an error. However, I don't know if I like this approach because it might cause silent errors if I make a mistake somewhere else where virtuals and inventory
was meant to be populated but didn't.
2. Skip
exampleVirtual
when serializing usingclan.toObject({ virtuals: { pathsToSkip: ['user.exampleVirtual'] } })
Trying user.exampleVirtual
didn't work, but I was able to do this with let clanObj = playerClan.toObject({ virtuals: { pathsToSkip: ["exampleVirtual"] } })
. However, this means I'd have to constantly maintain the list of virtuals the User
has. Doing virtuals: false
would be better, but of course it also removes the clanSchema
vrituals (membersCount
).
I wanted to add this behavior directly to the clanSchema
so I don't have to remember to do that every time I'm fetching a clan in my code, but it has no effect on User
virtuals (even trying to completely disable all virtuals).
I forgot to include in my example User
schema, that also includes toJSON: { virtuals: true }, toObject: { virtuals: true },
. If I removed that from User
schema, then the following would work, but otherwise this doesn't work (I think the User
schema settings are treated as more important):
const clanSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Please add a name value"],
unique: true,
},
members: [memberSchema],
},
{
timestamps: true,
toJSON: { virtuals: false },
toObject: { virtuals: false },
}
);
Anyhow, my ideal solution would be to directly specify in the clanSchema
or memberSchema
itself, that I do not want to have any User
virtuals included. This is so:
toObject
settings every time I fetch a clan in code.User
virtuals are not wanted.I would love to just be able to specify a virtuals option in the autopopulate settings like:
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
autopopulate: {
select: "name avatar avatarFrame",
maxDepth: 1,
virtuals: false,
},
},
How about autopulating user with lean
set? autopopulate: { select: "...", maxDepth: 1, lean: true }
. That will skip all virtuals on User.
The issue is that virtuals are computed properties defined on the User
model's prototype, so fully disabling them would require instantiating the document with a different constructor.
Another potential option is that Mongoose could support setting default toObject()
and toJSON()
options on a per-document basis. Something like const user = new User({ name: 'test' }, null, { toObject: { virtuals: false }, toJSON: { virtuals: false } })
, so plugins like mongoose-autopopulate could just instantiate the populated doc in a way that virtuals are excluded in toObject() by default. Would that help?
How can I specify that the auto-populated document should not include or calculate its virtual properties?
For example, I have a clan member schema that includes the
User
document. I only want to auto-populate the User'sname, avatar, avatarFrame
and nothing more.I also had the issue that since the
User
itself has autopopulate fields (inventory
for example), those were being included despite not meant to be. I fixed that by settingmaxDepth: 1
, not sure if that's the intended fix.Though that now means since the virtual fields are still trying to be included, it causes an error since it's only intended to be used when the
User
is fully autopopulated (not needed in this case.)Member schema:
User schema: