Docs Menu
Docs Home
/ /
Etapas de la pipeline de agregación

$group (operador de expresión)

$group

La etapa $group combina múltiples documentos que tienen el mismo campo, campos, o expresión en un solo documento de acuerdo con una clave de agrupación. 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 _id en la etapa de pipeline $group para establecer la clave de grupo. Consulta a continuación ejemplos de uso.

En la salida de la etapa $group, el campo _id se 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

$group no ordena sus documentos de salida.

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.

La etapa $group tiene la siguiente forma de prototipo:

{
$group:
{
_id: <expression>, // Group key
<field1>: { <accumulator1> : <expression1> },
...
}
}
Campo
Descripción

_id

Requerido. La expresión _id especifica la clave del grupo. Si especifica un valor _id de null o cualquier otro valor constante, la etapa $group devuelve un único documento que agrega los valores de todos los documentos de entrada. Consulte el ejemplo de Group by Null.

field

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.

$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.

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 $setWindowFields.

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 $setWindowFields.

Devuelve el elemento más bajo dentro de un grupo según el orden de acomodo especificado.

Nuevo en la versión 5.2.

Disponible en las etapas $group y $setWindowFields.

Devuelve una agregación de los campos n inferiores dentro de un grupo, según el orden de clasificación especificado.

Nuevo en la versión 5.2.

Disponible en las etapas $group y $setWindowFields.

Devuelve la cantidad de documentos en un grupo.

Distinto de la etapa de canalización de $count.

Novedades en la versión 5.0: Disponible en las $group y $setWindowFields etapas.

Devuelve un valor del primer documento de cada grupo. El orden solo se define si los documentos están ordenados.

Distinto del $first operador de matriz.

Se cambió en la versión 5.0: disponible en la etapa $setWindowFields.

Devuelve una agregación de los primeros elementos den dentro de un grupo. Solo es significativo cuando los documentos están en un orden definido. Distinto del operador de arreglo $firstN.

Novedad en la versión 5.2: disponible en el $group, expresión y etapas $setWindowFields.

Devuelve un valor del último documento de cada grupo. El orden solo se define si los documentos están ordenados.

Distinto del $last operador de matriz.

Se cambió en la versión 5.0: disponible en la etapa $setWindowFields.

Devuelve una agregación de los últimos elementos n dentro de un grupo. Solo es significativo cuando los documentos están en un orden definido. Distinto del operador de arreglo $lastN.

Novedad en la versión 5.2: disponible en el $group, expresión y etapas $setWindowFields.

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 $setWindowFields.

Devuelve una agregación de los elementos n con el valor máximo en un grupo. Distinto del operador de arreglo $maxN.

Nuevo en la versión 5.2.

Disponible en $group, $setWindowFields y como una expresión.

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 $setWindowFields.

Devuelve una agregación de los n elementos de menor valor en un grupo. Distinto del operador de arreglo $minN.

Nuevo en la versión 5.2.

Disponible en $group, $setWindowFields y como una expresión.

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 $setWindowFields.

Devuelve la desviación estándar poblacional de los valores de entrada.

Se cambió en la versión 5.0: disponible en la etapa $setWindowFields.

Devuelve la desviación estándar muestral de los valores de entrada.

Se cambió en la versión 5.0: disponible en la etapa $setWindowFields.

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 $setWindowFields.

Devuelve el elemento principal dentro de un grupo según el orden de acomodo especificado.

Nuevo en la versión 5.2.

Disponible en las etapas $group y $setWindowFields.

Devuelve una agregación de los campos n principales dentro de un grupo, según el orden de clasificación especificado.

Nuevo en la versión 5.2.

Disponible en las etapas $group y $setWindowFields.

Si la etapa $group supera los 100 megabytes de RAM, MongoDB guarda datos en archivos temporales. Sin embargo, si la opción allowDiskUse se establece en false, $group devuelve un error. Para obtener más información, consulta Límites de pipeline de agregación.

En esta sección, se describen optimizaciones para mejorar el rendimiento de $group. Existen optimizaciones que puedes realizar manualmente y optimizaciones que MongoDB realiza de forma interna.

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" }
}
}
])

Nota

A partir de la versión 6.0.18, el motor de consultas de ejecución basado en ranuras ya no está habilitado de forma predeterminada en las versiones de parche de 6.0. Si desea que sus consultas utilicen el motor de consultas de ejecución basado en ranuras, actualice a la versión 8.0, donde está habilitado de forma predeterminada.

A partir de la 5.2 versión, MongoDB utiliza el motor de consulta de ejecución basado en ranuras para ejecutar etapas $group si:

  • $group es la primera etapa en el pipeline.

  • Todas las etapas anteriores del pipeline también pueden ser ejecutadas por el motor basado en ranuras.

