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

Mongodb - consulta os totais de hoje, os totais da semana e os totais do mês em uma consulta


Esta é mais uma questão de como você espera que a saída se pareça, pois qualquer resultado agregado precisa essencialmente ser agrupado no nível mais baixo e, em seguida, agrupar progressivamente em "grãos" mais altos até que o nível mais alto ( "mês") seja alcançado. Esse tipo de dado implica em dados agrupados por "mês" em última análise, a menos que você os divida de outra forma.

Em essência, progressivamente $group :
db.collection.aggregate([
    // First total per day. Rounding dates with math here
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$createdAt", new Date(0) ] },
                    { "$mod": [
                        { "$subtract": [ "$createdAt", new Date(0) ] },
                        1000 * 60 * 60 * 24
                    ]}                        
                ]},
                new Date(0)
            ]
        },
        "week": { "$first": { "$week": "$createdAt" } },
        "month": { "$first": { "$month": "$createdAt" } },
        "total": { "$sum": "$num" }
    }},

    // Then group by week
    { "$group": {
        "_id": "$week",
        "month": { "$first": "$month" },
        "days": {
            "$push": {
                "day": "$_id",
                "total": "$total"
            }
        },
        "total": { "$sum": "$total" }
    }},

    // Then group by month
    { "$group": {
        "_id": "$month",
        "weeks": {
            "$push": {
                "week": "$_id",
                "total": "$total",
                "days": "$days"
            }
        },
        "total": { "$sum": "$total" }
    }}
])

Portanto, cada nível após o primeiro que soma por dia é progressivamente inserido no conteúdo da matriz para seu valor de "arredondamento" e os totais também são somados nesse nível.

Se você quiser uma saída mais plana com um registro por dia contendo seus totais semanais e mensais, bem como o total do dia, basta anexar dois $unwind instruções para o final do pipeline:
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }

E opcionalmente $project os campos "pontilhados" para algo mais simples e legível, se necessário.

Se você estiver abrangendo "anos" com isso, inclua essa operação na chave de agrupamento pelo menos a partir do nível "semana" para que você não combine dados de anos diferentes e eles sejam separados.

Também é minha preferência geral usar a "date math" abordagem ao arredondar datas, pois retorna uma Date objeto, mas como é usado em outros níveis além de "dia", você pode usar alternadamente o operadores de agregação de data em vez de.

Não há necessidade de mapReduce como isso é bastante intuitivo e há uma quantidade finita de dias em um mês, o que significa que o limite de BSON ao aninhar matrizes no conteúdo durante a agregação não será quebrado.