【问题标题】:NestJS - Nested polymorphism in sub schemaNestJS - 子模式中的嵌套多态性
【发布时间】:2022-03-15 18:15:36
【问题描述】:

在 NestJs 中,我试图让 mongoose 基于其属性之一可以具有不同类型的模式创建文档。但是,当我保存文档时,与特定类型相关的所有属性都会丢失。我错过了什么?为了简单起见,在这个 sn-p 中我使用了一个字符串数组而不是输入枚举

@Schema()
export class ClassAModel {
  @Prop({ type: ClassBSchema, required: true })
  object!: ClassB1Model | ClassB2Model
}

export const ClassASchema = SchemaFactory.createForClass(ClassAModel)

///
@Schema({ _id: false })
export class ClassB1Model  {
  @Prop({ enum: ['B1'], required: true  })
  type!: 'B1'
  
  @Prop({ required: true  })
  onlyForClassB1Model!: string
}

export const ClassB1Schema = SchemaFactory.createForClass(ClassB1Model)

///
@Schema({ _id: false })
export class ClassB2Model  {
  @Prop({ enum: ['B2'], required: true  })
  type!: 'B2'
  
  @Prop({ required: true })
  onlyForClassB2Model!: string
}

export const ClassB2Schema = SchemaFactory.createForClass(ClassB2Model)

///
@Schema({ _id: false, discriminatorKey: 'type' })
export class ClassBModel {
  @Prop({ enum: ['B1', 'B2'], required: true  })
  type!: 'B1' | 'B2'
}

export const ClassBSchema = SchemaFactory.createForClass(ClassBModel)

ClassBSchema.discriminators = {
  B1: ClassB1Schema,
  B2: ClassB2Schema,
}

当我尝试使用模型实例保存文档时,它只保存了 ClassBModel 中定义的道具(所以只有类型)。所有其他道具都没有被拾取。

 // some class method
 public async saveDoc(): Promise<void> {
   const payload = {
     object: {
       type: 'B2',
       onlyForClassB2Model: 'random string'
     }
   }
   return this.model.create(payload) // yields { object: { type: 'B2' } }
 }

我知道顶级文档的鉴别器可以按照 NestJSDocs 中的描述定义,使用 nestjs 模块中的鉴别器,但这是一个不同的情况,鉴别器位于注入模型的内部。如何让 mongoose 认识到它需要保存有效载荷中的所有属性?

【问题讨论】:

  • 一种草率的方式只是在单个模型中定义所有属性,然后将所有子类型属性设为可选,但我绝对更喜欢在猫鼬模式中明确定义不同的子类型

标签: mongoose polymorphism nestjs mongoose-schema


【解决方案1】:

在子模式上使用鉴别器对象不会将鉴别器链接到基本模式。您需要在 Schema.DocumentsArray 或 Schema.Embedded schemaType 上调用鉴别器方法。由于 Typescript 无法使用 createForClass 方法自行推断它是什么模式类型,因此您必须明确声明它涉及嵌入式模式类型(对于简单对象和 DocumentsArray 用于数组)。鉴别器方法将选择的键作为鉴别器及其关联的模式。

import { Schema as MongooseSchema } from 'mongoose'

@Schema()
export class ClassAModel {
  @Prop({ type: ClassBSchema, required: true })
  object!: ClassB1Model | ClassB2Model
}

export const ClassASchema = SchemaFactory.createForClass(ClassAModel)
const classBSchema = ClassASchema.path<MongooseSchema.Types.Embedded>('type')
classBSchema.discriminator('B1', ClassB1Schema)
classBSchema.discriminator('B2', ClassB2Schema)

同时删除子类型模式上的 Prop 装饰器,用于 mongo 验证的鉴别器,因为 mongoose 会抱怨子类型不能与基本类型具有相同的键

///
@Schema({ _id: false })
export class ClassB1Model  {
  type!: 'B1'
  
  @Prop({ required: true  })
  onlyForClassB1Model!: string
}

export const ClassB1Schema = SchemaFactory.createForClass(ClassB1Model)

///
@Schema({ _id: false })
export class ClassB2Model  {
  type!: 'B2'
  
  @Prop({ required: true })
  onlyForClassB2Model!: string
}

最后可以去掉subschema上的discriminatos对象,但是还是需要在schema装饰器中声明discriminator key

@Schema({ _id: false, discriminatorKey: 'type' })
export class ClassBModel {
  @Prop({ enum: ['B1', 'B2'], required: true  })
  type!: 'B1' | 'B2'
}

export const ClassBSchema = SchemaFactory.createForClass(ClassBModel)

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,根据@ant_hony 的研究做了一个实用函数,如果你的项目中有很多类似的案例,它可以简化这个问题。把它留在这里,也许它可以帮助某人??

    import { DiscriminatorOptions } from '@nestjs/mongoose/dist/interfaces/model-definition.interface';
    type TClass<T = any> = new (...args: any[]) => T;
    
    function initDiscriminators<T extends TClass>(rootClass: T, path: string, discriminators: DiscriminatorOptions[]): Schema<T> {
        const root = SchemaFactory.createForClass(rootClass);
        const child = root.path(path);
    
        discriminators.forEach((discriminator) => {
            child['discriminator'](discriminator.name, discriminator.schema);
        });
    
        return root;
    }
    
    

    这种用法非常简单:

    const ClassASchema = initDiscriminators(ClassAModel, 'type', [
      { name: 'B1', schema: SchemaFactory.createForClass(ClassB1Model) },
      { name: 'B2', schema: SchemaFactory.createForClass(ClassB2Model) }
    ]);
    
    

    【讨论】:

      猜你喜欢
      • 2021-06-21
      • 2020-05-20
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-28
      • 2021-10-15
      • 2015-09-21
      相关资源
      最近更新 更多