Next.js中使用Mongoose鉴别器仍触发OverwriteModelError问题
Mongoose鉴别器在Next.js中触发OverwriteModelError的解决方案
问题描述
在Next.js项目中基于SchemaVersion创建Mongoose鉴别器,本意通过检查已存在模型避免覆盖错误,但仍抛出:OverwriteModelError: Cannot overwrite V1 model once compiled.
原模型定义代码:
import { SectionStatus } from '@/types/connectwise.types' import mongoose, { Schema, model, models, Document } from 'mongoose' enum SchemaVersion { V1 = 'V1', V2 = 'V2', } export type EntityBase = { status: SectionStatus schemaVersion: SchemaVersion } export type EntityV1 = EntityBase & { migrationId: string sectionName: string sectionEndpoint: string isDependencyResolved: boolean isBaseDependency: boolean record_count: number processed_record_count: number error_record_count: number schemaVersion: SchemaVersion.V1 } export type EntityV2 = EntityBase & { migrationId: mongoose.Types.ObjectId entityName: string entityEndpoint: string successCount: number errorCount: number skipCount: number objectExistsCount: number totalToSendCount: number schemaVersion: SchemaVersion.V2 } export type IEntity = EntityV1 | EntityV2 export type IEntityDocument = IEntity & Document // Base Schema const entityBaseSchema = new Schema<EntityBase>( { status: { type: Number, enum: Object.values(SectionStatus), required: true }, schemaVersion: { type: String, required: true, enum: Object.values(SchemaVersion), default: SchemaVersion.V1 }, }, { timestamps: true, collection: 'connectwisebasedependency', discriminatorKey: 'schemaVersion' }, ) const entityV1Schema = new Schema<Omit<EntityV1, keyof EntityBase>>( { migrationId: { type: String, required: true }, sectionName: { type: String, required: true }, sectionEndpoint: { type: String, required: true }, isDependencyResolved: { type: Boolean, required: true }, isBaseDependency: { type: Boolean, required: true }, record_count: { type: Number, required: true }, processed_record_count: { type: Number, required: true }, error_record_count: { type: Number, required: true }, }, { timestamps: true, collection: 'connectwisebasedependency' }, ) const entityV2Schema = new Schema<Omit<EntityV2, keyof EntityBase>>( { migrationId: { type: Schema.Types.ObjectId, ref: 'Migration', required: true }, entityName: { type: String, required: true }, entityEndpoint: { type: String, required: true }, successCount: { type: Number, required: true }, errorCount: { type: Number, required: true }, objectExistsCount: { type: Number, required: true }, skipCount: { type: Number, required: true }, totalToSendCount: { type: Number, required: true }, }, { timestamps: true, collection: 'connectwisebasedependency' }, ) export const BaseEntity = (models.Entity as mongoose.Model<EntityBase>) || model<EntityBase>('Entity', entityBaseSchema) export const EntityV1Model = (BaseEntity.discriminators?.[SchemaVersion.V1] as mongoose.Model<EntityV1>) || BaseEntity.discriminator<EntityV1>(SchemaVersion.V1, entityV1Schema) export const EntityV2Model = (BaseEntity.discriminators?.[SchemaVersion.V2] as mongoose.Model<EntityV2>) || BaseEntity.discriminator<EntityV2>(SchemaVersion.V2, entityV2Schema)
问题根源
- Next.js热重载会重复执行模型定义代码,当
BaseEntity重新创建时,其discriminators属性为空,但之前创建的鉴别器模型已存在于Mongoose全局models对象中,此时调用discriminator()会尝试覆盖已编译的模型。 - 鉴别器的子Schema无需重复指定
collection,会自动继承基类的配置,重复设置可能引发冲突。
修复方案
修改鉴别器模型的获取逻辑,直接从Mongoose全局models对象中检查是否已存在目标鉴别器模型;同时移除子Schema的collection配置:
import { SectionStatus } from '@/types/connectwise.types' import mongoose, { Schema, model, models, Document } from 'mongoose' enum SchemaVersion { V1 = 'V1', V2 = 'V2', } export type EntityBase = { status: SectionStatus schemaVersion: SchemaVersion } export type EntityV1 = EntityBase & { migrationId: string sectionName: string sectionEndpoint: string isDependencyResolved: boolean isBaseDependency: boolean record_count: number processed_record_count: number error_record_count: number schemaVersion: SchemaVersion.V1 } export type EntityV2 = EntityBase & { migrationId: mongoose.Types.ObjectId entityName: string entityEndpoint: string successCount: number errorCount: number skipCount: number objectExistsCount: number totalToSendCount: number schemaVersion: SchemaVersion.V2 } export type IEntity = EntityV1 | EntityV2 export type IEntityDocument = IEntity & Document // Base Schema const entityBaseSchema = new Schema<EntityBase>( { status: { type: Number, enum: Object.values(SectionStatus), required: true }, schemaVersion: { type: String, required: true, enum: Object.values(SchemaVersion), default: SchemaVersion.V1 }, }, { timestamps: true, collection: 'connectwisebasedependency', discriminatorKey: 'schemaVersion' }, ) const entityV1Schema = new Schema<Omit<EntityV1, keyof EntityBase>>( { migrationId: { type: String, required: true }, sectionName: { type: String, required: true }, sectionEndpoint: { type: String, required: true }, isDependencyResolved: { type: Boolean, required: true }, isBaseDependency: { type: Boolean, required: true }, record_count: { type: Number, required: true }, processed_record_count: { type: Number, required: true }, error_record_count: { type: Number, required: true }, }, { timestamps: true }, // 移除重复的collection配置 ) const entityV2Schema = new Schema<Omit<EntityV2, keyof EntityBase>>( { migrationId: { type: Schema.Types.ObjectId, ref: 'Migration', required: true }, entityName: { type: String, required: true }, entityEndpoint: { type: String, required: true }, successCount: { type: Number, required: true }, errorCount: { type: Number, required: true }, objectExistsCount: { type: Number, required: true }, skipCount: { type: Number, required: true }, totalToSendCount: { type: Number, required: true }, }, { timestamps: true }, // 移除重复的collection配置 ) export const BaseEntity = (models.Entity as mongoose.Model<EntityBase>) || model<EntityBase>('Entity', entityBaseSchema) // 直接从全局models中获取鉴别器模型,避免重复创建 export const EntityV1Model = (models[SchemaVersion.V1] as mongoose.Model<EntityV1>) || BaseEntity.discriminator<EntityV1>(SchemaVersion.V1, entityV1Schema) export const EntityV2Model = (models[SchemaVersion.V2] as mongoose.Model<EntityV2>) || BaseEntity.discriminator<EntityV2>(SchemaVersion.V2, entityV2Schema)
额外说明
Mongoose创建鉴别器时,会自动将鉴别器模型注册到全局models对象中,键名就是鉴别器的名称(此处为V1、V2)。通过直接检查models对象,可以确保在Next.js热重载时不会重复创建已编译的模型,彻底避免覆盖错误。
内容的提问来源于stack exchange,提问作者nischalmainali21
相关产品推荐
相关产品推荐

