Skip to main content
Version: Next

Many to Many

Many to Many Relationship

Many-to-many relations are slightly more complicated than hasOne and hasMany relationships. An example of a many-to-many relationship is a user that has many roles and those roles are also shared by other users in the application. For example, a user may be assigned the role of "Author" and "Editor"; however, those roles may also be assigned to other users as well. So, a user has many roles and a role has many users.

Collection structure

To define this relationship, three database collections are needed: users, roles, and roleUser. The roleUser collection is derived from the alphabetical order of the related model names and contains userId and roleId keys. This collection is used as an intermediate collection linking the users and roles.

Remember, since a role can belong to many users, we cannot simply place a userId column on the roles collection. This would mean that a role could only belong to a single user. In order to provide support for roles being assigned to multiple users, the roleUser collection is needed. We can summarize the relationship's collection structure like so:

users
_id - ObjectId
name - string

roles
_id - ObjectId
name - string

roleUser
_id - ObjectId
userId - ObjectId
roleId - ObjectId

Model Structure

Many-to-many relationships are defined by writing a method that returns the result of the belongsToMany method. For example, let's define a roles method on our User model. The first argument passed to this method is the name of the related model class:

import { Mongoloquent } from "mongoloquent";
import Role from "./yourPath/Role";
import RoleUser from "./yourPath/RoleUser";

class User extends Mongoloquent {
static collection = "users";

static roles() {
return this.belongsToMany(Role, RoleUser, "userId", "roleId");
}
}

Once the relationship is defined, you may access the user's roles using the roles dynamic relationship property:

import User from "./yourPath/User";

const user = await User.with("roles").find("65ab7e3d05d58a1ad246ee87");

// your relationship data can accessed in the data property
console.log(user.data);

Defining the Inverse of the Relationship

To define the "inverse" of a many-to-many relationship, you should define a method on the related model which also returns the result of the belongsToMany method. To complete our user / role example, let's define the users method on the Role model:

import { Mongoloquent } from "mongoloquent";
import User from "./yourPath/User";
import RoleUser from "./yourPath/RoleUser";

class Role extends Mongoloquent {
static collection = "roles";

static users() {
return this.belongsToMany(User, RoleUser, "roleId", "userId");
}
}

Monggoloquent can also select or exclude certain related model keys

import User from "./yourPath/User";

// with select keys
await User.with("roles", {
select: ["name"],
}).find("65ab7e3d05d58a1ad246ee87");

// with exclude keys
await User.with("roles", {
exclude: ["name"],
}).find("65ab7e3d05d58a1ad246ee87");

Support us

Mongoloquent is an MIT-licensed open source project. It can grow thanks to the support by these awesome people. If you'd like to join them, please read more here.

Sponsors

_

Partners