MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Calcular a média de campos em documentos/matriz incorporados


A estrutura de agregação no MongoDB 3.4 e mais recente oferece o $reduce operador que calcula eficientemente o total sem a necessidade de dutos extras. Considere usá-lo como uma expressão para retornar o total de avaliações e obter o número de avaliações usando $size . Junto com $addFields , a média pode ser calculada usando o operador aritmético $divide como na fórmula average = total ratings/number of ratings :
db.collection.aggregate([
    { 
        "$addFields": { 
            "rating_average": {
                "$divide": [
                    { // expression returns total
                        "$reduce": {
                            "input": "$ratings",
                            "initialValue": 0,
                            "in": { "$add": ["$$value", "$$this.rating"] }
                        }
                    },
                    { // expression returns ratings count
                        "$cond": [
                            { "$ne": [ { "$size": "$ratings" }, 0 ] },
                            { "$size": "$ratings" }, 
                            1
                        ]
                    }
                ]
            }
        }
    }           
])

Saída de amostra
{
    "_id" : ObjectId("58ab48556da32ab5198623f4"),
    "title" : "The Hobbit",
    "ratings" : [ 
        {
            "title" : "best book ever",
            "rating" : 5.0
        }, 
        {
            "title" : "good book",
            "rating" : 3.5
        }
    ],
    "rating_average" : 4.25
}

Com versões mais antigas, você precisaria primeiro aplicar o $unwind operador nas ratings array primeiro como sua etapa inicial do pipeline de agregação. Isso irá desconstruir as ratings campo array dos documentos de entrada para gerar um documento para cada elemento. Cada documento de saída substitui a matriz por um valor de elemento.

O segundo estágio do pipeline seria o $group operador que agrupa documentos de entrada pelo _id e title keys e aplica o $avg desejado expressão de acumulador para cada grupo que calcula a média. Existe outro operador de acumulador $push que preserva o campo de matriz de classificações original retornando uma matriz de todos os valores resultantes da aplicação de uma expressão a cada documento no grupo acima.

A etapa final do pipeline é o $project operador que remodela cada documento no fluxo, como adicionando o novo campo ratings_average .

Portanto, se, por exemplo, você tiver um documento de amostra em sua coleção (como acima e abaixo):
db.collection.insert({
    "title": "The Hobbit",

    "ratings": [
        {
            "title": "best book ever",
            "rating": 5
        },
        {
            "title": "good book",
            "rating": 3.5
        }
    ]
})

Para calcular a média da matriz de classificações e projetar o valor em outro campo ratings_average , você pode aplicar o seguinte pipeline de agregação:
db.collection.aggregate([
    {
        "$unwind": "$ratings"
    },
    {
        "$group": {
            "_id": {
                "_id": "$_id",
                "title": "$title"
            },
            "ratings":{
                "$push": "$ratings"
            },
            "ratings_average": {
                "$avg": "$ratings.rating"
            }
        }
    },
    {
        "$project": {
            "_id": 0,
            "title": "$_id.title",
            "ratings_average": 1,
            "ratings": 1
        }
    }
])

Resultado :
/* 1 */
{
    "result" : [ 
        {
            "ratings" : [ 
                {
                    "title" : "best book ever",
                    "rating" : 5
                }, 
                {
                    "title" : "good book",
                    "rating" : 3.5
                }
            ],
            "ratings_average" : 4.25,
            "title" : "The Hobbit"
        }
    ],
    "ok" : 1
}