Mongoose Movie模型新增评分时自动计算平均评分实现咨询
在Mongoose中实现新增评分自动计算平均分的方案
这里我推荐你给Movie模型新增一个averageRating字段来存储计算好的平均分,比每次需要时动态计算要高效得多。下面分两种常见场景给出具体实现:
方案一:实例操作 + 预保存中间件(推荐日常使用)
这种方式适合你先通过findById/findOne拿到Movie实例,再修改评分的场景,能自动触发平均分计算。
1. 定义Movie模型结构
首先完善Schema,加上ratings数组和averageRating字段,同时给评分加范围限制:
const mongoose = require('mongoose'); const movieSchema = new mongoose.Schema({ title: { type: String, required: true }, // 其他业务字段... ratings: [{ type: Number, min: 1, max: 5, required: true }], averageRating: { type: Number, default: 0, min: 0, max: 5 } });
2. 添加预保存中间件
这个中间件会在实例保存前自动检查ratings数组是否被修改,若修改则重新计算平均分:
movieSchema.pre('save', function(next) { // 只有当ratings数组发生变化时才计算,避免无意义的重复计算 if (this.isModified('ratings')) { if (this.ratings.length === 0) { this.averageRating = 0; } else { // 计算平均分:总和除以数量 const total = this.ratings.reduce((sum, rating) => sum + rating, 0); this.averageRating = total / this.ratings.length; // 可选:保留两位小数,让结果更美观 this.averageRating = parseFloat(this.averageRating.toFixed(2)); } } next(); });
3. 封装实例方法(可选但更易用)
给模型加一个addRating方法,让外部调用更简洁,还能做参数校验:
movieSchema.methods.addRating = async function(rating) { // 先校验评分合法性 if (typeof rating !== 'number' || rating < 1 || rating > 5) { throw new Error('评分必须是1到5之间的数字'); } // 新增评分到数组 this.ratings.push(rating); // 保存实例(会自动触发预保存中间件计算平均分) await this.save(); return this; };
使用示例
// 新增评分的示例代码 async function addMovieRating(movieId, userRating) { try { const movie = await Movie.findById(movieId); if (!movie) { throw new Error('未找到对应电影'); } const updatedMovie = await movie.addRating(userRating); console.log(`当前电影平均分:${updatedMovie.averageRating}`); return updatedMovie; } catch (err) { console.error('新增评分失败:', err.message); } }
方案二:直接数据库更新操作(适合批量/无需实例的场景)
如果是直接用updateOne/updateMany这类不经过模型实例的操作,预保存中间件不会生效,这时候可以用MongoDB的聚合管道来实现更新时自动计算平均分:
async function addRatingViaUpdate(movieId, userRating) { if (typeof userRating !== 'number' || userRating < 1 || userRating > 5) { throw new Error('评分必须是1到5之间的数字'); } const result = await Movie.updateOne( { _id: movieId }, [ // 第一步:把新评分追加到ratings数组 { $set: { ratings: { $concatArrays: ['$ratings', [userRating]] } } }, // 第二步:计算新的平均分 { $set: { averageRating: { $avg: '$ratings' } } } ] ); // 检查是否成功更新 if (result.modifiedCount === 0) { throw new Error('未找到对应电影或评分未更新'); } // 返回更新后的电影数据 return await Movie.findById(movieId); }
注意事项
- 方案二需要MongoDB版本在4.2及以上,因为聚合管道更新是这个版本开始支持的。
- 两种方案都处理了空数组的情况,避免出现除以0的错误。
内容的提问来源于stack exchange,提问作者tilly
相关产品推荐
相关产品推荐

