如何计算符合给定条件的子文档的平均值之和?

2024-04-20 14:04:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我的文档结构如下:

[
    {
        series_id: 0,
        books: [
            {
                book_id: 0,
                scores: [
                    {
                        critic_id: 0,
                        score : 7.5
                    },
                    {
                        critic_id: 1,
                        score : 8.5
                    },
                    {
                        critic_id: 2,
                        score : 2.5
                    }
                ]
            },
            {
                book_id: 1,
                scores: [
                    {
                        critic_id: 0,
                        score : 5.5
                    },
                    {
                        critic_id: 1,
                        score : 7.5
                    },
                    {
                        critic_id: 2,
                        score : 9.5
                    }
                ]
            }
        ]
    },
    ...
]

现在,我想找到一个系列,在给定评论家ID列表的情况下,每本书的平均得分总和最高(最好是有序的)。你知道吗

所以,举个例子,我想找出评论家得分最高的系列[0,2]。这将返回:

[
    {
        series_id: 0,
        score: 12.5 
    },
    ...
]

或者只是序列ID的有序列表:

[ 0, ... ]

因为评论家0和2的平均值是5,评论家0和2的平均值是7.5。加起来是12.5

现在我被困在:

return list(db['series']).find(sort=[("series.books.scores", 1)])

Tags: 文档id列表情况books结构平均值series
1条回答
网友
1楼 · 发布于 2024-04-20 14:04:47

从版本3.2开始,$avg累加器表达式以前只在$group阶段可用,现在也在$project阶段可用,我们可以利用它来缩短以前的管道。你知道吗

为此,您可以在$redact阶段之后$project您的文档,并在投影中使用$avg运算符,我们可以返回^{}返回的分数数组的平均值,然后使用$group返回预期结果。你知道吗

db.series.aggregate([
    { '$match': {
        'books.scores.critic_id': { '$in': [ 0,2 ] }
    }},
    { '$unwind': '$books' }, 
    { '$project': { 
        'series_id': 1, 
        'book_id': '$books.book_id', 
        'scores': '$books.scores'
    }},
    { '$redact': {
        '$cond': [
            { '$or': [
                { '$eq': [ '$critic_id', 0 ] }, 
                { '$eq': [ '$critic_id', 2 ] },
                { '$not': '$critic_id' }
            ]}, 
            '$$DESCEND', '$$PRUNE'
        ]
    }}, 
    { '$project': { 
        'series_id': 1, 
        'score': {
            '$avg': { 
                '$map': { 
                    'input': '$scores', 
                    'as': 'score', 
                    'in': '$$score.score'
                }
            }
        }
    }}, 
    { '$group': { 
        '_id': '$_id', 
        'series_id': { '$first': '$series_id' }, 
        'score': { '$sum': '$score' }
    }}
])

由此产生:

{ "_id" : ObjectId("56543c98571635184da33953"), "series_id" : 0, "score" : 12.5 }

在mongodb3.2之前,您需要对“books”数组进行反规范化,然后$project我们的文档。然后,我们可以使用^{}返回值来减小将在下一阶段处理的文档的大小。然后是$unwind$group阶段。你知道吗

db.series.aggregate([
    { '$match': { 
        'books.scores.critic_id': { '$in': [ 0, 2 ] }
    }},
    { '$unwind': '$books' }, 
    { '$project': { 
        'series_id': 1, 
        'book_id': '$books.book_id', 
        'scores': '$books.scores'
    }},
    { '$redact': {
        '$cond': [
            { '$or': [
                { '$eq': [ '$critic_id', 0 ] }, 
                { '$eq': [ '$critic_id', 2 ] }, 
                { '$not': '$critic_id' } 
            ]}, 
            '$$DESCEND', '$$PRUNE'
        ]
    }}, 
    { '$unwind': '$scores' },
    { '$group': { 
        '_id': '$book_id', 
        'series_id': { '$first': '$series_id' }, 
        'avgScores': { '$avg': '$scores.score' }
    }},
    { '$group': {
        '_id': '$series_id', 
        'score': { '$sum': '$avgScores' }
    }} 
])

由此产生:

{ "_id" : 0, "score" : 12.5 }

另一种方法是首先使用^{}运算符筛选出“critical\u id”不是^{}[0, 2]的文档。我们管道中的下一步是^{}阶段,用于对“books”和“scores”数组进行反规范化。从那里你需要两个^{}阶段。第一个用来计算“score”的^{},第二个用来返回这些平均值的^{}。你知道吗

db.series.aggregate([
    { '$match': {
        'books.scores.critic_id': { '$in': [ 0,2 ] }
    }}, 
    { '$unwind': '$books' }, 
    { '$unwind': '$books.scores' },
    { '$match': { 
        'books.scores.critic_id': { '$in': [ 0, 2 ] }
    }}, 
    { '$group': { 
        '_id': '$books.book_id',
        'series_id': { '$first': '$series_id' }, 
        'total': { '$avg': '$books.scores.score' }
    }}, 
    { '$group': { 
        '_id': '$series_id', 
        'score': { '$sum': '$total' }
    }}
])

返回:

{ "_id" : 0, "score" : 12.5 }

始终可以向管道的末尾添加可选的^{}阶段,如下所示:

{ '$project': { 
    'series_id': '$_id', 
    'score': 1, 
    '_id': 0
}} 

要返回此项:

{ "score" : 12.5, "series_id" : 0 }

但这会导致性能下降。你知道吗

值得注意的是,PyMongo返回一个游标,因此您需要在游标上循环并打印结果。

相关问题 更多 >