Typegoose Models and Many to Many Relationships Typegoose Models and Many to Many Relationships mongoose mongoose

Typegoose Models and Many to Many Relationships


So, this was not easy to find out, hope this answer helps others. I still think there is the need to document more of the basic stuff - like deleting the references to an object when the object gets deleted. Like, anyone with references will need this, yet not in any documentation (typegoose, mongoose, mongodb) is given a complete example.

Answer 1:

@prop({    ref: () => User,    foreignField: 'memberOfDepartments',     localField: '_id', // compare this to the foreign document's value defined in "foreignField"    justOne: false})public members: Ref<User>[];

This is, as it is in the question, the correct way to define the virtual. But what I did wrong and I think is not so obvious: I had to call

.populate({ path: 'members', model: User })

explicitly as in

 const res = await this.departmentModel            .findById(id)            .populate({ path: 'supervisors', model: User })            .populate({ path: 'members', model: User })            .lean()            .exec();

If you don't do this, you won't see the property members at all. I had problems with this because, if you do it on a reference field like supervisors, you get at least an array ob objectIds. But if you don't pupulate the virtuals, you get no members-field back at all.

Answer 2:My research lead me to the conclusion that the best solution tho this is to use a pre-hook. Basically you can define a function, that gets called before (if you want after, use a post-hook) a specific operation gets executed. In my case, the operation is "delete", because I want to delete the references before i want to delete the document itself.You can define a pre-hook in typegoose with this decorator, just put it in front of your model:

@pre<Department>('deleteOne', function (next) {    const depId = this.getFilter()["_id"];    getModelForClass(User).updateMany(        { 'timeTrackingProfile.memberOfDepartments': depId },        { $pull: { 'timeTrackingProfile.memberOfDepartments': depId } },        { multi: true }    ).exec();    next();})export class Department {[...]   }

A lot of soultions found in my research used "remove", that gets called when you call f.e. departmentmodel.remove(). Do not use this, as remove() is deprecated. Use "deleteOne()" instead. With "const depId = this.getFilter()["_id"];" you are able to access the id of the document thats going to be deletet within the operation.