Utilizing Typegoose Schemas for Implementing Complex Many-to-Many Connections

Currently, I am developing a backend using NestJs and Typegoose, with the following models:

DEPARTMENT

@modelOptions({ schemaOptions: { collection: 'user_department', toJSON: { virtuals: true }, toObject: { virtuals: true }, id: false } })
export class Department {

    @prop({ required: true })
    _id: mongoose.Types.ObjectId;

    @prop({ required: true })
    name: string;

    @prop({ ref: () => User, type: String })
    public supervisors: Ref<User>[];

    members: User[];

    static paginate: PaginateMethod<Department>;
}

USER

@modelOptions({ schemaOptions: { collection: 'user' } })
export class User {

    @prop({ required: true, type: String })
    _id: string;

    @prop({ required: true })
    userName: string;

    @prop({ required: true })
    firstName: string;

    @prop({ required: true })
    lastName: string;

    [...]

    @prop({ ref: () => Department, default: [] })
    memberOfDepartments?: Ref<Department>[];

    static paginate: PaginateMethod<User>;
}

Regarding the relationship between users and departments, one user can be associated with multiple departments, and one department can have many members (users). To handle this scenario efficiently, I opted for one-way embedding as mentioned in this resource: Two Way Embedding vs. One Way Embedding in MongoDB (Many-To-Many). This is why the User model contains the "memberOfDepartments" array, while the Department model does not store a Member-array.

The first question arises when querying the members of a Department object by looking for users where the department._id is present in the "memberOfDepartments" array. I attempted various methods like virtual populate, but encountered issues primarily related to handling one-to-many relationships effectively within Typegoose.

The second question pertains to managing the deletion of a department and subsequently removing references to that department from associated users. While there are resources available for MongoDB and Mongoose documentation, adapting these concepts to work seamlessly with Typegoose poses challenges due to limited documentation.

Answer №1

Discovering this solution was quite challenging, but hopefully it will benefit others in a similar situation. There seems to be a lack of comprehensive documentation on basic concepts such as deleting references to an object when the object itself is deleted. This information is crucial for anyone working with references, yet it is not clearly outlined in existing documentation for typegoose, mongoose, or mongodb.

Answer 1:

@prop({
    ref: () => User,
    foreignField: 'memberOfDepartments', 
    localField: '_id',
    justOne: false
})
public members: Ref<User>[];

The correct way to define the virtual, as mentioned in the question, is shown above. However, what may not be immediately obvious is the need to explicitly call

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

as demonstrated below:

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

Failing to do so will result in the 'members' property not being visible at all. It's important to populate the virtuals to ensure that the desired fields are included in the response.

Answer 2:

After conducting research, I discovered that using a pre-hook is the most effective approach in handling this issue. By defining a function to be executed before a specific operation (or after, if needed), we can address the problem of deleting references prior to deleting the document itself.

@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 {
[...]   
}

It's worth noting that many solutions I came across used the 'remove' method, which has been deprecated. Instead, it's advisable to use 'deleteOne()' for better functionality. Utilizing 'const depId = this.getFilter()["_id"];' allows access to the document ID being targeted by the delete operation.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transfer text between Angular components

Here is the landing-HTML page that I have: <div class="container"> <div> <mat-radio-group class="selected-type" [(ngModel)]="selectedType" (change)="radioChange()"> <p class="question">Which movie report would you like ...

What is the best way to retrieve a specific child element from a MongoDB collection using a specific key?

I'm dealing with a group of users structured like this: { _id: myid, name : 'blabla', ia : { [0]-> 'an id', [1]-> 'a second id' } } My goal is to retrieve only the first id in the ia section, s ...

Understanding the inner workings of a Mongoose model without the need for

