Home > Back-end >  @Prop decorator for specific nested objects in array
@Prop decorator for specific nested objects in array

Time:08-08

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:

  1. 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.
  2. In order to preserve the nested schema validation, each object has to have its own schema or be defined using the raw schema definition.
  3. 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

  • Related