Definición
Compatibilidad
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.
Sintaxis
Puede pasar un operando de ruta de campo o un operando de documento para descomponer un campo de arreglo.
Operando de ruta de campo
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.
Operando del documento con opciones
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 | |
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 |
Comportamientos
Ruta de campo sin arreglo
Cuando el operando no se resuelve en un arreglo, pero no está ausente,
null, o no es un arreglo vacío,$unwindtrata el operando como un arreglo de un solo elemento.Cuando el operando es
null, falta o un arreglo vacío$unwindsigue el comportamiento establecido para la opción preserveNullAndEmptyArrays.
Campo ausente
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.
Ejemplos
Desenrollar un arreglo
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.
Valores faltantes o no en forma de arreglo
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,sizeses un arreglo poblado.$unwinddevuelve un documento para cada elemento en el camposizes.En el documento
"_id": 3,sizesse resuelve en un arreglo de un solo elemento.Los documentos
"_id": 2, "_id": 4y"_id": 5no devuelven nada porque el camposizesno 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" } } ] )
preserveNullAndEmptyArrays y includeArrayIndex
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 } ])
preserveNullAndEmptyArrays
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 }
includeArrayIndex
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 }
Agrupar por valores desglosados
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
$unwindgenera un nuevo documento para cada elemento en el arreglosizes. La etapa utiliza la opción preserveNullAndEmptyArrays para incluir en la salida aquellos documentos donde el camposizesesté 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
$groupetapa agrupa los documentos porsizesy 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
$sortordena los documentos poraveragePriceen 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") }
Desenrollar arreglos incrustados
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
$unwindgenera un nuevo documento para cada elemento en el arregloitems:{ _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
$unwindproduce un nuevo documento para cada elemento en los arreglositems.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
$groupetapa 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; } [] 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:
PreserveNullAndEmptyArraysasegura que los documentos que contienen un arreglo vacío en el campoGenresse incluyan en el resultado.La opción
IncludeArrayIndexagrega un nuevo campo llamadoIndexa cada documento de salida. El valor de este campo es el índice del valor del campoGenresen el arregloGenresdel 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:
preserveNullAndEmptyArraysasegura que los documentos que contienen un arreglo vacío en el campogenresse incluyan en el resultado.includeArrayIndexañade un nuevo campo llamadoindexa cada documento de salida. El campo contiene el índice del arreglo del valorgenresen el campogenresdel documento de entrada.
const pipeline = [ { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true, includeArrayIndex: "index" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Obtén más información
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.