Relations
Relations work a little differently in mongoose
when compared to other relational ORMs such as sequelize
or typescript
. You can read more about relations (references) in mongoose
here
There are multiple ways to set of references in mongoose
there are intended as starting point.
Filtering on references is not supported by mongoose.
One to Many/Many To One Example
To set up a one to many/many to one relationship in mongoose you will store a reference in your document
For example lets add sub tasks to our todo items by storing a todoItem
ref on our subTask
- TodoItemEntity
- SubTaskEntity
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { SchemaTypes, Types, Document } from 'mongoose';
@Schema({ timestamps: true })
export class TodoItemEntity extends Document {
@Prop({ required: true })
title!: string;
@Prop()
description?: string;
@Prop({ required: true })
completed!: boolean;
@Prop({ default: Date.now })
createdAt!: Date;
@Prop({ default: Date.now })
updatedAt!: Date;
@Prop({ default: 0 })
priority!: number;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
}
export const TodoItemEntitySchema = SchemaFactory.createForClass(TodoItemEntity);
TodoItemEntitySchema.virtual('subTasks', {
ref: 'SubTaskEntity',
localField: '_id',
foreignField: 'todoItem',
});
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { SchemaTypes, Types, Document } from 'mongoose';
@Schema({ timestamps: true })
export class SubTaskEntity extends Document {
@Prop({ required: true })
title!: string;
@Prop()
description?: string;
@Prop({ required: true })
completed!: boolean;
@Prop({ type: SchemaTypes.ObjectId, ref: 'TodoItemEntity', required: true })
todoItem!: Types.ObjectId;
@Prop()
createdAt!: Date;
@Prop()
updatedAt!: Date;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
}
export const SubTaskEntitySchema = SchemaFactory.createForClass(SubTaskEntity);
Now that we have the relationship defined we can add the @Relation
and @CursorConnection
to our DTOs
- TodoItemDTO
- SubTaskDTO
import { FilterableField, IDField, KeySet, CursorConnection } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
import { SubTaskDTO } from '../../sub-task/dto/sub-task.dto';
@ObjectType('TodoItem')
@KeySet(['id'])
// disable the remove because mongoose does not support removing a virtual
@CursorConnection('subTasks', () => SubTaskDTO, { update: { enabled: true } })
export class TodoItemDTO {
@IDField(() => ID)
id!: string;
@FilterableField()
title!: string;
@FilterableField({ nullable: true })
description?: string;
@FilterableField()
completed!: boolean;
@FilterableField(() => GraphQLISODateTime)
createdAt!: Date;
@FilterableField(() => GraphQLISODateTime)
updatedAt!: Date;
@Field()
age!: number;
@FilterableField()
priority!: number;
@FilterableField({ nullable: true })
createdBy?: string;
@FilterableField({ nullable: true })
updatedBy?: string;
}
import { FilterableField, IDField, KeySet, Relation } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';
@ObjectType('SubTask')
@KeySet(['id'])
// disable the remove because a sub task cannot exist without a todoitem
@Relation('todoItem', () => TodoItemDTO, { update: { enabled: true } })
export class SubTaskDTO {
@IDField(() => ID)
id!: string;
@FilterableField()
title!: string;
@FilterableField({ nullable: true })
description?: string;
@FilterableField()
completed!: boolean;
@FilterableField(() => GraphQLISODateTime)
createdAt!: Date;
@FilterableField(() => GraphQLISODateTime)
updatedAt!: Date;
@FilterableField({ nullable: true })
createdBy?: string;
@FilterableField({ nullable: true })
updatedBy?: string;
}
Many To Many Example
In this example we'll add tags
to todoItems
by storing an array of tag
references on the todoItems
.
- TodoItemEntity
- TagEntity
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { SchemaTypes, Types, Document } from 'mongoose';
@Schema({ timestamps: true })
export class TodoItemEntity extends Document {
@Prop({ required: true })
title!: string;
@Prop()
description?: string;
@Prop({ required: true })
completed!: boolean;
@Prop({ default: Date.now })
createdAt!: Date;
@Prop({ default: Date.now })
updatedAt!: Date;
// notice the brackets around the prop options
@Prop([{ type: SchemaTypes.ObjectId, ref: 'TagEntity' }])
tags!: Types.ObjectId[];
@Prop({ default: 0 })
priority!: number;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
}
export const TodoItemEntitySchema = SchemaFactory.createForClass(TodoItemEntity);
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
@Schema({ timestamps: true })
export class TagEntity extends Document {
@Prop({ required: true })
name!: string;
@Prop()
createdAt!: Date;
@Prop()
updatedAt!: Date;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
}
export const TagEntitySchema = SchemaFactory.createForClass(TagEntity);
TagEntitySchema.virtual('todoItems', {
ref: 'TodoItemEntity',
localField: '_id',
foreignField: 'tags',
});
Now that we have the relationship defined we can add the @CursorConnection
to our DTOS
- TodoItemDTO
- TagDTO
import { FilterableField, IDField, KeySet, CursorConnection } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
import { TagDTO } from '../../tag/dto/tag.dto';
@ObjectType('TodoItem')
@KeySet(['id'])
@CursorConnection('tags', () => TagDTO)
export class TodoItemDTO {
@IDField(() => ID)
id!: string;
@FilterableField()
title!: string;
@FilterableField({ nullable: true })
description?: string;
@FilterableField()
completed!: boolean;
@FilterableField(() => GraphQLISODateTime)
createdAt!: Date;
@FilterableField(() => GraphQLISODateTime)
updatedAt!: Date;
@Field()
age!: number;
@FilterableField()
priority!: number;
@FilterableField({ nullable: true })
createdBy?: string;
@FilterableField({ nullable: true })
updatedBy?: string;
}
import { FilterableField, IDField, KeySet, CursorConnection } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';
@ObjectType('Tag')
@KeySet(['id'])
// disable update and remove since it is a virtual in the entity
@CursorConnection('todoItems', () => TodoItemDTO)
export class TagDTO {
@IDField(() => ID)
id!: string;
@FilterableField()
name!: string;
@FilterableField(() => GraphQLISODateTime)
createdAt!: Date;
@FilterableField(() => GraphQLISODateTime)
updatedAt!: Date;
@FilterableField({ nullable: true })
createdBy?: string;
@FilterableField({ nullable: true })
updatedBy?: string;
}