Definición
$groupLa etapa
$groupsepara los documentos en grupos según una clave de grupo. El resultado es un documento por cada clave de grupo única.Una clave de grupo suele ser un campo o un conjunto de campos. La clave de grupo también puede ser el resultado de una expresión. Utiliza el campo
_iden la etapa de pipeline$grouppara establecer la clave de grupo. Consulta a continuación ejemplos de uso.En la salida de la etapa
$group, el campo_idse establece en la clave de grupo para ese documento.Los documentos de salida también pueden incluir campos adicionales que se configuran mediante expresiones de acumulador.
Nota
$groupno ordena sus documentos de salida.
Compatibilidad
Puedes usar $group para implementaciones alojadas en los siguientes entornos:
MongoDB Atlas: El servicio totalmente gestionado para implementaciones de MongoDB en la nube
MongoDB Enterprise: La versión basada en suscripción y autogestionada de MongoDB
MongoDB Community: La versión de MongoDB con código fuente disponible, de uso gratuito y autogestionada.
Sintaxis
La etapa $group tiene la siguiente forma de prototipo:
{ $group: { _id: <expression>, // Group key <field1>: { <accumulator1> : <expression1> }, ... } }
Campo | Descripción |
|---|---|
| Requerido. La expresión |
| Opcional. Calculado utilizando los operadores de acumulación. |
El _id y los operadores de acumulación pueden aceptar cualquier expression válida. Para obtener más información sobre las expresiones, consulta Expresiones.
Considerations
Rendimiento
$group es una etapa de bloqueo, que hace que el pipeline espere a que se recuperen todos los datos de entrada para la etapa de bloqueo antes del procesamiento de los datos. Una etapa de bloqueo puede reducir el rendimiento porque disminuye el procesamiento paralelo en un pipeline con múltiples etapas. Una etapa de bloqueo también puede utilizar cantidades sustanciales de memoria para grandes conjuntos de datos.
Operador de acumulación
El operador <accumulator> debe ser uno de los siguientes operadores de acumulación:
Modificado en la versión 5.0.
Nombre | Descripción |
|---|---|
Devuelve el resultado de una función de acumulador definida por el usuario. | |
Devuelve un arreglo de valores de expresión únicos para cada grupo. El orden de los elementos del arreglo es indefinido. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve un promedio de valores numéricos. Ignora los valores no numéricos. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve la cantidad de documentos en un grupo. Distinto de la etapa de canalización de Novedades en la versión 5.0: Disponible en las | |
Devuelve el resultado de una expresión para el primer documento de un grupo. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve el resultado de una expresión para el último documento de un grupo. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve el valor de la expresión más alto para cada grupo. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve un documento creado al combinar los documentos de entrada para cada grupo. | |
Devuelve el valor de la expresión más bajo para cada grupo. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve un arreglo de valores de expresión para los documentos en cada grupo. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve la desviación estándar poblacional de los valores de entrada. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve la desviación estándar muestral de los valores de entrada. Se cambió en la versión 5.0: disponible en la etapa | |
Devuelve la suma de valores numéricos. Ignora los valores no numéricos. Se cambió en la versión 5.0: disponible en la etapa |
$group y restricciones de memoria
La etapa $group tiene un límite de 100 megabytes de RAM. Por defecto, si la etapa supera este límite, $group devuelve un error. Para permitir más espacio para el procesamiento por etapas, utiliza la opción allowDiskUse para activar que las etapas del pipeline de agregación guarden datos en archivos temporales.
Optimización para devolver el primer documento de cada grupo
Si una secuencia sorts y del mismo groups campo y la etapa $group solo usan el operador acumulador,$first considere agregar un índice al campo agrupado que coincida con el orden de clasificación. En algunos casos, la etapa puede usar el índice para encontrar rápidamente el primer documento de cada $group grupo.
Ejemplo
Si una colección llamada foo contiene un índice { x: 1, y: 1 }, la siguiente pipeline puede usar ese índice para encontrar el primer documento de cada grupo:
db.foo.aggregate([ { $sort:{ x : 1, y : 1 } }, { $group: { _id: { x : "$x" }, y: { $first : "$y" } } } ])
Ejemplos
Contar el número de documentos en una colección
En mongosh, cree una colección de muestra llamada sales con los siguientes 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") }, ])
La siguiente operación de agregación utiliza la etapa para contar la cantidad de documentos en $group la sales colección:
db.sales.aggregate( [ { $group: { _id: null, count: { $count: { } } } } ] )
La operación devuelve el siguiente resultado:
{ "_id" : null, "count" : 8 }
Esta operación de agregación es equivalente a la siguiente instrucción SQL:
SELECT COUNT(*) AS count FROM sales
Retrieve Distinct Values
La siguiente operación de agregación utiliza la etapa $group para recuperar los valores distintos de los elementos de la colección sales:
db.sales.aggregate( [ { $group : { _id : "$item" } } ] )
La operación devuelve el siguiente resultado:
{ "_id" : "abc" } { "_id" : "jkl" } { "_id" : "def" } { "_id" : "xyz" }
Agrupar por artículo que contiene
La siguiente operación de agregación agrupa los documentos por el campo item, y calcula el importe total de venta por artículo y devuelve solo los artículos con un importe total de venta mayor o igual a 100:
db.sales.aggregate( [ // First Stage { $group : { _id : "$item", totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } } } }, // Second Stage { $match: { "totalSaleAmount": { $gte: 100 } } } ] )
- Primera etapa:
- La etapa
$groupagrupa los documentos poritempara recuperar los valores distintos de los elementos. Esta etapa devuelve eltotalSaleAmountpara cada elemento. - Segunda etapa:
- La etapa
$matchfiltra los documentos resultantes para devolver solo los elementos con untotalSaleAmountmayor o igual a 100.
La operación devuelve el siguiente resultado:
{ "_id" : "abc", "totalSaleAmount" : Decimal128("170") } { "_id" : "xyz", "totalSaleAmount" : Decimal128("150") } { "_id" : "def", "totalSaleAmount" : Decimal128("112.5") }
Esta operación de agregación es equivalente a la siguiente instrucción SQL:
SELECT item, Sum(( price * quantity )) AS totalSaleAmount FROM sales GROUP BY item HAVING totalSaleAmount >= 100
Tip
Calcular el recuento, la suma y el promedio
En mongosh, cree una colección de muestra llamada sales con los siguientes 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 día del año
El siguiente pipeline calcula el importe total de ventas, la cantidad media de ventas y el recuento de ventas para cada día del año 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 } } ])
- Primera etapa:
- La etapa
$matchfiltra los documentos para que solo pasen los documentos del año 2014 a la siguiente etapa. - Segunda etapa:
- La etapa
$groupagrupa los documentos por fecha y calcula el monto total de la venta, la cantidad promedio y el conteo total de los documentos en cada grupo. - Tercera etapa:
- La etapa
$sortordena los resultados por el monto total de venta de cada grupo en orden descendente.
La operación devuelve los siguientes 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 operación de agregación es equivalente a la siguiente instrucción 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
Tip
db.collection.countDocuments()que envuelve la etapa de agregación$groupcon una expresión$sum.
Agrupar por null
La siguiente operación de agregación especifica un grupo _id de null, y calcula el importe total de venta, la cantidad promedio y el recuento de todos los documentos en la colección.
db.sales.aggregate([ { $group : { _id : null, totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } } ])
La operación devuelve el siguiente resultado:
{ "_id" : null, "totalSaleAmount" : Decimal128("452.5"), "averageQuantity" : 7.875, "count" : 8 }
Esta operación de agregación es equivalente a la siguiente instrucción SQL:
SELECT Sum(price * quantity) AS totalSaleAmount, Avg(quantity) AS averageQuantity, Count(*) AS Count FROM sales
Tip
db.collection.countDocuments()que envuelve la etapa de agregación$groupcon una expresión$sum.
Pivot Data
En mongosh, cree una colección de muestra llamada books con los siguientes 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
La siguiente operación de agregación ordena los datos de la colección books para agrupar los títulos por autores.
db.books.aggregate([ { $group : { _id : "$author", books: { $push: "$title" } } } ])
La operación devuelve los siguientes documentos:
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] } { "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
Agrupar documentos por author
La siguiente operación de agregación agrupa documentos por author:
db.books.aggregate([ // First Stage { $group : { _id : "$author", books: { $push: "$$ROOT" } } }, // Second Stage { $addFields: { totalCopies : { $sum: "$books.copies" } } } ])
- Primera etapa:
$grouputiliza la variable del sistema$$ROOTpara agrupar todos los documentos por autores. Esta etapa pasa los siguientes documentos a la siguiente 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 } ] } - Segunda etapa:
$addFieldsañade un campo a la salida que contiene el total de copias de libros para cada autor.Nota
Los documentos resultantes no deben exceder el límite de tamaño de documento BSON de 16 megabytes.
La operación devuelve los siguientes 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 }
Tip
Recursos adicionales
El tutorial Agregación con el conjunto de datos de código postal proporciona un ejemplo extenso del $group operador en un caso de uso común.