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

$unwind (agregación)

$unwind

Descompone un campo de arreglo de los documentos de entrada para producir un documento para cada elemento. Cada documento de salida es el documento de entrada con el valor del campo del arreglo reemplazado por el elemento.

Puedes usar $unwind 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.

Puede pasar un operando de ruta de campo o un operando de documento para descomponer un campo de arreglo.

Puedes pasar la ruta de campo del arreglo a $unwind. Al utilizar esta sintaxis, $unwind no genera un documento si el valor del campo es nulo, está ausente o es un arreglo vacío.

{ $unwind: <field path> }

Cuando especifiques la ruta de campo, antepón el nombre del campo con un signo de dólar $ y enciérralo entre comillas.

Puedes pasar un documento a $unwind para especificar varias opciones de comportamiento.

{
$unwind:
{
path: <field path>,
includeArrayIndex: <string>,
preserveNullAndEmptyArrays: <boolean>
}
}
Campo
Tipo
Descripción

string

Ruta de campo a un campo de arreglo. Para especificar una ruta de campo, anteponer el nombre del campo con un signo de dólar $ y encerrarlo entre comillas.

string

Opcional. El nombre de un nuevo campo para contener el índice del arreglo del elemento. El nombre no puede comenzar con un signo de dólar $.

booleano

Opcional.

  • Si true, si el path es nulo, está ausente o es un arreglo vacío, $unwind genera el documento.

  • Si false, si path es nulo, está ausente o es un arreglo vacío, $unwind no genera un documento.

El valor por defecto es false.

  • Cuando el operando no se resuelve en un arreglo, pero no está ausente, null, o no es un arreglo vacío, $unwind trata el operando como un arreglo de un solo elemento.

  • Cuando el operando es null, falta o un arreglo vacío $unwind sigue el comportamiento establecido para la opción preserveNullAndEmptyArrays.

Si especificas una ruta para un campo que no existe en un documento de entrada o el campo es un arreglo vacío, $unwind, por defecto, ignora el documento de entrada y no se generarán documentos para ese documento de entrada.

Para generar documentos donde el campo de arreglo esté ausente, sea nulo o un arreglo vacío, utiliza la opción preserveNullAndEmptyArrays.

En mongosh, crea una colección de muestra llamada inventory con el siguiente documento:

db.inventory.insertOne({ _id: 1, item: "ABC1", sizes: [ "S", "M", "L"] })

La siguiente agregación utiliza la etapa $unwind para generar un documento por cada elemento del arreglo sizes:

db.inventory.aggregate( [ { $unwind : "$sizes" } ] )

La operación devuelve los siguientes resultados:

{ _id: 1, item: "ABC1", sizes: "S" }
{ _id: 1, item: "ABC1", sizes: "M" }
{ _id: 1, item: "ABC1", sizes: "L" }

Cada documento es idéntico al documento de entrada, excepto por el valor del campo sizes, que ahora contiene un valor del arreglo sizes original.

Considere la colección clothing:

db.clothing.insertMany([
{ _id: 1, item: "Shirt", sizes: [ "S", "M", "L"] },
{ _id: 2, item: "Shorts", sizes: [ ] },
{ _id: 3, item: "Hat", sizes: "M" },
{ _id: 4, item: "Gloves" },
{ _id: 5, item: "Scarf", sizes: null }
])

$unwind trata el campo sizes como un arreglo de un solo elemento si:

  • el campo está presente,

  • el valor no es nulo, y

  • el valor no es un arreglo vacío.

Expanda los sizes arreglos con $unwind:

db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )

La operación $unwind devuelve:

{ _id: 1, item: 'Shirt', sizes: 'S' },
{ _id: 1, item: 'Shirt', sizes: 'M' },
{ _id: 1, item: 'Shirt', sizes: 'L' },
{ _id: 3, item: 'Hat', sizes: 'M' }
  • En el documento "_id": 1, sizes es un arreglo poblado. $unwind devuelve un documento para cada elemento en el campo sizes.

  • En el documento "_id": 3, sizes se resuelve en un arreglo de un solo elemento.

  • Los documentos "_id": 2, "_id": 4 y "_id": 5 no devuelven nada porque el campo sizes no se puede reducir a un arreglo de un solo elemento.

Nota

La sintaxis { path: <FIELD> } es opcional. Las siguientes operaciones $unwind son equivalentes.

db.clothing.aggregate( [ { $unwind: "$sizes" } ] )
db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )

Los preserveNullAndEmptyArrays y includeArrayIndex ejemplos utilizan la siguiente colección:

db.inventory2.insertMany([
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: [ "S", "M", "L"] },
{ _id: 2, item: "EFG", price: Decimal128("120"), sizes: [ ] },
{ _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" },
{ _id: 4, item: "LMN" , price: Decimal128("10") },
{ _id: 5, item: "XYZ", price: Decimal128("5.75"), sizes: null }
])

La siguiente operación $unwind utiliza la opción preserveNullAndEmptyArrays para incluir documentos cuyo campo sizes es nulo, está ausente o es un arreglo vacío.

