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
相关产品推荐
相关产品推荐

