Mongoose:在 db.collection() 中查找并聚合在一起

Mongoose: find and aggregate together in db.collection()

我在 mongodb 中有一个名为 recipe 的集合,其中我有一个名为 comments 的文档,它是一个数组,在每个食谱中都保存了评论。在 comments 数组中我有一个 ratings,类型是 Number。所以我想计算 ratings 的平均值,但不知道如何使用 db.collection().aggregate 代码在食谱集合中工作,在带有评分变量的评论文档中。

这是 mongodb 中的 recipe 集合:

const { Double } = require('bson');
const { timeStamp } = require('console');
const mongoose = require('mongoose');

const recipeSchema = new mongoose.Schema({
    name: {
        type: String,
        required: 'This field is required.'
    },
    description: {
        type: String,
        required: 'This field is required.'
    },
    quantity: {
        type: Array,
        required: 'This field is required.'
    },
    ingredients: {
        type: Array,
        required: 'This field is required.'
    },
    categoryByServing: {
        type: String,
        enum: ['Reggeli', 'Ebéd', 'Vacsora', 'Desszert', 'Levesek', 'Egyéb'],
        required: 'This field is required.'
    },
    categoryByNationality: {
        type: String,
        enum: ['Thai', 'Kínai', 'Indiai', 'Olasz', 'Angol', 'Magyar', 'Egyéb'],
        required: 'This field is required.'
    },
    image: {
        type: Array,
        required: 'This field is required.'
    },
    comments: [
        {
            username: String,
            comment: String,
            date: {
                type: Date,
                default: Date.now
            },
            rating: Number
        },{
            timestamps: true
        }
    ],
    count: {
        type: Number
    },
    likes: {
        type: Number
    },
    recipe_id: {
        type: String
    }

});

recipeSchema.index({ name: 'text', description: 'text' });
const Recipe = module.exports = mongoose.model('Recipe', recipeSchema);

这是我在评论 post 方法中实现评分平均值计算的代码:

 /**
 * POST /comment-recipe
 * Comment Recipe
*/
module.exports.CommentRecipeOnPost = async(req, res) => {
    
    let recipeId = req.params.id

    const comment = new Comment({
      username: req.body.username,
      comment: req.body.comment,
      date: req.body.date,
      rating: req.body.rating
    });
    comment.save((err, result) => {
      if (err){
        console.log(err)
      }else {
        Recipe.findById(req.params.id, (err, post) =>{
          if(err){
            console.log(err);
          }else{
            post.comments.push(result);
            post.save();




            db.collection('recipes').aggregate([
              {
                $unwind: "$comments"
              },
              {
                $group: {
                  _id: null,
                  avgrating: {
                    $avg: "$rating"
                  }
                }
              }
            ]).toArray()
              .then(results => {
                  console.log({ rating: results[0].avgrating })
              })
              .catch(error => console.error(error))





            console.log('====comments=====')
            console.log(post.comments);
            res.redirect('/recipe/' + recipeId);
          }
        })
      }
    })
}

更新

chridam in the comments using only a $project which I didn't figure out at first demo

指出了一个更简单的方法
db.collection.aggregate([
  {
    $project: {
      _id: 0,
      name: 1,
      avgRating: {
        $avg: "$comments.rating"
      }
    }
  }
])

.. 或使用 $addFields,这将为每个记录 demo 提供评分的平均值作为新字段 avgRating。如果只需要获取某些字段,您可以在之后使用项目步骤

db.collection.aggregate([
  {
    $addFields: {
      avgRating: {
        $avg: "$comments.rating"
      }
    }
  }
])

  1. 您已正确完成 $unwind 步骤,现在您将获得每条评论的记录。
  {
    "_id": "1",
    "comments": {
      "comment": "commment1-1",
      "rating": 4
    },
    "name": "recipe 1"
  },
  {
    "_id": "1",
    "comments": {
      "comment": "comment1-2",
      "rating": 3
    },
    "name": "recipe 1"
  },
...
  1. $group 阶段按 _idname 等独特的东西分组,$avg 应该属于 $comments.rating 而不是 $rating.

最后管道应该看起来像这样。 demo

db.collection.aggregate([
  {
    $unwind: "$comments"
  },
  {
    $group: {
      _id: "$name", //group by something unique for that document containing comments
      avgRating: {
        $avg: "$comments.rating"
      }
    }
  }
])