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

如何基于关联Schema计算平均值?按书籍ID/标题求书评平均评分

从关联Schema计算书籍平均评分的解决方案

我明白你需要基于书籍的ID或标题,计算它关联的书评的平均评分。从你给出的Book Schema来看,bookReviews字段是引用BookReview文档的ObjectId数组,对吧?那我们可以通过两种常见方式来实现这个需求:使用Mongoose的聚合管道或者结合populate后计算,下面分别详细说明。

方法一:使用MongoDB聚合管道(推荐,性能更优)

聚合管道直接在数据库层面计算,避免把所有书评数据拉到应用层处理,适合数据量较大的场景。我们可以通过$lookup关联BookReview集合,然后计算平均值。

按书籍ID查询并计算平均评分

const Book = mongoose.model('Book', bookSchema);

async function getAverageRatingByBookId(bookId) {
  const result = await Book.aggregate([
    // 匹配目标书籍
    { $match: { _id: mongoose.Types.ObjectId(bookId) } },
    // 关联BookReview集合,获取该书籍的所有书评
    {
      $lookup: {
        from: 'bookreviews', // 注意这里是BookReview模型对应的集合名,通常是小写复数
        localField: 'bookReviews',
        foreignField: '_id',
        as: 'reviews'
      }
    },
    // 计算平均评分,如果没有书评则返回0
    {
      $project: {
        _id: 1,
        title: 1,
        averageRating: {
          $avg: '$reviews.rating' // 假设BookReview Schema里有rating字段
        }
      }
    }
  ]);

  return result.length > 0 ? result[0] : null;
}

按书籍标题查询并计算平均评分

只需要修改$match阶段的条件:

async function getAverageRatingByBookTitle(title) {
  const result = await Book.aggregate([
    { $match: { title: { $regex: title, $options: 'i' } } }, // 模糊匹配,忽略大小写
    {
      $lookup: {
        from: 'bookreviews',
        localField: 'bookReviews',
        foreignField: '_id',
        as: 'reviews'
      }
    },
    {
      $project: {
        _id: 1,
        title: 1,
        averageRating: { $avg: '$reviews.rating' }
      }
    }
  ]);

  return result; // 返回所有匹配标题的书籍及其平均评分
}

方法二:使用Mongoose Populate后在应用层计算

如果你的数据量不大,也可以先populate出所有关联的书评,然后在代码里计算平均值:

async function getAverageRatingByBookId(bookId) {
  const book = await Book.findById(bookId).populate('bookReviews');
  
  if (!book) return null;
  
  // 计算平均评分
  const reviews = book.bookReviews;
  if (reviews.length === 0) {
    return { ...book.toObject(), averageRating: 0 };
  }
  
  const totalRating = reviews.reduce((sum, review) => sum + review.rating, 0);
  const averageRating = totalRating / reviews.length;
  
  return { ...book.toObject(), averageRating };
}

注意事项

  • 确保BookReview Schema里确实有rating字段(数值类型),如果字段名不同,记得修改代码里的对应部分。
  • 聚合管道中的集合名bookreviews要和你的BookReview模型对应的MongoDB集合名一致,Mongoose默认会把模型名转为小写复数。
  • 如果需要处理大量数据,优先选择聚合管道的方式,性能会比populate后计算好很多。

内容的提问来源于stack exchange,提问作者HKIT9

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:30:11