You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS使用Mongoose Discriminators报错:无法覆盖已编译的Beer模型

解决Mongoose鉴别器启动报错:Cannot overwrite Beer model once compiled

我有一个Discount模型,其动态属性type需要关联Vegetable、Bread、Beer三个模型。参考Nest官方文档后,打算用Discriminators实现这个逻辑,同时Item模型也采用同样的方案,但启动服务器时出现了如下错误:

[Nest] 9181  - 04/22/2024, 3:21:24 PM   ERROR [ExceptionHandler] Cannot overwrite `Beer` model once compiled.
OverwriteModelError: Cannot overwrite `Beer` model once compiled.
    at Function.Model.discriminator (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/mongoose@8.3.2/node_modules/mongoose/lib/model.js:1244:11)
    at InstanceWrapper.useFactory [as metatype] (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+mongoose@10.0.6_@nestjs+common@10.3.8_@nestjs+core@10.3.8_mongoose@8.3.2_rxjs@7.8.1/node_modules/@nestjs/mongoose/dist/mongoose.providers.js:10:42)
    at Injector.instantiateClass (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/injector/injector.js:368:55)
    at callback (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/injector/injector.js:65:45)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at Injector.resolveConstructorParams (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/injector/injector.js:144:24)
    at Injector.loadInstance (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/injector/injector.js:70:13)
    at Injector.loadProvider (/Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/injector/injector.js:97:9)
    at /Users/lablabeedev/code/online-grocery-store/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/injector/instance-loader.js:56:13)
    at async Promise.all (index 3)

Discount模块代码

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { Beer, BeerSchema } from '../beer/schemas/beer.schema';
import { Bread, BreadSchema } from '../bread/schemas/bread.schema';
import {
  Vegetable,
  VegetableSchema,
} from '../vegetable/schemas/vegetable.schema';
import { DiscountController } from './discount.controller';
import { DiscountService } from './discount.service';
import { Discount, DiscountSchema } from './schemas/discount.schema';

@Module({
  imports: [
    MongooseModule.forFeature([
      {
        name: Discount.name,
        schema: DiscountSchema,
        discriminators: [
          { name: Beer.name, schema: BeerSchema },
          { name: Vegetable.name, schema: VegetableSchema },
          { name: Bread.name, schema: BreadSchema },
        ],
      },
    ]),
  ],
  controllers: [DiscountController],
  providers: [DiscountService],
})
export class DiscountModule {}

Discount Schema代码

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { ApiProperty } from '@nestjs/swagger';
import { Document } from 'mongoose';
import { Beer } from '../../beer/schemas/beer.schema';
import { Bread } from '../../bread/schemas/bread.schema';
import { Vegetable } from '../../vegetable/schemas/vegetable.schema';

// export enum DiscountType {
//   Vegetable = 'Vegetable',
//   Beer = 'Beer',
//   Bread = 'Bread',
// }
export type ItemType = Vegetable | Beer | Bread;
export type DiscountDocument = Discount & Document;

@Schema({ discriminatorKey: 'type' })
export class Discount {
  @ApiProperty({
    example: '1',
    description: 'The unique identifier for the discount',
  })
  @Prop()
  id: string;

  @Prop({
    type: { type: String },
    enum: ['Vegetable', 'Beer', 'Bread'],
  })
  type: ItemType;

  @ApiProperty({
    example: 3.99,
    description: 'The price of the discount',
  })
  @Prop({ required: true })
  price: number;

  @ApiProperty({
    example: 1,
    description: 'The age of the discount in days',
  })
  @Prop({ required: true })
  ageInDays: number;
}

export const DiscountSchema = SchemaFactory.createForClass(Discount);

解决方案

报错原因是Beer、Vegetable、Bread这三个模型已经在各自的模块中完成注册(比如BeerModule里通过MongooseModule.forFeature注册了Beer模型),当你在DiscountModule的鉴别器中再次使用同名模型时,Mongoose会尝试覆盖已编译的模型,触发错误。

方案1:给鉴别器使用独立的模型名称

在DiscountModule的鉴别器配置中,给每个子模型指定一个不同于原模型的名称,避免冲突:

// 修改DiscountModule中的discriminators配置
discriminators: [
  { name: 'DiscountBeer', schema: BeerSchema },
  { name: 'DiscountVegetable', schema: VegetableSchema },
  { name: 'DiscountBread', schema: BreadSchema },
]

同时更新Discount Schema中type字段的枚举值,匹配新的鉴别器名称:

@Prop({
  type: { type: String },
  enum: ['DiscountBeer', 'DiscountVegetable', 'DiscountBread'],
})
type: string;

方案2:手动动态注册鉴别器

不在MongooseModule.forFeature中配置鉴别器,而是在DiscountService中注入主模型后,手动检查并注册鉴别器,避免重复覆盖:

// DiscountService代码修改
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { DiscountDocument } from './schemas/discount.schema';
import { BeerSchema } from '../beer/schemas/beer.schema';
import { BreadSchema } from '../bread/schemas/bread.schema';
import { VegetableSchema } from '../vegetable/schemas/vegetable.schema';

@Injectable()
export class DiscountService {
  constructor(
    @InjectModel('Discount') private readonly discountModel: Model<DiscountDocument>,
  ) {
    // 注册前先检查鉴别器是否已存在
    if (!this.discountModel.discriminators?.['Beer']) {
      this.discountModel.discriminator('Beer', BeerSchema);
    }
    if (!this.discountModel.discriminators?.['Vegetable']) {
      this.discountModel.discriminator('Vegetable', VegetableSchema);
    }
    if (!this.discountModel.discriminators?.['Bread']) {
      this.discountModel.discriminator('Bread', BreadSchema);
    }
  }

  // 其他业务方法...
}

方案3:移除子模型的独立注册

如果Beer、Vegetable、Bread不需要作为独立模型使用,仅作为Discount的鉴别器存在,可以删除它们各自模块中的MongooseModule.forFeature注册,只在Discount的鉴别器中定义。


内容的提问来源于stack exchange,提问作者Oussama Bouchikhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 12:05:56