猫鼬聚合以获取平均评分,对每个评分进行计数并返回实际评分

时间:2020-02-27 19:52:04

标签: node.js mongodb mongoose

我试图获取产品的平均评级,加上每个评级的计数,还返回实际评级,并使用分页来限制返回的金额而不影响平均或计数。

所以我正在尝试实现以下目标:

这是我的评分集合:

       {
            "productId": "3"
            "userid" : 5,
            "rating" : 5
            "comment": "this is nice"
        },
        {
            "productId": "3"
            "userid" : 2,
            "rating" :4
            "comment": "this is very nice"
        }

这是我想要的最终结果

{
    "_id" : 1,
    "avgRating": "3.6"
    "counts" : [
        {
            "rating" : 5,
            "count" : 8
        },
        {
            "rating" : 3,
            "count" : 2
        },
        {
            "rating" : 4,
            "count" : 4
        },
        {
            "rating" : 1,
            "count" : 4
        }
    ],
   "ratings": [
       {
            "productId": "3"
            "userid" : 5,
            "rating" : 5
            "comment": "this is nice"
        },
        {
            "productId": "3"
            "userid" : 2,
            "rating" :4
            "comment": "this is very nice"
        },
        {
            "productId": "3"
            "userid" : 12,
            "rating" : 4
            "comment": "this is okay"
        }
    ]
}

到目前为止,我已经掌握了每个评分的计数:

db.votes.aggregate([
    { $match: { postId: {$in: [1,2]} } },
    {
      $group: { _id: { post: "$postId", rating: "$vote" }, count: { $sum: 1 } }
    },
    {
      $group: {
        _id: "$_id.post",
        counts: { $push: { rating: "$_id.rating", count: "$count" } }
      }
    }
  ])

1 个答案:

答案 0 :(得分:6)

您离我们并不遥远,我们只需要调整一些事情即可:

db.votes.aggregate([
    {
        $match:
            {
                postId: {$in: [1, 2]}
            }
    },
    {
        $group: {
            _id: {post: "$postId", rating: "$vote"},
            count: {$sum: 1},
            reviews: {$push : "$$ROOT" } //keep the original document
        }
    },
    {
        $group: {
            _id: "$_id.post",
            counts: {$push: {rating: "$_id.rating", count: "$count"}},
            reviews: {$push: "$reviews"},
            totalItemCount: {$sum: "$count"}, //for avg calculation
            totalRating: {$sum: "$_id.rating"} // //for avg calculation
        }
    },
    {
        $project: {
            _id: "$_id",
            avgRating: {$divide: ["$totalRating", "$totalItemCount"]},
            counts: "$counts",
            reviews: {
                $slice: [
                    {
                        $reduce: {
                            input: "$reviews",
                            initialValue: [],
                            in: { $concatArrays: ["$$value", "$$this"] }
                        }
                    },
                    0, //skip
                    10 //limit
                ]
            }

        }
    }
])

请注意,为清晰起见,我保留了当前的管道结构,但是我觉得使用利用$facet的管道可能会更高效,因为我们在分组时不必将整个集合保存在内存中。 我们将其分为两个部分,一个是当前管道减去检阅部分,另一个是$skip$limit阶段。

编辑: $ facet版本:

db.votes.aggregate([
    {
        "$match": {
            "postId": {"$in": [1, 2]}
        }
    },
    {
        "$facet": {
            "numbers": [
                {
                    "$group": {
                        "_id": {
                            "post": "$postId",
                            "rating": "$vote"
                        },
                        "count": {
                            "$sum": 1.0
                        }
                    }
                },
                {
                    "$group": {
                        "_id": "$_id.post",
                        "counts": {
                            "$push": {
                                "rating": "$_id.rating",
                                "count": "$count"
                            }
                        },
                        "totalItemCount": {
                            "$sum": "$count"
                        },
                        "totalRating": {
                            "$sum": "$_id.rating"
                        }
                    }
                }
            ],
            "reviews": [
                {
                    "$skip": 0.0
                },
                {
                    "$limit": 10.0
                }
            ]
        }
    },
    {
        "$unwind": "$numbers"
    },
    {
        "$project": {
            "_id": "$numbers._id",
            "reviews": "$reviews",
            "avgRating": {"$divide": ["$numbers.totalRating", "$numbers.totalItemCount"]},
            "counts": "$numbers.counts"
        }
    }
]);