Reputation: 1965
I define a Person and Story schemas :
@Schema()
export class Person extends Document {
@Prop()
name: string;
}
export const PersonSchema = SchemaFactory.createForClass(Person);
@Schema()
export class Story extends Document {
@Prop()
title: string;
@Prop()
author: { type: MongooseSchema.Types.ObjectId , ref: 'Person' }
}
export const StorySchema = SchemaFactory.createForClass(Story);
In my service I implemented save and read functions:
async saveStory(){
const newPerson = new this.personModel();
newPerson.name = 'Ian Fleming';
await newPerson.save();
const newStory = new this.storyModel();
newStory.title = 'Casino Royale';
newStory.author = newPerson._id;
await newStory.save();
}
async readStory(){
const stories = await this.storyModel.
findOne({ title: 'Casino Royale' })
console.log('stories ',stories);
}
When I ran readStory() I get the following output:
stories {
_id: 5f135150e46fa5256a3a1339,
title: 'Casino Royale',
author: 5f135150e46fa5256a3a1338,
__v: 0
}
When I add a populate('author')
to my query then I get author as null:
stories {
_id: 5f135150e46fa5256a3a1339,
title: 'Casino Royale',
author: null,
__v: 0
}
How do I populate the author field with the referenced Person document ?
Upvotes: 16
Views: 27898
Reputation: 1
In my response, I defined booking and tasker
schemas. I used a UUID
(which generates a unique string) instead of an ObjectID
to override the default ObjectID
of each MongoDB document. Due to the taskerID
property, I used the type as string
instead of Types.ObjectId
.
booking.schema.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import mongoose, { HydratedDocument } from "mongoose";
export type BookingDocument = HydratedDocument<Booking>;
@Schema({ collection: "booking" })
export class Booking {
@Prop({ required: true })
_id: string;
@Prop({required: true, type: mongoose.Schema.Types.String, ref: "Tasker"})
taskerID: string;
}
export const BookingSchema = SchemaFactory.createForClass(Booking);
export const BookingModel = {name: Booking.name, schema: BookingSchema};
tasker.schema.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { HydratedDocument } from "mongoose";
export type TaskerDocument = HydratedDocument<Tasker>;
@Schema({ collection: "tasker" })
export class Tasker {
@Prop({ required: true })
_id: string;
@Prop({ required: true })
phone: string;
@Prop({ required: true })
name: string;
}
export const TaskerSchema = SchemaFactory.createForClass(Tasker);
export const TaskerModel = { name: Tasker.name, schema: TaskerSchema };
I populated mongoose references as in the below example.
booking.service.ts
import { Injectable } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Booking, BookingDocument } from "../db-module/schemas/booking.schema";
import * as mongoose from "mongoose";
import { Tasker, TaskerDocument } from "../db-module/schemas/tasker-user.schema";
@Injectable()
export class BookingService {
constructor(
@InjectModel(Booking.name)
private bookingModel: mongoose.Model<BookingDocument>,
@InjectModel(Tasker.name)
private taskerModel: mongoose.Model<TaskerDocument>
) {}
async findById(id: string): Promise<Booking> {
const Booking = await this.bookingModel.findOne({ _id: id }).populate(
"taskerID",
"",
this.taskerModel,
).exec();
return Booking.toJSON();
}
}
Upvotes: 0
Reputation: 7278
None of above work for me, I had to use populate()
. Reference from https://dev.to/mossnana/nestjs-with-mongoose-populate-4mo7?signin=true
An example of complete code and structure users.service.ts
import { User, UserDocument } from 'src/schemas/user.schema';
import { Role, RoleDocument } from 'src/schemas/role.schema';
...
constructor(
@InjectModel(User.name) private userModel: Model<UserDocument>,
@InjectModel(Role.name) private roleModel: Model<RoleDocument>,
private roleService: RolesService
) {}
async findOne(id: string) {
return await this.userModel.findOne({ _id: id }).populate('role', '', this.roleModel).exec();
}
user.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import * as mongoose from 'mongoose';
import { Role } from './role.schema';
export type UserDocument = User & mongoose.Document;
@Schema()
export class User {
@Prop({ required: true, type: String })
email: string;
@Prop({ type: String })
name: string;
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Role' })
role: Role;
}
export const UserSchema = SchemaFactory.createForClass(User);
role.schema.ts
export type RoleDocument = Role & mongoose.Document;
@Schema()
export class Role {
@Prop({ type: String, required: true, unique: true, index: true })
name: string;
@Prop({ type: [String], required: true })
permissions: string[];
}
export const RoleSchema = SchemaFactory.createForClass(Role);
Upvotes: 4
Reputation: 1965
Found it. My mistake was in defining the schema. Should be :
@Schema()
export class Story extends Document {
@Prop()
title: string;
@Prop({ type: MongooseSchema.Types.ObjectId , ref: 'Person' })
author: MongooseSchema.Types.ObjectId
}
Upvotes: 8
Reputation: 434
After much reading and testing on mongoose references in nestjs. I think the accepted answer can be improved. I will show this in 2 steps. The first step is showing the declaration of MongooseSchema and including the comment of @illnr regarding the author property to use Types.ObjectId
instead of MongooseSchema.Types.ObjectId
.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types, Schema as MongooseSchema } from 'mongoose';
@Schema()
export class Story extends Document {
@Prop()
title: string;
@Prop({ type: MongooseSchema.Types.ObjectId , ref: 'Person' })
author: Types.ObjectId
}
export const StorySchema = SchemaFactory.createForClass(Story);
And as a second step, I think it improves readability to use the Person class as type for the author property, as shown here.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types, Schema as MongooseSchema } from 'mongoose';
import { Person } from './person.schema'
@Schema()
export class Story extends Document {
@Prop()
title: string;
@Prop({ type: MongooseSchema.Types.ObjectId , ref: 'Person' })
author: Person
}
export const StorySchema = SchemaFactory.createForClass(Story);
Upvotes: 18