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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:07:38