Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.97k stars 3.84k forks source link

Mongoose casts wrong data type on property with options and `type` property #11199

Closed Cartman720 closed 2 years ago

Cartman720 commented 2 years ago

Do you want to request a feature or report a bug?

bug

What is the current behavior?

I am using @nestjs/mongoose class schema definition, However when I retrieve document from database, mongoose converts values such as string, number to Document.

@Schema({
  timestamps: true,
  toObject: {
    virtuals: true,
  },
  toJSON: {
    virtuals: true,
  },
})
export class BlogArticle {
  @Prop({
    type: String,
    require: true,
    trim: true,
  })
  title: string;

  @Prop(String)
  description: string;

  @Prop(String)
  thumbnail: string;

  @Prop(
    raw({
      image: {
        type: String,
        default: null,
      },
      label: {
        type: String,
        default: '',
      },
    }),
  )
  banner: any;

  @Prop({
    type: String,
    default: '',
  })
  content: string;

  @Prop({
    type: String,
    unique: true,
    sparse: true,
  })
  slug: string;

  @Prop({
    type: mongoose.Schema.Types.ObjectId,
    ref: User.name,
  })
  author: UserDocument;

  @Prop(Boolean)
  isVisible: boolean;

  @Prop(Date)
  publishedAt: Date;

  get estimatedTime() {
    return getTextReadingTime(this.content);
  }
}

export const BlogArticleSchema = SchemaFactory.createForClass(BlogArticle);

BlogArticleSchema.loadClass(BlogArticle);

On above schem you can see that content property is a string with default property. However you can see on the screenshot of VSCode debugger, that retrieved data is a `Document object, hence I can't use JS built-in string methods i.e. match, split etc.

unknown

I have debugged @nestjs/mongoose SchemaFactory and did found that it creates valid Mongose schema, so I don't think this is related to external package, rather it's a bug in mongoose itself.

Attaching debugger screenshot for SchemaFactory.

tempsnip

But when I set @Prop(String) to `content property I get valid string, howerver this isn't viable for my case, because I want to use mongoose options for properties.

What is the expected behavior? I would expect to get string value with option @Prop({ type: String, default: '' })

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.

Mongoose: 6.1.5 Mongo: 4.4

IslandRhythms commented 2 years ago

Could you please provide all the code to reproduce this issue. I am having a hard time attempting to reproduce based on a block of code and screenshots.

Cartman720 commented 2 years ago

Hi @IslandRhythms,

Here the repo with reproduction.

https://github.com/Cartman720/nestjs-mongoose-bug

With NestJS team we could find out that whenever we provide @Prop(Symbol) to class property it converts all string properties to Document Objects, even the instance of mongoose connections outside of @nestjs/mongoose becomes corrupted, thus taking into account that problem is global and not tied to NestJS I assum this may be a mongoose issue.

vkarpov15 commented 2 years ago

@Cartman720 we took a look and the workaround is the same as in https://github.com/Automattic/mongoose/issues/10807#issuecomment-962491969 . Replace @Prop(String) with @Prop({ type: String }) and your script runs fine. @Prop(Date) makes NestJS do something strange like set String.type = String. Another fix is to delete String.type after schema declaration:

BlogArticleSchema.loadClass(BlogArticle);
// @ts-ignore
delete String.type;

We added a workaround and the fix will be in v6.1.8. However, we still recommend doing @Prop({ type: String }) instead of @Prop(String) always, because the latter makes NestJS modify JavaScript globals in a strange way that we haven't been able to figure out yet.

vkarpov15 commented 2 years ago

Opened an issue with nestjs/mongoose about this