$group (agregação)
Definição
$group
O estágio
$group
separa os documentos em grupos de acordo com uma "chave de grupo". A saída é um documento para cada chave de grupo exclusiva.Uma chave de grupo geralmente é um campo ou grupo de campos. A chave do grupo também pode ser o resultado de uma expressão. Use o campo
_id
no estágio de pipeline$group
para definir a chave do grupo. Veja abaixo exemplos de uso.Na saída do estágio
$group
, o campo_id
é definido como a chave de grupo desse documento.Os documentos de saída também podem conter campos adicionais que são definidos usando expressions acumuladoras.
Observação
$group
não ordena seus documentos de saída.
Compatibilidade
Você pode utilizar o $group
para implantações hospedadas nos seguintes ambientes:
MongoDB Atlas: o serviço totalmente gerenciado para implantações do MongoDB na nuvem
MongoDB Enterprise: a versão autogerenciada e baseada em assinatura do MongoDB
MongoDB Community: uma versão com código disponível, de uso gratuito e autogerenciada do MongoDB
Sintaxe
O estágio $group
tem a seguinte forma de protótipo:
{ $group: { _id: <expression>, // Group key <field1>: { <accumulator1> : <expression1> }, ... } }
Campo | Descrição |
---|---|
| Obrigatório. A |
| Opcional. Computado usando os operadores acumulador . |
O _id
e os operadores acumuladores podem aceitar qualquer expression
válida. Para obter mais informações sobre expressões, consulte Operadores de expressão.
Considerações
Desempenho
$group
é um estágio de bloqueio, que faz com que o pipeline espere que todos os dados de entrada sejam recuperados para o estágio de bloqueio antes de processar os dados. Um estágio de bloqueio pode reduzir o desempenho porque reduz o processamento paralelo de um pipeline com vários estágios. Um estágio de bloqueio também pode usar quantidades substanciais de memória para grandes conjuntos de dados.
Operador acumulador
O operador <accumulator>
deve ser um dos seguintes operadores acumuladores:
Alterado na versão 5.0.
Nome | Descrição |
---|---|
Retorna o resultado de uma função de acumulador definida pelo usuário. | |
Retorna uma array de valores de expressão exclusivos para cada grupo. A ordem dos elementos da array é indefinida. Alterado na versão 5.0: Disponível no estágio | |
Retorna uma média de valores numéricos. Ignora valores não numéricos. Alterado na versão 5.0: Disponível no estágio | |
Retorna o elemento inferior de um grupo de acordo com a ordem de classificação especificada. Novidades na versão 5.2. Disponível nos estágios | |
Retorna uma agregação dos campos Novidades na versão 5.2. Disponível nos estágios | |
Retorna o número de documentos em um grupo. Diferente do estágio Novidade na versão 5.0: Disponível nos estágios | |
Retorna o resultado de uma expressão para o primeiro documento em um grupo. Alterado na versão 5.0: Disponível no estágio | |
Retorna uma agregação dos primeiros Novidade na versão 5.2: Disponível nos estágios | |
Retorna o resultado de uma expressão para o último documento em um grupo. Alterado na versão 5.0: Disponível no estágio | |
Retorna uma agregação dos últimos Novidade na versão 5.2: Disponível nos estágios | |
Retorna o valor de expressão mais alto para cada grupo. Alterado na versão 5.0: Disponível no estágio | |
Retorna uma agregação dos Novidades na versão 5.2. Disponível em | |
Retorna uma aproximação da mediana, o 50º percentil, como um valor escalar. Novidades na versão 7.0. Este operador está disponível como acumulador nestas etapas: Também está disponível como uma expressão de agregação. | |
Retorna um documento criado combinando os documentos de entrada de cada grupo. | |
Retorna o valor de expressão mais baixo para cada grupo. Alterado na versão 5.0: Disponível no estágio | |
Retorna uma agregação dos Novidades na versão 5.2. Disponível em | |
Retorna uma array de valores escalares que correspondem aos valores percentuais especificados. Novidades na versão 7.0. Este operador está disponível como acumulador nestas etapas: Também está disponível como uma expressão de agregação. | |
Retorna uma matriz de valores de expressão para documentos em cada grupo. Alterado na versão 5.0: Disponível no estágio | |
Retorna o desvio padrão da população dos valores de entrada. Alterado na versão 5.0: Disponível no estágio | |
Retorna o desvio padrão da amostra dos valores de entrada. Alterado na versão 5.0: Disponível no estágio | |
Retorna uma soma de valores numéricos. Ignora valores não numéricos. Alterado na versão 5.0: Disponível no estágio | |
Retorna o principal elemento de um grupo de acordo com a ordem de classificação especificada. Novidades na versão 5.2. Disponível nos estágios | |
Retorna uma agregação dos principais campos Novidades na versão 5.2. Disponível nos estágios |
$group
e restrições de memória
Se o estágio $group
exceder 100 megabytes de RAM, o MongoDB gravará os dados em arquivos temporários. No entanto, se a opção allowDiskUse estiver definida como false
, $group
retornará um erro. Para obter mais informações, consulte Limites do pipeline de agregação.
$group
Otimizações de desempenho
Esta seção descreve otimizações para melhorar o desempenho de $group
. Existem otimizações que você pode fazer manualmente e otimizações que o MongoDB faz internamente.
Otimização para retornar o primeiro ou último documento de cada grupo
Se um pipeline sorts
e groups
pelo mesmo campo e o estágio $group
utilizar somente o operador acumulador $first
ou $last
, considere adicionar um índice no campo agrupado que corresponda à ordem de classificação. Em alguns casos, o estágio $group
pode usar o índice para localizar rapidamente o primeiro ou último documento de cada grupo.
Exemplo
Se uma coleção chamada foo
contiver um índice { x: 1, y: 1 }
, o pipeline a seguir poderá usar esse índice para localizar o primeiro documento de cada grupo:
db.foo.aggregate([ { $sort:{ x : 1, y : 1 } }, { $group: { _id: { x : "$x" }, y: { $first : "$y" } } } ])
Mecanismo de execução de consulta baseado em slot
A partir da versão 5.2, O MongoDB utiliza o mecanismo de consulta de execução baseado em slot para executar estágios $group
se:
$group
é o primeiro estágio do pipeline.Todos os estágios anteriores do pipeline também podem ser executados pelo mecanismo de execução baseado em slot.
Para mais informações, consulte Otimização do$group
.
Exemplos
Contagem do número de documentos em uma coleção
No mongosh
, crie uma coleção de amostra denominada sales
com os seguintes documentos:
db.sales.insertMany([ { "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") }, { "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") }, { "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") }, { "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") }, { "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") }, { "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") }, { "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") }, { "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") }, ])
A operação de agregação a seguir usa o estágio $group
para contar o número de documentos na coleção sales
:
db.sales.aggregate( [ { $group: { _id: null, count: { $count: { } } } } ] )
A operação retorna o seguinte resultado:
{ "_id" : null, "count" : 8 }
Esta operação de agregação é equivalente à seguinte declaração SQL:
SELECT COUNT(*) AS count FROM sales
Retrieve Distinct Values
A seguinte operação de agregação utiliza o estágio $group
para recuperar os valores de item distintos da coleção sales
:
db.sales.aggregate( [ { $group : { _id : "$item" } } ] )
A operação retorna o seguinte resultado:
{ "_id" : "abc" } { "_id" : "jkl" } { "_id" : "def" } { "_id" : "xyz" }
Observação
Quando você utiliza $group
para recuperar valores distintos em uma coleção fragmentada, se a operação resultar em DISTINCT_SCAN
um, o resultado poderá conter documentos órfãos.
O único pipeline semanticamente correto afetado é efetivamente o equivalente lógico de um comando, em que há distinct
um $group
estágio no início do pipeline ou próximo a ele e o $group
não é precedido por um estágio $sort
.
Por exemplo, operações $group
do seguinte formulário podem resultar em um DISTINCT_SCAN
:
{ $group : { _id : "$<field>" } }
Para obter mais informações sobre o comportamento de recuperar valores distintos, consulte o comportamento de comando distinto.
Para ver se a sua operação resulta em um,DISTINCT_SCAN
verifique os resultados de explicação da sua operação.
Agrupar por item tendo
A seguinte operação de agregação agrupa documentos pelo campo item
, calculando o valor total de venda por item e retornando apenas os itens com valor total de venda maior ou igual a 100:
db.sales.aggregate( [ // First Stage { $group : { _id : "$item", totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } } } }, // Second Stage { $match: { "totalSaleAmount": { $gte: 100 } } } ] )
- Primeiro estágio:
- O estágio
$group
agrupa os documentos poritem
para recuperar os valores distintos dos itens. Este estágio retornatotalSaleAmount
para cada item. - Segundo estágio:
- O estágio
$match
filtra os documentos resultantes para retornar somente itens com umtotalSaleAmount
maior ou igual a 100.
A operação retorna o seguinte resultado:
{ "_id" : "abc", "totalSaleAmount" : Decimal128("170") } { "_id" : "xyz", "totalSaleAmount" : Decimal128("150") } { "_id" : "def", "totalSaleAmount" : Decimal128("112.5") }
Esta operação de agregação é equivalente à seguinte declaração SQL:
SELECT item, Sum(( price * quantity )) AS totalSaleAmount FROM sales GROUP BY item HAVING totalSaleAmount >= 100
Calcular Count, Sum, e Average
No mongosh
, crie uma coleção de amostra denominada sales
com os seguintes documentos:
db.sales.insertMany([ { "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") }, { "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") }, { "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") }, { "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") }, { "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") }, { "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") }, { "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") }, { "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") }, ])
Agrupar por dia do ano
O pipeline a seguir calcula o valor total de vendas, a quantidade média de vendas e a contagem de vendas para cada dia do ano de 2014:
db.sales.aggregate([ // First Stage { $match : { "date": { $gte: new ISODate("2014-01-01"), $lt: new ISODate("2015-01-01") } } }, // Second Stage { $group : { _id : { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } }, // Third Stage { $sort : { totalSaleAmount: -1 } } ])
- Primeiro estágio:
- O estágio
$match
filtra os documentos para passar apenas os documentos do ano 2014 para o próximo estágio. - Segundo estágio:
- A etapa
$group
agrupa os documentos por data e calcula o valor total da venda, a quantidade média e a contagem total dos documentos em cada grupo. - Terceiro estágio:
- O estágio
$sort
classifica os resultados pelo valor total da venda para cada grupo em ordem decrescente.
A operação retorna os seguintes resultados:
{ "_id" : "2014-04-04", "totalSaleAmount" : Decimal128("200"), "averageQuantity" : 15, "count" : 2 } { "_id" : "2014-03-15", "totalSaleAmount" : Decimal128("50"), "averageQuantity" : 10, "count" : 1 } { "_id" : "2014-03-01", "totalSaleAmount" : Decimal128("40"), "averageQuantity" : 1.5, "count" : 2 }
Esta operação de agregação é equivalente à seguinte declaração SQL:
SELECT date, Sum(( price * quantity )) AS totalSaleAmount, Avg(quantity) AS averageQuantity, Count(*) AS Count FROM sales WHERE date >= '01/01/2014' AND date < '01/01/2015' GROUP BY date ORDER BY totalSaleAmount DESC
Dica
Veja também:
db.collection.countDocuments()
que envolve o estágio de agregação$group
com uma expressão$sum
.
Agrupar por null
A operação de agregação a seguir especifica um grupo _id
de null
, calculando o valor total da venda, a quantidade média e a contagem de todos os documentos na coleção.
db.sales.aggregate([ { $group : { _id : null, totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } } ])
A operação retorna o seguinte resultado:
{ "_id" : null, "totalSaleAmount" : Decimal128("452.5"), "averageQuantity" : 7.875, "count" : 8 }
Esta operação de agregação é equivalente à seguinte declaração SQL:
SELECT Sum(price * quantity) AS totalSaleAmount, Avg(quantity) AS averageQuantity, Count(*) AS Count FROM sales
Dica
Veja também:
db.collection.countDocuments()
que envolve o estágio de agregação$group
com uma expressão$sum
.
Dados dinâmicos
No mongosh
, crie uma coleção de amostra denominada books
com os seguintes documentos:
db.books.insertMany([ { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }, { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }, { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }, { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }, { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 } ])
Agrupar title
por author
A seguinte operação de agregação dinamiza os dados na coleção books
para ter títulos agrupados por autores.
db.books.aggregate([ { $group : { _id : "$author", books: { $push: "$title" } } } ])
A operação retorna os seguintes documentos:
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] } { "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
Agrupar documentos por author
A seguinte operação de agregação agrupa documentos por author
:
db.books.aggregate([ // First Stage { $group : { _id : "$author", books: { $push: "$$ROOT" } } }, // Second Stage { $addFields: { totalCopies : { $sum: "$books.copies" } } } ])
- Primeiro estágio:
$group
usa a variável de sistema$$ROOT
para agrupar os documentos inteiros por autores. Esta etapa passa os seguintes documentos para a próxima etapa:{ "_id" : "Homer", "books" : [ { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }, { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 } ] }, { "_id" : "Dante", "books" : [ { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }, { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }, { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 } ] } - Segundo estágio:
$addFields
adiciona um campo à saída contendo o total de cópias de livros de cada autor.Observação
Os documentos resultantes não devem exceder o limite de Tamanho do documento BSON de 16 megabytes.
A operação retorna os seguintes documentos:
{ "_id" : "Homer", "books" : [ { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }, { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 } ], "totalCopies" : 20 } { "_id" : "Dante", "books" : [ { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }, { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }, { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 } ], "totalCopies" : 5 }
Recursos adicionais
O tutorial Agregação com o conjunto de dados de código postal fornece um exemplo extensivo do operador $group
em um caso de uso comum.