Mongoose中post deleteMany/deleteOne中间件未触发问题求助
解决Mongoose中Model.deleteMany()无法触发post中间件的问题
问题现象
批量删除文档时,调用document.deleteOne()能正常触发post中间件,但调用Model.deleteMany()(如Lecture.deleteMany(...)和Chapter.deleteMany(...))时中间件完全不触发,不过文档本身确实被成功删除。
相关代码
路由处理代码
export const DELETE = catchAsync(async function (req, { params }) { // Check if course exists const course = await Course.findById(params.id).populate({ path: "chapters", select: { id: 1 }, }); if (!course) return new AppError("No courses found with the provided id", 404); const { chapters } = course; const chapterIds = chapters.map((chapter) => chapter.id); // Delete course's lectures await Lecture.deleteMany({ chapter: { $in: chapterIds } }); // Delete course's chapters await Chapter.deleteMany({ _id: { $in: chapterIds } }); // Specifically this one // Delete course await course.deleteOne(); });
Chapter模型的post中间件
chapterSchema.post( ["deleteOne", "deleteMany"], { document: true }, async function (doc, next) { console.log("POST DELETE ONE CHAPTER"); const course = await Course.findById(this.course); // Find and remove chapter from course const index = course.chapters.findIndex((ch) => ch._id === doc._id); course.chapters.splice(index, 1); await course.save(); next(); } );
原因分析
Mongoose的中间件分为文档中间件和查询中间件:
- 你设置了
{ document: true },这会让中间件仅对文档实例的方法生效(比如doc.deleteOne())。 Model.deleteMany()属于查询级别的批量操作,不会触发文档中间件;同时Mongoose本身不支持deleteMany的文档中间件,因为批量操作没有单个文档实例传递给中间件。
修复方案
方案一:改用查询中间件适配deleteMany
将deleteMany对应的中间件改为查询中间件({ query: true }),同时调整逻辑以适配查询操作:
// 单独处理deleteMany的查询中间件 chapterSchema.post("deleteMany", { query: true }, async function (result, next) { // 获取本次删除的所有章节数据(包含关联的course字段) const deletedChapters = await Chapter.find(this.getQuery()).select('course'); // 遍历更新每个关联的Course for (const chapter of deletedChapters) { const course = await Course.findById(chapter.course); if (course) { // 过滤掉已删除的章节ID course.chapters = course.chapters.filter(ch => ch._id.toString() !== chapter._id.toString()); await course.save(); } } next(); }); // 保留原有的deleteOne文档中间件 chapterSchema.post("deleteOne", { document: true }, async function (doc, next) { console.log("POST DELETE ONE CHAPTER"); const course = await Course.findById(this.course); if (course) { const index = course.chapters.findIndex((ch) => ch._id.toString() === doc._id.toString()); if (index !== -1) { course.chapters.splice(index, 1); await course.save(); } } next(); });
方案二:遍历文档逐个调用deleteOne(小数据量场景)
如果批量删除的数据量不大,可以先查询出所有目标文档,再逐个调用deleteOne(),这样就能复用原有文档中间件:
修改路由中的删除逻辑:
// 替换原Chapter.deleteMany逻辑 const chaptersToDelete = await Chapter.find({ _id: { $in: chapterIds } }); for (const chapter of chaptersToDelete) { await chapter.deleteOne(); } // 同理修改Lecture的删除逻辑 const lecturesToDelete = await Lecture.find({ chapter: { $in: chapterIds } }); for (const lecture of lecturesToDelete) { await lecture.deleteOne(); }
方案对比
- 方案一:适合大数据量场景,性能更优,但需要单独编写查询中间件逻辑。
- 方案二:逻辑简单,无需修改中间件,但数据量大时会产生多次数据库请求,性能较差。
内容的提问来源于stack exchange,提问作者Tidris
相关产品推荐
相关产品推荐

