Mongoose触发Query was already executed报错,评论更新删除无法同步影片评分求助
问题原因
你遇到的Query was already executed报错,核心是Mongoose的查询实例是一次性的,你在pre(/^findOneAnd/)中间件里调用await this.findOne()已经把当前查询实例执行了一次,后面控制器里触发的findByIdAndUpdate底层属于findOneAndUpdate匹配规则,同一个查询实例执行两次就会触发该报错。
解决方案
1. 修改pre查询中间件
通过clone()方法克隆查询实例后再执行查询,避免污染原查询实例,同时把查询到的原始文档挂载到查询实例上方便后续使用:
reviewSchema.pre(/^findOneAnd/, async function(next) { // 克隆实例执行查询,不影响原查询逻辑 this.reviewDoc = await this.clone().findOne(); next(); });
2. 新增post查询中间件
在update/delete操作执行完成后,触发评分计算逻辑,覆盖更新、删除场景:
reviewSchema.post(/^findOneAnd/, async function() { await this.reviewDoc.constructor.calcAvgRating(this.reviewDoc.movie); });
3. 补充聚合逻辑边界处理
当对应电影的所有评论都被删除时,聚合返回的结果是空数组,原代码会触发索引报错,补充判断逻辑:
reviewSchema.statics.calcAvgRating = async function(movieId) { const stats = await this.aggregate([ { $match:{movie:movieId} }, { $group:{ _id:'$movie', nRating:{$sum:1}, avgRating:{$avg:'$rating'} } } ]) if (stats.length) { await movieModel.findByIdAndUpdate(movieId,{ ratingsQuantity:stats[0].nRating, avgRating:stats[0].avgRating }); } else { // 无评论时重置为默认值 await movieModel.findByIdAndUpdate(movieId,{ ratingsQuantity: 0, avgRating: 0 }); } }
4. 控制器无需修改
原有updateOne控制器代码可以直接正常运行,不需要调整。
修改完成后新增评论、更新评论、删除评论都会自动同步更新对应电影的评分和评论数量字段,不会再触发查询重复执行报错。
内容的提问来源于stack exchange,提问作者harsh dixit
相关产品推荐
相关产品推荐

