I have an array of objects that returns in JSON, each object looks like that:
{
"reviewId": "f1a0ec26-9aca-424f-8b05-cff6aa3d2337",
"authorName": "Some name",
"comments": [
{
"userComment": {
"text": "\tAmazing!",
"lastModified": {
"seconds": "1659685904",
},
"starRating": 5,
"reviewerLanguage": "en",
}
},
{
"developerComment": {
"text": "Thank you.",
"lastModified": {
"seconds": "1659688852",
}
}
}
]
}
I'm trying to create a Schema for this specific object, but I have some issues and I cannot understand how to create it, this is what i've done so far:
import mongoose, { Document, Mongoose } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
export type ReviewDocument = Review & Document;
@Schema()
export class Review {
@Prop({ type: String, required: true })
reviewId: string;
@Prop({ type: String })
authorName: string;
@Prop({ type: mongoose.Schema.Types.Array })
comments: Comments[];
}
@Schema()
export class Comments {
@Prop({ type: mongoose.Schema.Types.ObjectId })
userComment: UserComment;
@Prop({ type: mongoose.Schema.Types.ObjectId })
developerComment: DeveloperComment;
}
@Schema()
export class UserComment {
@Prop({ type: String })
text: string;
@Prop({ type: String })
originalText: string;
@Prop({ type: String })
lastModified: string;
@Prop({ type: Number })
starRating: number;
@Prop({ type: String })
reviewerLanguage: string;
}
@Schema()
export class DeveloperComment {
@Prop({ type: String })
text: string;
@Prop({ type: String })
lastModified: string;
}
export const ReviewSchema = SchemaFactory.createForClass(Review);
It gives me an error:
/.../rtr-backend/src/schemas/reviews.schema.ts:21
userComment: UserComment;
^
ReferenceError: Cannot access 'UserComment' before initialization
at Object.<anonymous> (/.../rtr-backend/src/schemas/reviews.schema.ts:21:18)
at Module._compile (node:internal/modules/cjs/loader:1120:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1174:10)
at Module.load (node:internal/modules/cjs/loader:998:32)
at Function.Module._load (node:internal/modules/cjs/loader:839:12)
at Module.require (node:internal/modules/cjs/loader:1022:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (/.../rtr-backend/src/reviews/reviews.module.ts:6:1)
at Module._compile (node:internal/modules/cjs/loader:1120:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1174:10)
What is best practice?
CodePudding user response:
I think you need to first define the schemas in the order of their dependencies, like this:
import mongoose, { Document, Mongoose } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
export type ReviewDocument = Review & Document;
@Schema()
export class UserComment {
@Prop({ type: String })
text: string;
@Prop({ type: String })
originalText: string;
@Prop({ type: String })
lastModified: string;
@Prop({ type: Number })
starRating: number;
@Prop({ type: String })
reviewerLanguage: string;
}
@Schema()
export class DeveloperComment {
@Prop({ type: String })
text: string;
@Prop({ type: String })
lastModified: string;
}
@Schema()
export class Comments {
@Prop({ type: mongoose.Schema.Types.ObjectId })
userComment: UserComment;
@Prop({ type: mongoose.Schema.Types.ObjectId })
developerComment: DeveloperComment;
}
@Schema()
export class Review {
@Prop({ type: String, required: true })
reviewId: string;
@Prop({ type: String })
authorName: string;
@Prop({ type: mongoose.Schema.Types.Array })
comments: Comments[];
}
export const ReviewSchema = SchemaFactory.createForClass(Review);
CodePudding user response:
There are a few things to notice when you define a mongoose schema:
- The schema types of primitive properties (e.g.
string
,number
) are automatically inferred, so you don't need to explicitly specify{ type: String}
in the decorator. - In order to preserve the nested schema validation, each object has to have its own schema or be defined using the
raw
schema definition. - For each nested schema please mind the default properties created by mongoose, such as
timestamps
,_id
,__v
, and so on. You could manipulate them by passing options object in the@Schema()
decorator.
Here is the schema definition for your use case:
import { Prop, raw, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types } from 'mongoose';
@Schema({ _id: false })
class UserComment {
@Prop({ required: true })
text: string;
@Prop(raw({ seconds: { type: Number } }))
lastModified: Record<string, number>;
@Prop({ required: true })
starRating: number;
@Prop({ required: true })
reviewerLanguage: string;
}
const UserCommentSchema = SchemaFactory.createForClass(UserComment);
@Schema({ _id: false })
class DeveloperComment {
@Prop({ required: true })
text: string;
@Prop(raw({ seconds: { type: Number } }))
lastModified: Record<string, number>;
}
const DeveloperCommentSchema = SchemaFactory.createForClass(DeveloperComment);
@Schema({ _id: false })
class Comment {
@Prop({ type: UserCommentSchema })
userComment?: UserComment;
@Prop({ type: DeveloperCommentSchema })
developerComment?: DeveloperComment;
}
const CommentSchema = SchemaFactory.createForClass(Comment);
@Schema({ timestamps: true, versionKey: false })
export class Review {
_id: Types.ObjectId;
createdAt: Date;
updatedAt: Date;
@Prop({ unique: true, required: true })
reviewId: string;
@Prop({ required: true })
authorName: string;
@Prop({ type: [CommentSchema], default: [] })
comments: Comment[];
}
export type ReviewDocument = Review & Document;
export const ReviewSchema = SchemaFactory.createForClass(Review);
PS: In this documentation page you could find plenty of use cases: https://docs.nestjs.com/techniques/mongodb