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

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)

问题根源

  1. Next.js热重载会重复执行模型定义代码,当BaseEntity重新创建时,其discriminators属性为空,但之前创建的鉴别器模型已存在于Mongoose全局models对象中,此时调用discriminator()会尝试覆盖已编译的模型。
  2. 鉴别器的子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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 10:33:12