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

NestJS中用Mongoose实现MongoDB自增字段遇预存钩子问题

解决NestJS中Mongoose自增字段的问题

问题1:pre-save钩子未执行

pre-save钩子不触发的常见原因及解决办法:

  • 钩子注册时机错误:必须在Schema编译为Model之前绑定pre-save钩子。在NestJS中,要确保pre('save')钩子是在@Schema()装饰的类定义之后、MongooseModule.forFeature()注册Schema之前完成绑定的。
  • 使用了不触发save的方法:如果代码里用insertMany()批量创建文档,该方法不会触发pre-save钩子,需换成create()或逐个调用save方法。
  • 异步处理异常未捕获:钩子是async函数,要包裹try/catch处理异常,否则异常会导致钩子静默失败。

问题2:Model实例创建方式错误

你代码里new Model<UserDocument>()的写法完全错误,Mongoose的Model不能通过new直接实例化获取。在pre-save钩子的上下文里,this指向当前文档实例,通过this.constructor即可拿到对应的Model:

UserSchema.pre<User>('save', async function (next) {
  // 通过当前文档的constructor获取对应Model
  const UserModel = this.constructor as Model<UserDocument>;
  const lastUser = await UserModel.findOne({}).sort({ number: -1 }).exec();
  this.number = lastUser ? lastUser.number + 1 : 1;
  next();
});

更可靠的自增实现(避免并发冲突)

上面的写法在高并发场景下会出现重复自增数的问题——多个请求可能同时查到相同的lastUser,导致生成重复的number。推荐用单独的计数器集合实现原子操作:

  1. 定义计数器Schema和Model:
// counter.schema.ts
import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

export type CounterDocument = Counter & Document;

@Schema()
export class Counter {
  @Prop({ required: true, unique: true })
  modelName: string;

  @Prop({ default: 0 })
  seq: number;
}

export const CounterSchema = SchemaFactory.createForClass(Counter);
  1. 在User的pre-save钩子中调用计数器:
UserSchema.pre<User>('save', async function (next) {
  if (this.isNew) { // 仅在创建新文档时触发自增
    const CounterModel = await import('./counter.model').then(m => m.CounterModel);
    const counter = await CounterModel.findOneAndUpdate(
      { modelName: 'User' },
      { $inc: { seq: 1 } },
      { new: true, upsert: true } // 不存在则创建,返回更新后的文档
    );
    this.number = counter.seq;
  }
  next();
});

注意:要在NestJS的Module中注册CounterModel:

// 对应业务模块
@Module({
  imports: [
    MongooseModule.forFeature([
      { name: Counter.name, schema: CounterSchema },
      { name: User.name, schema: UserSchema }
    ])
  ]
})
export class YourModule {}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 16:45:44