DEV Community

Cover image for Mais performance e insight rápidos no Aggregation Framework - $facet
Leandro Domingues
Leandro Domingues

Posted on

Mais performance e insight rápidos no Aggregation Framework - $facet

Fala pessoALL, blz? Hoje vou mostrar um recurso muito bacana no Aggregation Pipeline, mais especificamente um estágio que pode ser muito útil para nos fornecer uma visão com múltiplas dimensões dos dados e até mesmo aumentar a performance da nossa agregação.

Estágios do Aggregation Pipeline

Para quem não tem familiaridade com o Aggregation Framework, um dos componentes são os estágios do pipeline. Os estágios contém desde as mais simples operações como um filtro através do $match até operações complexas de agrupamento e "separação" dos dados com $group ou $bucket. Hoje veremos o $facet.

O Dataset

Como base para nosso exemplo utilizarei uma base de dados que contém os dados demográficos de eleitores do Brasil. Essa base originalmente é disponibilizada através de um conjunto de arquivos .csv que eu já importei para uma instância do MongoDB Atlas. Abaixo a estrutura do documento:

{
    "_id" : ObjectId("5d532411541c5b6d0cfe90d3"),
    "ano" : 2018,
    "uf" : "SP",
    "codigoCidade" : 64777,
    "nomeCidade" : "GUARULHOS",
    "codigoSitBiometrica" : 1,
    "sitBiometrica" : "Biométrico",
    "zona" : 393,
    "codigoGenero" : 2,
    "genero" : "MASCULINO",
    "codigoEstadoCivil" : 7,
    "estadoCivil" : "SEPARADO JUDICIALMENTE",
    "codigoFaixaEtaria" : 6064,
    "faixaEtaria" : "60 a 64 anos",
    "codigoEscolaridade" : 8,
    "escolaridade" : "SUPERIOR COMPLETO",
    "qtdPerfil" : 13,
    "qtdBiometria" : 13,
    "qtdDeficiencia" : 1,
    "qtdNomeSocial" : 0
}
Enter fullscreen mode Exit fullscreen mode

Veja que os dados estão classificados por perfil, ou seja, cada documento da minha coleção tem um total de pessoas que se encaixam em um determinado perfil. No exemplo acima podemos dizer que temos 13 pessoas no seguinte perfil:

  • cidade: Guarulhos
  • zona: 393
  • genero: MASCULINO
  • estado civil: SEPARADO JUDICIALMENTE
  • faixa etária: 60 a 64 anos
  • escolaridade: SUPERIOR COMPLETO

Se quisermos saber o total de eleitores por gênero, poderíamos fazer um $group simples:

db.eleitorado.aggregate([
    {
        $group: {
            _id: "$genero",
            total: {
                $sum: "$qtdPerfil"
            }
        }
     }
])
Enter fullscreen mode Exit fullscreen mode
/* 1 */
{
    "_id" : "MASCULINO",
    "total" : 69902977
}

/* 2 */
{
    "_id" : "FEMININO",
    "total" : 77339897
}

/* 3 */
{
    "_id" : "NÃO INFORMADO",
    "total" : 63401
}
Enter fullscreen mode Exit fullscreen mode

Isso nos permite agrupar por um dos atributos, no caso gênero, e se fizermos dessa maneira teremos que executar várias vezes o aggregate, trocando o atributo e teremos resultados separados. Utilizando essa abordagem perdermos muita performance e eficiência, por isso podemos utilizar o $facet!

Múltiplas dimensões

O operador $facet nos permite executar múltiplos estágios em paralelo e obter o resultado de cada um desses estágios de uma única vez. Dentro de cada execução do $facet podemos utilizar vários operadores, como $group, $unwind, $sort, etc.

O exemplo a seguir incrementa o nosso pipeline colocando um filtro por UF e Cidade e gera um agrupamento por cada um dos atributos contidos no documento:

