Falcort
Falcort

Reputation: 93

NestJS MongoDB nested object schema

I am currently running the code :

export class SystemInformationContent {
  createdAt: number;

  createdBy: User | mongoose.Schema.Types.ObjectId | null;

  updatedAt?: number;

  updatedBy?: User | mongoose.Schema.Types.ObjectId | null;
}

@Schema()
export class SystemInformation {
  @Prop(
    raw({
      createdAt: { type: Number, required: true },
      createdBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
      updatedAt: { type: Number, default: 0 },
      updatedBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        default: null,
      },
    }),
  )
  system: SystemInformationContent;
}

I did not found any way of "extending" the schema of SystemInformationContent and so used the raw() function in the @Prop() decorator, but I am wondering if there is a way to do something like this:

export class SystemInformationContent {
  @Prop({ required: true })
  createdAt: number;

  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
  createdBy: User | mongoose.Schema.Types.ObjectId | null;

  @Prop({ default: 0 })
  updatedAt?: number;

  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User', default: null })
  updatedBy?: User | mongoose.Schema.Types.ObjectId | null;
}

@Schema()
export class SystemInformation {
  @Prop(???)
  system: SystemInformationContent;
}

I did not found anything working to put into the SystemInformation.system @Prop() that take in account the schema of SystemInformationContent.

Do you guys know if there is an other way than the raw or if I am missing something ?

Edit: All classes of my NestJS application are extending SystemInformation so they all look like :

{
  ...,
  system: {
    createdAt: 1616778310610,
    createdBy: "605e14469d860eb1f0641cad",
    editedAt: 0,
    createdBy: null,
  },
}

Upvotes: 3

Views: 7465

Answers (3)

Falcort
Falcort

Reputation: 93

Found a solution !

I edited SystemInformationContent to :

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import * as mongoose from 'mongoose';
import { User } from '~/schemas/user.schema';

@Schema({ _id: false })
export class SystemInformationContent {
  @Prop({ type: Number, required: true })
  createdAt: number;

  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
  createdBy: User;

  @Prop({ type: Number, default: 0 })
  updatedAt?: number;

  @Prop({
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    default: null,
  })
  updatedBy?: User;
}

export const SystemInformationContentSchema = SchemaFactory.createForClass(
  SystemInformationContent,
);

Then in SystemInformation I edited to :

import { Prop, Schema } from '@nestjs/mongoose';
import {
  SystemInformationContent,
  SystemInformationContentSchema,
} from '~/schemas/systemInformationContent.schema';

@Schema()
export default class SystemInformation {
  @Prop({ required: true, type: SystemInformationContentSchema })
  system: SystemInformationContent;
}

Now everything is working, I used the @Schema({ _id: false }) to remove the ID generated by the SchemaFactory so I end up with that in the DB :

{
  ...,
  "system": {
    "updatedBy": null,
    "updatedAt": 0,
    "createdAt": 1616847116986,
    "createdBy": {
      "$oid": "605f210cc9fe3bcbdf01c95d"
    }
  },
  ...,
}

Upvotes: 6

nileger
nileger

Reputation: 225

If I get it right, this is what you want. You only missed the @Schema() annotation for SystemInformationContent and need to inherit this schema where ever you want to. I had a similar problem. So, if this is not what you asked for, please let me know.

@Schema()
export class SystemInformationContent {
  // put your @Prop() attributes here, that need to be available in every other schema
}

@Schema()
export class SystemInformation extends SystemInformationContent {
  // no need to do anything else here
  // the attributes are inherited from SystemInformationContent 
}

Upvotes: 0

hasezoey
hasezoey

Reputation: 1096

if you are using typegoose (7.0 or later), then what you currently have should be enough, if you have emitDecoratorMetadata enabled in the tsconfig

example with typegoose (the code below uses option extensions from ~7.4):

export class SystemInformationContent {
  @Prop({ required: true })
  createdAt: number;

  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: () => User })
  createdBy: Ref<User>;

  @Prop({ default: 0 })
  updatedAt?: number;

  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: () => User }) // default is "undefined"
  updatedBy?: Ref<User>;
}

export class SystemInformation {
  @Prop() // thanks to "emitDecoratorMetadata" no explicit types are needed 
  system: SystemInformationContent;

  // but if wanting to do explicit types
  @Prop({ type: () => SystemInformationContent })
  system: SystemInformationContent;
}

PS: i dont know where your function raw comes from, but this is not an typegoose function

Upvotes: 0

Related Questions