db.inventory2.aggregate( [
{ $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } }
] )

La salida incluye aquellos documentos donde el campo sizes es nulo, está ausente o es un arreglo vacío:

{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "S" }
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "M" }
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "L" }
{ _id: 2, item: "EFG", price: Decimal128("120") }
{ _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" }
{ _id: 4, item: "LMN", price: Decimal128("10") }
{ _id: 5, item: "XYZ", price: Decimal128("5.75"), sizes: null }

La siguiente operación $unwind utiliza la opción includeArrayIndex para incluir el índice del arreglo en el resultado.

db.inventory2.aggregate( [
{
$unwind:
{
path: "$sizes",
includeArrayIndex: "arrayIndex"
}
}])

La operación desenvuelve el arreglo sizes e incluye el índice del arreglo en el nuevo campo arrayIndex. Si el campo sizes no se resuelve en un arreglo poblado pero no falta, no es nulo ni es un arreglo vacío, el campo arrayIndex es null.

{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "S", arrayIndex: Long(0) }
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "M", arrayIndex: Long(1) }
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "L", arrayIndex: Long(2) }
{ _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M", arrayIndex: null }

En mongosh, cree una colección de muestra llamada inventory2 con los siguientes documentos:

db.inventory2.insertMany([
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: [ "S", "M", "L"] },
{ _id: 2, item: "EFG", price: Decimal128("120"), sizes: [ ] },
{ _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" },
{ _id: 4, item: "LMN" , price: Decimal128("10") },
{ _id: 5, item: "XYZ", price: Decimal128("5.75"), sizes: null }
])

El siguiente pipeline desenrolla el arreglo sizes y agrupa los documentos resultantes por los valores de tamaño desenrollado:

db.inventory2.aggregate( [
// First Stage
{
$unwind: { path: "$sizes", preserveNullAndEmptyArrays: true }
},
// Second Stage
{
$group:
{
_id: "$sizes",
averagePrice: { $avg: "$price" }
}
},
// Third Stage
{
$sort: { "averagePrice": -1 }
}
] )
Primera etapa:

La etapa $unwind genera un nuevo documento para cada elemento en el arreglo sizes. La etapa utiliza la opción preserveNullAndEmptyArrays para incluir en la salida aquellos documentos donde el campo sizes esté ausente, sea nulo o un arreglo vacío. Esta etapa pasa los siguientes documentos a la siguiente etapa:

{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "S" }
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "M" }
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "L" }
{ _id: 2, item: "EFG", price: Decimal128("120") }
{ _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" }
{ _id: 4, item: "LMN", price: Decimal128("10") }
{ _id: 5, item: "XYZ", price: Decimal128("5.75"), sizes: null }
Segunda etapa:

La $group etapa agrupa los documentos por sizes y calcula el precio promedio de cada tamaño. Esta etapa pasa los siguientes documentos a la siguiente etapa:

{ _id: "S", averagePrice: Decimal128("80") }
{ _id: "L", averagePrice: Decimal128("80") }
{ _id: "M", averagePrice: Decimal128("120") }
{ _id: null, averagePrice: Decimal128("45.25") }
Tercera etapa:

La etapa $sort ordena los documentos por averagePrice en orden descendente. La operación devuelve el siguiente resultado:

{ _id : "M", averagePrice: Decimal128("120") }
{ _id : "L", averagePrice: Decimal128("80") }
{ _id : "S", averagePrice: Decimal128("80") }
{ _id : null, averagePrice: Decimal128("45.25") }

Tip

En mongosh, cree una colección de muestra llamada sales con los siguientes documentos:

db.sales.insertMany( [
{
_id: "1",
items: [
{
name: "pens",
tags: [ "writing", "office", "school", "stationary" ],
price: Decimal128("12.00"),
quantity: Int32("5")
},
{
name: "envelopes",
tags: [ "stationary", "office" ],
price: Decimal128("19.95"),
quantity: Int32("8")
}
]
},
{
_id: "2",
items: [
{
name: "laptop",
tags: [ "office", "electronics" ],
price: Decimal128("800.00"),
quantity: Int32("1")
},
{
name: "notepad",
tags: [ "stationary", "school" ],
price: Decimal128("14.95"),
quantity: Int32("3")
}
]
}
])

La siguiente operación agrupa los artículos vendidos por sus etiquetas y calcula el monto total de ventas por cada etiqueta.

db.sales.aggregate([
// First Stage
{ $unwind: "$items" },
// Second Stage
{ $unwind: "$items.tags" },
// Third Stage
{
$group:
{
_id: "$items.tags",
totalSalesAmount:
{
$sum: { $multiply: [ "$items.price", "$items.quantity" ] }
}
}
}
])
Primera etapa

La primera etapa $unwind genera un nuevo documento para cada elemento en el arreglo items:

{ _id: "1", items: { name: "pens", tags: [ "writing", "office", "school", "stationary" ], price: Decimal128("12.00"), quantity: 5 } }
{ _id: "1", items: { name: "envelopes", tags: [ "stationary", "office" ], price: Decimal128("19.95"), quantity: 8 } }
{ _id: "2", items: { name: "laptop", tags: [ "office", "electronics" ], price: Decimal128("800.00"), quantity": 1 } }
{ _id: "2", items: { name: "notepad", tags: [ "stationary", "school" ], price: Decimal128("14.95"), quantity: 3 } }
Segunda etapa

La segunda etapa $unwind produce un nuevo documento para cada elemento en los arreglos items.tags:

{ _id: "1", items: { name: "pens", tags: "writing", price: Decimal128("12.00"), quantity: 5 } }
{ _id: "1", items: { name: "pens", tags: "office", price: Decimal128("12.00"), quantity: 5 } }
{ _id: "1", items: { name: "pens", tags: "school", price: Decimal128("12.00"), quantity: 5 } }
{ _id: "1", items: { name: "pens", tags: "stationary", price: Decimal128("12.00"), quantity: 5 } }
{ _id: "1", items: { name: "envelopes", tags: "stationary", price: Decimal128("19.95"), quantity: 8 } }
{ _id: "1", items: { name: "envelopes", tags: "office", "price" : Decimal128("19.95"), quantity: 8 } }
{ _id: "2", items: { name: "laptop", tags: "office", price: Decimal128("800.00"), quantity: 1 } }
{ _id: "2", items: { name: "laptop", tags: "electronics", price: Decimal128("800.00"), quantity: 1 } }
{ _id: "2", items: { name: "notepad", tags: "stationary", price: Decimal128("14.95"), quantity: 3 } }
{ _id: "2", items: { name: "notepad", "ags: "school", price: Decimal128("14.95"), quantity: 3 } }
Tercera etapa

La $group etapa agrupa los documentos por la etiqueta y calcula el importe total de ventas de artículos con cada etiqueta:

{ _id: "writing", totalSalesAmount: Decimal128("60.00") }
{ _id: "stationary", totalSalesAmount: Decimal128("264.45") }
{ _id: "electronics", totalSalesAmount: Decimal128("800.00") }
{ _id: "school", totalSalesAmount: Decimal128("104.85") }
{ _id: "office", totalSalesAmount: Decimal128("1019.60") }

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 $unwind etapa a una canalización de agregación, llame a Unwind()método en un PipelineDefinition objeto.

El siguiente ejemplo crea una plataforma de pipeline que itera sobre el campo Genres en cada documento Movie de entrada. Para cada valor en el campo Genres, la plataforma crea un nuevo documento Movie y rellena su campo Genres con el valor Genres del documento de entrada.

var pipeline = new EmptyPipelineDefinition<Movie>()
.Unwind(m => m.Genres);

Puede utilizar un objeto AggregateUnwindOptions para personalizar el comportamiento del método Unwind(). El siguiente ejemplo realiza la misma operación que el ejemplo anterior, pero también incluye las siguientes opciones:

  • PreserveNullAndEmptyArrays asegura que los documentos que contienen un arreglo vacío en el campo Genres se incluyan en el resultado.

  • La opción IncludeArrayIndex agrega un nuevo campo llamado Index a cada documento de salida. El valor de este campo es el índice del valor del campo Genres en el arreglo Genres del documento de entrada.

var pipeline = new EmptyPipelineDefinition<Movie>()
.Unwind(m => m.Genres,
new AggregateUnwindOptions<Movie>()
{
PreserveNullAndEmptyArrays = true,
IncludeArrayIndex = new ExpressionFieldDefinition<Movie, int>(
m => m.Index)
});

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 $unwind a una canalización de agregación, utilice el Operador $unwind en un objeto de canalización.

El siguiente ejemplo crea una plataforma de pipeline que itera sobre el campo genres en cada documento movie de entrada. Para cada valor en el campo genres, la plataforma crea un nuevo documento movie y rellena su campo genres con el valor genres del documento de entrada. A continuación, el ejemplo ejecuta el pipeline de agregación:

const pipeline = [{ $unwind: "$genres" }];
const cursor = collection.aggregate(pipeline);
return cursor;

Puedes personalizar el comportamiento del método $unwind. El siguiente ejemplo realiza la misma operación que el ejemplo anterior, pero también incluye las siguientes opciones:

  • preserveNullAndEmptyArrays asegura que los documentos que contienen un arreglo vacío en el campo genres se incluyan en el resultado.

  • includeArrayIndex añade un nuevo campo llamado index a cada documento de salida. El campo contiene el índice del arreglo del valor genres en el campo genres del documento de entrada.

const pipeline = [
{
$unwind: {
path: "$genres",
preserveNullAndEmptyArrays: true,
includeArrayIndex: "index"
}
}
];
const cursor = collection.aggregate(pipeline);
return cursor;

Para aprender más sobre métodos relacionados, consulte las guías $group, $sum y $multiply.

Para ver cómo usar $unwind en un ejemplo completo, consulta el tutorial descomponer arreglos y agrupar datos.

Volver

$unset

En esta página