db.eleitorado.aggregate([
    {
        $match: {
            "uf": "SP",
            "nomeCidade": "GUARULHOS"
        }
    }, 
    {
        $facet: {
            "genero": [ 
                {
                    $group: {
                        _id:"$genero",
                        total: {
                            $sum: "$qtdPerfil"
                        }
                    }
                },
                {
                    $sort: {
                        total: -1
                    }
                }
            ],
            "estadoCivil": [
                {
                        $group: {
                        _id: "$estadoCivil",
                        total: {
                            $sum: "$qtdPerfil"
                        }
                    }  
                },
                {
                    $sort: {
                        _id: 1
                    }
                }
            ],
            "escolaridade": [
                {
                    $group: {
                        _id: "$escolaridade",
                        total: {
                            $sum: "$qtdPerfil"
                        }
                    }
                },
                {
                    $sort: {
                        _id: 1
                    }
                }
            ],
            "faixaEtaria": [
                {
                    $group: {
                        _id: "$faixaEtaria",
                        total: {
                            $sum: "$qtdPerfil"
                        }
                    }
                },
                {
                    $sort: {
                        _id: 1
                    }
                }
            ]
        }
    }
])
Enter fullscreen mode Exit fullscreen mode
/* 1 */
{
    "genero" : [ 
        {
            "_id" : "FEMININO",
            "total" : 439778
        }, 
        {
            "_id" : "MASCULINO",
            "total" : 374563
        }, 
        {
            "_id" : "NÃO INFORMADO",
            "total" : 1
        }
    ],
    "estadoCivil" : [ 
        {
            "_id" : "CASADO",
            "total" : 335326
        }, 
        {
            "_id" : "DIVORCIADO",
            "total" : 42696
        }, 
        {
            "_id" : "NÃO INFORMADO",
            "total" : 2
        }, 
        {
            "_id" : "SEPARADO JUDICIALMENTE",
            "total" : 8363
        }, 
        {
            "_id" : "SOLTEIRO",
            "total" : 403840
        }, 
        {
            "_id" : "VIÚVO",
            "total" : 24115
        }
    ],
    "escolaridade" : [ 
        {
            "_id" : "ANALFABETO",
            "total" : 13341
        }, 
        {
            "_id" : "ENSINO FUNDAMENTAL COMPLETO",
            "total" : 57995
        }, 
        {
            "_id" : "ENSINO FUNDAMENTAL INCOMPLETO",
            "total" : 140105
        }, 
        {
            "_id" : "ENSINO MÉDIO COMPLETO",
            "total" : 321555
        }, 
        {
            "_id" : "ENSINO MÉDIO INCOMPLETO",
            "total" : 94857
        }, 
        {
            "_id" : "LÊ E ESCREVE",
            "total" : 18788
        }, 
        {
            "_id" : "SUPERIOR COMPLETO",
            "total" : 122057
        }, 
        {
            "_id" : "SUPERIOR INCOMPLETO",
            "total" : 45644
        }
    ],
    "faixaEtaria" : [ 
        {
            "_id" : "100 anos ou mais",
            "total" : 4
        }, 
        {
            "_id" : "16 anos",
            "total" : 1464
        }, 
        {
            "_id" : "17 anos",
            "total" : 5595
        }, 
        {
            "_id" : "18 anos",
            "total" : 16434
        }, 
        {
            "_id" : "19 anos",
            "total" : 20556
        }, 
        {
            "_id" : "20 anos",
            "total" : 21905
        }, 
        {
            "_id" : "21 a 24 anos",
            "total" : 82067
        }, 
        {
            "_id" : "25 a 29 anos",
            "total" : 83577
        }, 
        {
            "_id" : "30 a 34 anos",
            "total" : 88977
        }, 
        {
            "_id" : "35 a 39 anos",
            "total" : 91908
        }, 
        {
            "_id" : "40 a 44 anos",
            "total" : 83734
        }, 
        {
            "_id" : "45 a 49 anos",
            "total" : 77979
        }, 
        {
            "_id" : "50 a 54 anos",
            "total" : 71406
        }, 
        {
            "_id" : "55 a 59 anos",
            "total" : 59686
        }, 
        {
            "_id" : "60 a 64 anos",
            "total" : 49362
        }, 
        {
            "_id" : "65 a 69 anos",
            "total" : 34511
        }, 
        {
            "_id" : "70 a 74 anos",
            "total" : 16506
        }, 
        {
            "_id" : "75 a 79 anos",
            "total" : 5951
        }, 
        {
            "_id" : "80 a 84 anos",
            "total" : 2005
        }, 
        {
            "_id" : "85 a 89 anos",
            "total" : 578
        }, 
        {
            "_id" : "90 a 94 anos",
            "total" : 117
        }, 
        {
            "_id" : "95 a 99 anos",
            "total" : 13
        }, 
        {
            "_id" : "Inválido",
            "total" : 7
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Notem que para o agrupamento de gênero, utilizei o estágio $sort para exibir pelo total em ordem decrescente, já para os outros utilizei o nome do perfil para classificar por ordem alfabética. Isso demonstra que os estágios são executados de forma independente e podem conter "sub-estágios" diferentes entre eles.

Bom, a ideia era demonstrar que podemos ter um pouco mais de performance e eficiência na classificação e agrupamento dos dados em uma coleção no MongoDB utilizando o Aggregation Framework.

Espero que tenham gostado, fiquem à vontade em compartilhar e comentar!

Até a próxima!

Top comments (0)