Para obtener más información, consulta $groupOptimizació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

Tip

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" }

Nota

Cuando utiliza $group para recuperar valores distintos en una colección fragmentada, si la operación da como DISTINCT_SCAN resultado, el resultado podría contener documentos huérfanos.

La única canalización semánticamente correcta que se ve afectada es efectivamente un equivalente lógico de un comando, donde hay distinct una $group etapa al comienzo o cerca del comienzo de la canalización y la $group no está precedida por una $sort etapa.

Por ejemplo, las operaciones $group del siguiente formulario pueden resultar en un DISTINCT_SCAN:

{ $group : { _id : "$<field>" } }

Para obtener más información sobre el comportamiento al recuperar valores distintos, consulte el comportamiento del comando distinct.

Para ver si la operación resulta en un DISTINCT_SCAN, se deben consultar los resultados de la explicación de la operación.

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 $group agrupa los documentos por item para recuperar los valores distintos de los elementos. Esta etapa devuelve el totalSaleAmount para cada elemento.
Segunda etapa:
La etapa $match filtra los documentos resultantes para devolver solo los elementos con un totalSaleAmount mayor 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

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") },
])

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 $match filtra los documentos para que solo pasen los documentos del año 2014 a la siguiente etapa.
Segunda etapa:
La etapa $group agrupa 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 $sort ordena 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

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

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 }
])

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" ] }

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:

$group utiliza la variable del sistema $$ROOT para 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:

$addFields añ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 mebibytes.

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
}

Los ejemplos de C# en esta página utilizan la base de datos sample_mflix de los conjuntos de datos de muestra de Atlas. Para aprender a crear un clúster gratuito de MongoDB Atlas y cargar los conjuntos de datos de muestra, consulta Primeros pasos en la documentación del controlador de MongoDB .NET/C#.

La siguiente clase Movie modela los documentos en la colección sample_mflix.movies:

public class Movie
{
public ObjectId Id { get; set; }
public int Runtime { get; set; }
public string Title { get; set; }
public string Rated { get; set; }
public List<string> Genres { get; set; }
public string Plot { get; set; }
public ImdbData Imdb { get; set; }
public int Year { get; set; }
public int Index { get; set; }
public string[] Comments { get; set; }
[BsonElement("lastupdated")]
public DateTime LastUpdated { get; set; }
}

Nota

ConventionPack para Pascal Case

Las clases de C# en esta página utilizan Pascal case para los nombres de sus propiedades, pero los nombres de los campos en la colección de MongoDB utilizan camel case. Para tener en cuenta esta diferencia, se puede usar el siguiente código para registrar un ConventionPack cuando la aplicación se inicie:

var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

Para usar el controlador MongoDB.NET/C# para agregar una $group etapa a una canalización de agregación, llame a Group()método en un PipelineDefinition objeto.

El siguiente ejemplo crea una etapa de pipeline que agrupa los documentos por el valor de su campo Rated. La calificación de cada grupo se muestra en un campo llamado Rating en cada documento de salida. Cada documento de salida también contiene un campo llamado TotalRuntime, cuyo valor es la duración total de todas las películas del grupo.

var pipeline = new EmptyPipelineDefinition<Movie>()
.Group(
id: m => m.Rated,
group: g => new
{
Rating = g.Key,
TotalRuntime = g.Sum(m => m.Runtime)
}
);

Los ejemplos de Node.js en esta página utilizan la base de datos sample_mflix de los conjuntos de datos de muestra de Atlas. Para aprender a crear un clúster gratuito de MongoDB Atlas y cargar los conjuntos de datos de muestra, consulte Primeros pasos en la documentación del controlador de MongoDB Node.js.

Para utilizar el controlador de MongoDB Node.js para agregar una etapa de $group a una canalización de agregación, utilice el Operador $group en un objeto de canalización.

En el siguiente ejemplo, se crea una etapa de pipeline que agrupa los documentos por el valor de su campo rated. Cada documento de salida tiene un campo rating que almacena la calificación de los grupos. Cada documento de salida también tiene un campo llamado totalRuntime que almacena la duración total de todas las películas del grupo. En el siguiente ejemplo, se ejecuta la pipeline de agregación:

const pipeline = [
{
$group: {
_id: "$rated",
rating: { $first: "$rated" },
totalRuntime: { $sum: "$runtime" }
}
}
];
const cursor = collection.aggregate(pipeline);
return cursor;

El tutorial Agrupar y Totalizar Datos proporciona un extenso ejemplo del operador $group en un caso de uso común.

Para aprender más sobre las etapas relacionadas del pipeline, consulta la guía $addFields.

Volver

$graphLookup

Obtén una insignia de habilidad

Domina los “Fundamentos de la transformación de datos” de forma gratuita.

Más información

En esta página