Virtuals
get & set
Mongoose offers developers the option to create virtual properties. As virtual properties, they are just 'calculated properties', meaning, there are no actual reads or writes to the database.
A virtual property can have a setter and a getter. ES6 Classes have get
& set
functions, which Mongoose uses for virtual property definitions (no Typegoose decorator can be used on them, because they are handled directly by Mongoose).
Do not confuse this get
& set
with @prop
's get
& set
No Typegoose decorator can be used on get
& set
functions, because they are directly handled by Mongoose.
Example:
class Name {
@prop()
public firstName?: string;
@prop()
public lastName?: string;
// this will create a virtual property called 'fullName'
public get fullName() {
return `${this.firstName} ${this.lastName}`;
}
public set fullName(full) {
const [firstName, lastName] = full.split(' ');
this.firstName = firstName;
this.lastName = lastName;
}
}
Resulting Document in MongoDB:
{
_id: ObjectId('<some long id>'),
firstName: 'Will',
lastName: 'Smith'
}
Difference between @prop's get & set and this get & set
This shows the difference between @prop
's get & set and this one
The difference between @prop
's and this one is simple, @prop
's get & set are actual properties that get saved to the database, only with a conversion layer.
The get & set of getter's & setter's are absolutely virtual.
Virtual Populate
Virtual-Populate is also supported by Typegoose
Options (look here for more details):
ref
: This is like a normal ref, use'ClassName'
when the classes are in different files RequiredforeignField
: Which property(on the ref-Class) to matchlocalField
against RequiredlocalField
: Which property(on the current-Class) to matchforeignField
against RequiredjustOne
: Return as One Document(true) or as Array(false) [Optional]count
: Return the number of Documents found instead of the actual Documents [Optional]options
: Extra Query Options [Optional]match
: Extra Match Options [Optional]
Example: for an array
class Kittens {
@prop({ required: true, ref: () => Cat ) }) // providing the type deferred
public parent: Ref<Cat>;
}
class Cat {
@prop({
ref: () => Kittens,
foreignField: 'parent', // compare this value to the document populate is called on
localField: '_id' // compare this to the foreign document's value defined in "foreignField"
})
public kittens: Ref<Kittens>[];
}
Example: for only one document
// I couldn't think of a real use case example
class Sub {
@prop({ required: true, ref: () => Parent }) // providing the type deferred
public parent: Ref<Parent>;
}
class Parent {
@prop({
ref: () => Sub,
foreignField: 'parent',
localField: '_id',
justOne: true // when this is not set to "true", mongoose will always return a Array
})
public one: Ref<Sub>;
}
Example (since typegoose 7.4): dynamic ref
, localField
and foreignField
class Sub {
@prop({ required: true })
public parentId!: mongoose.Types.ObjectId;
}
class Parent {
@prop({
ref: () => (doc: DocumentType<Parent>) => doc.from, // This need to be written this way, because since typegoose "7.1", deferred function are supported
foreignField: () => 'parentId', // no "doc" parameter provided here
localField: (doc: DocumentType<Parent>) => doc.local,
justOne: false
})
public nested?: Ref<Sub>[];
@prop({ required: true })
public local!: string;
@prop({ required: true })
public from!: string;
}
// later in some async code
const parent = await ParentModel.create({ local: '_id', from: getName(Sub) });
await SubModel.create({ parentId: parent._id });
Extra Notes
Why is my virtual not included in the output?
By default Mongoose doesn't output virtuals. To achieve this, you need to add toObject
and(/or) toJSON
to schemaOptions
in @modelOptions
.
Note: it can be set in @modelOptions
, but it can be set in getModelForClass
too (and in the doc.toJSON()
/doc.toObject()
functions).
Example:
class Sub {}
@modelOptions({
schemaOptions: {
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
})
class Parent {
@prop({
ref: Sub,
foreignField: 'parent',
localField: '_id',
justOne: true
})
public one: Ref<Sub>;
}
These options will be applied to all classes which inherit the class that got the options applied.