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

如何在最新版Mongoose中不使用Model.remove()实现级联删除?

解决Mongoose级联删除不触发中间件的问题

问题核心:findByIdAndDelete(包括findOneAndDelete这类查询式删除方法)不会触发pre('remove')中间件——因为pre('remove')仅在调用文档实例的remove()方法时生效,而Mongoose已弃用模型级的Model.remove(),同时查询式删除走的是另一条钩子链路。

下面给三个可行的解决方案:

方案1:适配查询式删除的中间件(推荐,直接对应findByIdAndDelete)

把原来的pre('remove')改成pre('findOneAndDelete'),注意此时中间件里的this是查询对象,需要先获取待删除的文档:

BootcampSchema.pre('findOneAndDelete', async function(next) {
  // 获取当前查询对应的文档
  const bootcamp = await this.model.findOne(this.getQuery());
  if (bootcamp) {
    console.log(`Courses being removed from bootcamp ${bootcamp._id}`);
    await this.model('Course').deleteMany({ bootcamp: bootcamp._id });
  }
  next();
});

之后调用findByIdAndDelete时,这个中间件就会触发,自动删除关联的Course。

方案2:先查文档再调用实例删除,触发文档级钩子

先通过findById拿到文档实例,再调用deleteOne(),同时修改中间件为文档级的pre('deleteOne'):

中间件代码:

// 加上{ document: true }指定为文档实例触发的钩子
BootcampSchema.pre('deleteOne', { document: true }, async function(next) {
  console.log(`Courses being removed from bootcamp ${this._id}`);
  await this.model('Course').deleteMany({ bootcamp: this._id });
  next();
});

删除逻辑代码:

// 替换原来的findByIdAndDelete调用
const bootcamp = await Bootcamp.findById(bootcampId);
if (bootcamp) {
  await bootcamp.deleteOne(); // 触发上面的pre('deleteOne')中间件
}

方案3:用post删除钩子简化操作

如果不需要在删除前做校验,用post('findOneAndDelete')钩子更简单,删除完成后直接处理关联数据:

BootcampSchema.post('findOneAndDelete', async function(doc) {
  // doc就是被删除的bootcamp文档
  if (doc) {
    await this.model('Course').deleteMany({ bootcamp: doc._id });
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:13:07