将书籍分组并计算每个评分

Group books and get count for each of their ratings

我有一个评级模型,其中包含一本书和评级值。我想获得数据库中每本书的所有评分(评分从 1 到 5 不等)。

我的架构看起来像 -

    {
      "_id": ObjectId("57e112312a52fe257e5d1d5c"),
      "book": ObjectId("57e111142a52fe257e5d1d42"),
      "rating": 4
    }
    {
      "_id": ObjectId("57e7a002420d22d6106a4715"),
      "book": ObjectId("57e111142a52fe257e5d1d42"),
      "rating": 5
    }
    {
      "_id": ObjectId("57e7a4cd98bfdb5a11962d54"),
      "book": ObjectId("57e111142a52fe257e5d17676"),
      "rating": 5
    }
    {
      "_id": ObjectId("57e7a4cd98bfdb5a11962d54"),
      "book": ObjectId("57e111142a52fe257e5d17676"),
      "rating": 1
    }

目前,我只能得到这一点,我可以获得每本书的评分数,但没有准确指定评分值计数。

这是我当前的查询 -

    db.ratings.aggregate([
        {$match: {book: {$in: [ObjectId("57e111142a52fe257e5d1d42"), ObjectId('57e6bef7cad79fa38555c643')]}}},
        {$group: {_id: {book: "$book", value: "$value"} } },
        {$group: {_id: "$_id.book", total: {$sum: 1}}},
    ])

输出是这样的 -

    {
        "result": [
            {
                "_id": ObjectId("57e6bef7cad79fa38555c643"),
                "total": 2
            },
            {
                "_id": ObjectId("57e111142a52fe257e5d1d42"),
                "total": 2
            }
        ],
        "ok": 1
    }

但是,我想合并所有文档并获得包含 rating 字段每个值的评分计数的结果,如下所示。重点是我只想要每本书的每个值的评分数。

    {
        result: [
            {
                _id: "57e111142a52fe257e5d17676",
                5_star_ratings: 1,
                4_star_ratings: 3,
                3_star_ratings: 4,
                2_star_ratings: 1,
                1_star_ratings: 0,
            },
            {
                _id: "57e111142a52fe257e5d1d42",
                5_star_ratings: 10,
                4_star_ratings: 13,
                3_star_ratings: 7,
                2_star_ratings: 8,
                1_star_ratings: 19,
            }
            .
            .
            .
            .
        ]
    }

我该怎么做?

完成任务需要一个$group管道,分别使用$cond operator in the $sum accumulator operator. The $cond operator will evaluate a logical condition based on its first argument (if) and then returns the second argument where the evaluation is true (then) or the third argument where false (else). This converts the true/false logic into 1 and 0 numerical values that feed into $sum:

{
    "$sum": { 
        "$cond": [ { "$eq": [ "$rating", 1 ] }, 1, 0 ]
    }
}

作为结果操作,您可能需要 运行 以下聚合管道:

var pipeline = [
    { 
        "$match": {
            "book": {
                "$in": [
                    ObjectId("57e111142a52fe257e5d1d42"), 
                    ObjectId('57e6bef7cad79fa38555c643')
                ]
            }
        }
    },
    { 
        "$group": { 
            "_id": "$book",             
            "5_star_ratings": {
                "$sum": {
                    "$cond": [ { "$eq": [ "$rating", 5 ] }, 1, 0 ]
                }
            },
            "4_star_ratings": {
                "$sum": {
                    "$cond": [ { "$eq": [ "$rating", 4 ] }, 1, 0 ]
                }
            },
            "3_star_ratings": {
                "$sum": {
                    "$cond": [ { "$eq": [ "$rating", 3 ] }, 1, 0 ]
                }
            },
            "2_star_ratings": {
                "$sum": {
                    "$cond": [ { "$eq": [ "$rating", 2 ] }, 1, 0 ]
                }
            },
            "1_star_ratings": {
                "$sum": {
                    "$cond": [ { "$eq": [ "$rating", 1 ] }, 1, 0 ]
                }
            }           
        }  
    },
]

db.ratings.aggregate(pipeline)

要获得执行速度比上述方法快得多的更灵活、性能更好的方法,请考虑运行使用如下替代管道

db.ratings.aggregate([
    { 
        "$match": {
            "book": {
                "$in": [
                    ObjectId("57e111142a52fe257e5d1d42"), 
                    ObjectId('57e6bef7cad79fa38555c643')
                ]
            }
        }
    },
    { 
        "$group": {
            "_id": { 
                "book": "$name",
                "rating": "$rating"
            },
            "count": { "$sum": 1 }
        }
    },
    { 
        "$group": {
            "_id": "$_id.book",
            "counts": {
                "$push": {
                    "rating": "$_id.rating",
                    "count": "$count"
                }
            }
        }
    }
])