3

I am working with Nest.js and trying to create a Schema with decorators which contain array of sub-document field.

I don't have any troubles, with importing/export the Schema and converting it to a model, until:

Error

I receive the following error in my service file.

After hours of googling, I discover that the real reason is behind the array sub-document fields, and only with them. As soon, as I remove the members field. the schema & model will be fine. And have to do nothing with the solution described in following, or any other answers relevant with extends Mongoose.Document. (If you have already done it)

Most of cases, that I found, are relevant with sub-documents, but not array sub-document. And I'd like to ask:

How to correctly create a field with an array of subdocuments in Nestjs via mongoose / Typescript with using of decorators?

And unseed this error:

S2344: Type 'Guild' does not satisfy the constraint 'Document<any, {}>'.   
  The types returned by 'delete(...).$where(...).cast(...)' are incompatible between these types.
   Type 'UpdateQuery<Guild>' is not assignable to type 'UpdateQuery<Document<any, {}>>'.
     Type 'UpdateQuery<Guild>' is not assignable to type '_UpdateQuery<_AllowStringsForIds<LeanDocument<Document<any, {}>>>>'.
Types of property '$pull' are incompatible.
  Type 'PullOperator<_AllowStringsForIds<LeanDocument<Guild>>>' has no properties in common with type 'PullOperator<_AllowStringsForIds<LeanDocument<Document<any, {}>>>>'.

My Schema is:

import { Document, Schema as MongooseSchema } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

class GuildMember {
  @Prop({ type: String, required: true, lowercase: true })
  _id: string;

  @Prop({ required: true })
  id: number;

  @Prop({ required: true })
  rank: number;
}

@Schema({ timestamps: true })
export class Guild extends Document {
  @Prop({ type: String, required: true, lowercase: true })
  _id: string;

  @Prop({ type: MongooseSchema.Types.Array})
  members: GuildMember[]
}

export const GuildsSchema = SchemaFactory.createForClass(Guild);

What have I done.

Various ways including:

  • Cover sub-document Class with @Schema() decorator and adding extends Document
  • Adding type: to field @Prop() decorator:
  @Prop({ type: [GuildMember] })
  members: GuildMember[]

or vice-versa. It's ok for primitives, but not for Class embedded documents.

  • adding @Prop({ ref: () => GuildMember })

And following the official NestJs docs:

  @Prop({ type: [{ type: MongooseSchema.Types.Array, ref: GuildMember }] })
  members: GuildMember[]

It still doesn't help. I thought, that it could be relevant, not just with mongoose.Document, but also another type, which is: mongoose.DocumentArray

Updated:

According to current progress. It seems that problem is relevant with the field: type value of default mongoose, not the @Prop decorator itself. So even if I write something like that:

  @Prop()
  members: Types.Array<GuildMember>

it still gives an error. Type is imported from: import { Document, Schema as MongooseSchema, Types } from 'mongoose';

AlexZeDim
  • 3,520
  • 2
  • 28
  • 64

2 Answers2

5

UPDATED:

Working solution:

with redefined _id for other TS primitive:

import { Document, Schema as MongooseSchema, Types } from "mongoose";
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

@Schema()
class Pet extends Document  {
  @Prop({ type: Number })
  _id: number;

  @Prop({ type: String })
  name: string;
}

export const PetsSchema = SchemaFactory.createForClass(Pet);

Parent schema array of objects field:

  @Prop({ type: [PetsSchema] })
  pets: Types.Array<Pet>;

If you want to disable build-in ObjectId at all

Use the following @Prop decorator and remove _id from child schema:

  @Prop({ _id: false, type: [PetsSchema] })
  pets: Types.Array<Pet>;
AlexZeDim
  • 3,520
  • 2
  • 28
  • 64
3

Your members prop is not a simple array. It is a collection of sub docs and should be declared as [SchemaTypes.ObjectId] which will implement sub documents with _id field via default mongo ObjectID value:

@Prop({ type: [SchemaTypes.ObjectId], ref: 'GuildMember'})
members: GuildMember[]
AlexZeDim
  • 3,520
  • 2
  • 28
  • 64
Pierre_T
  • 1,094
  • 12
  • 29
  • Okey, I have also found an answer for this question (should post it in couple of hours), but what I do want to use `SchemaTypes.ObjectId` for `_id` field for my `array of objects/docs`? Is there any option, like: `_id: false` ? – AlexZeDim Apr 12 '21 at 09:51
  • I dont get your question, sorry. Could be you be more specific ? – Pierre_T Apr 12 '21 at 09:59
  • I mean that, as far as I know, an array of documents can use *build-in _id field with generated Object_id values by mongo itself*. In my case, I generate the `_id` field for subdocuments by myself and don't want to generate it by mongo. So my question is: **How with this solution achieve non generated, but manual determination of `_id` for sub-documents?** – AlexZeDim Apr 12 '21 at 10:01
  • 1
    I don't know if there is a way to do that within mongoose. Maybe use some 3rd party lib like `class-transformer` ? see some use-cases in nestjs doc: https://docs.nestjs.com/techniques/serialization – Pierre_T Apr 12 '21 at 12:51
  • Hmm, I haven't seen this before. It deserves to take a look. – AlexZeDim Apr 12 '21 at 13:08