如何在最新版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
相关产品推荐
相关产品推荐