server.js process.env.NODE_ENV=process.env.NODE_ENV || 'development'; var mongoose=require('./config/mongoose'); express=require('./config/express'); var db=mongoose(); var app=express(); app.listen(3000,function(){ ...

This TypeScript issue in ServiceStack ignores namespaces, leading to duplicate declarations

Currently, I am exploring NativeScript and Angular2 with ServiceStack in C# as the backend. To develop the application, I utilized the typescript-ref command to generate TypeScript classes for use with the JsonServiceClient in ServiceStack: >typescrip ...

Can a type be established that references a type parameter from a different line?

Exploring the Promise type with an illustration: interface Promise<T> { then<TResult1 = T, TResult2 = never>( onfulfilled?: | ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ...

What steps can I take to avoid my application from shutting down suddenly?

When my application receives a URI for a MongoDB database, it creates an instance of MongoClient. const { MongoClient } = require("mongodb"); const config = require("../config"); const uri = config.dbAdress; const client = new MongoCl ...

Mongos and the Power of AutoScaling

Our current setup involves a group of application servers operating under an autoscaling configuration in AWS. Each of these application servers runs its own instance of mongos, allowing the application to connect to MongoDB cluster via localhost. I recen ...

When importing a React Component with styling into the pages folder, it fails to function properly

I created a component in my components directory with custom styling: // import Link from "next/link"; import {Link} from "react-scroll" export default function Navbar() { return ( <div className="fixed w-full h-[79px] fle ...

What is the best method for implementing a schema validation in MongoDB from the Mongo Shell to ensure that an array of strings is required?

I need help with determining the correct validation type for the last item in my list of required properties in Mongo Shell. The imageIDs property is giving me trouble as it is an array of strings. How can I ensure that its type is specifically an array ...

I am unable to retrieve a list of documents from a MongoDB collection using the MongoClient library in Node.js

I have successfully connected to my database from node.js using the MongoClient. I am able to perform write, update, and delete operations on documents in a collection. However, I am facing an issue when trying to retrieve data from the database. Below is ...

Oops! The Route.get() function in Node.js is throwing an error because it's expecting a callback function, but it received

Currently, I am learning how to create a web music admin panel where users can upload MP3 files. However, I have encountered the following errors: Error: Route.get() requires a callback function but received an [object Undefined] at Route. [as get] (C:&bso ...

Difficulty displaying SVGs in VueJS when using Typescript

I'm facing a strange issue that seems like it should be easy to solve. When using Vue with JavaScript as CDN, everything works perfectly (just like it does in Angular or any other framework). <img src="/assets/images/icons/myicoin.svg> However ...

"Two important components in Meteor are the Meteor.Collection and Meteor.Collection

Could you explain the difference between Meteor.Collection and Meteor.Collection.Cursor and how they are related? Also, does: new Meteor.Collection("name") create a collection in MONGODB with the name parameter? ...

Searching for geospatial indexes on Azure CosmosDB (DocumentDB) with the Mongo API does not yield results

My Azure CosmosDB contains a collection named restaurants with a field geoJSON that specifies the location of each restaurant in geoJSON format. I am using the MongoDB API for accessing this data. Upon logging into the DB through the mongo shell, I can vi ...

Is it possible for a component to have multiple templates that can be switched based on a parameter?

Scenario My task is to develop a component that fetches content from a database and displays it on the page. There needs to be two components working together to create a single page, following a specific component tree structure. DataList - receives ful ...

Troubleshooting: MongoDB/mongoose post save hook does not execute

My current setup involves the following model/schema: const InvitationSchema = new Schema({ inviter: {type: mongoose.Schema.Types.ObjectId, ref: 'Account', required: true}, organisation: {type: mongoose.Schema.Types.ObjectId, ref: 'Orga ...

Angular 5 TypeScript's key value data structure

I am in search of a way to replicate the functionality of a key/value dictionary in Python. My project is utilizing Angular 5 typescript, and I am still exploring the available data types. The ever-changing nature of Angular has made it challenging due to ...

Converting a string to ObjectId through JSON parsing

I am trying to create a dynamic where clause that includes an ObjectId. However, I am struggling to convert a string into an ObjectId using JSON.parse. Here is the code snippet that I am attempting to get working... var className = 'science'; v ...

What are some ways to enhance the speed of Mongo spark writes?

Struggling with performance issues while using the Mongo spark connector for writing data. I am attempting to write 0.3 million records in string and hex binary format (4kB) through the Mongo spark connector. spark = create_c3s_spark_session(app_name, spa ...

Issue regarding custom CSS implementation in Angular project

Being a French speaker, I apologize in advance for any mistakes I might make. I am also fairly new to Angular, so if you could provide detailed explanations in your responses, it would be greatly appreciated. Thank you. I am trying to import a custom CSS ...