Definição
Compatibilidade
Você pode utilizar o $unwind 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
Você pode passar um operando de caminho do campo ou um operando de documento para desdobrar um campo de array.
Operando de caminho de campo
Você pode fornecer o caminho do campo de array para. Ao $unwind$unwind usar essa sintaxe, não gera um documento se o valor do campo for nulo, ausente ou um array vazio.
{ $unwind: <field path> }
Quando você especifica o caminho do campo, prefixe o nome do campo com um sinal de dólar $ e coloque entre aspas.
Operando do documento com opções
Você pode fornecer um documento para para especificar várias opções de $unwind comportamento.
{ $unwind: { path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
Campo | Tipo | Descrição |
|---|---|---|
string | Caminho do campo para um campo de array. Para especificar um caminho do campo, prefixe o nome do campo com um sinal de dólar | |
string | Opcional. O nome de um novo campo para manter o índice da array do elemento. O nome não pode começar com um sinal de dólar | |
booleano | Opcional.
O valor padrão é |
Comportamentos
Caminho do campo não está em formato de array
Quando o valor em path não resulta em uma array, $unwind se comporta da seguinte maneira:
Se o valor não estiver ausente, não
null, e não for uma array vazia,$unwindgerará um único documento usando o valor como está.Se
includeArrayIndexfor especificado, o índice será0para entradas de array enullpara entradas que não sejam de array. Documentos mais tarde na lista têm um índice maior que0.Se o valor estiver
nullausente, ou uma array vazia,$unwindseguirá a opção preserveNullAndEmptyArrays. QuandoincludeArrayIndexé especificado e o documento é preservado, o índicenullé.
Campo ausente
Se você indicar um caminho de um campo inexistente ou de um campo vazio no documento de entrada,, $unwind o padrão é que o documento não processe nem emita documentos para esse documento de entrada.
Para gerar documentos onde o campo de array está ausente, nulo ou uma array vazia, use a opção preserveNullAndEmptyArrays.
Exemplos
Array de unwind
mongoshEm, crie uma collection de amostra denominada inventory com o seguinte documento:
db.inventory.insertOne({ _id: 1, item: "ABC1", sizes: [ "S", "M", "L"] })
A agregação a seguir usa o estágio para gerar um documento para cada elemento $unwind da sizes array:
db.inventory.aggregate( [ { $unwind : "$sizes" } ] )
A operação retorna os seguintes resultados:
{ _id: 1, item: "ABC1", sizes: "S" } { _id: 1, item: "ABC1", sizes: "M" } { _id: 1, item: "ABC1", sizes: "L" }
Cada documento é uma cópia exata documento de entrada, exceto pelo valor do campo sizes agora apresentando um array baseado no valor sizes original.
Valores ausentes ou não em formato de array
Considere a collection 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 o sizes campo como uma array de elemento único se:
o campo está presente,
o valor não for nulo, e
o valor não é um array vazio.
Expanda as sizes arrays $unwind com:
db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )
A operação $unwind retorna:
{ _id: 1, item: 'Shirt', sizes: 'S' }, { _id: 1, item: 'Shirt', sizes: 'M' }, { _id: 1, item: 'Shirt', sizes: 'L' }, { _id: 3, item: 'Hat', sizes: 'M' }
No
"_id": 1documento,sizesé uma array preenchida. retorna um documento para cada elemento$unwindnosizescampo.No documento,
"_id": 3,sizesfor resolvido para uma array de elemento único.Documentos
"_id": 2, "_id": 4e"_id": 5não retornam nada porque o camposizesnão pode ser reduzido a uma única array de elementos.
Observação
A { path: <FIELD> } sintaxe é opcional. As seguintes operações do são $unwind equivalentes.
db.clothing.aggregate( [ { $unwind: "$sizes" } ] ) db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )
preserveNullAndEmptyArrays e a includeArrayIndex
Os preserveNullAndEmptyArrays exemplos e utilizam a seguinte collection:includeArrayIndex
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
A operação a seguir $unwind usa a opção preserveNullAndEmptyArrays para incluir documentos cujo sizes campo é nulo, ausente ou uma array vazia.
db.inventory2.aggregate( [ { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } } ] )
A saída inclui os documentos onde o campo sizes é nulo, ausente ou uma array vazia:
{ _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
A seguinte operação $unwind do utiliza a opção includeArrayIndex para incluir o índice de array na saída.
db.inventory2.aggregate( [ { $unwind: { path: "$sizes", includeArrayIndex: "arrayIndex" } }])
A operação desenrola a array sizes e inclui o índice da array no novo campo arrayIndex. Se o campo sizes não resolver para uma array preenchida, mas não estiver ausente, nulo ou uma array vazia, o campo arrayIndex será 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 desenrolados
mongoshNo, crie uma collection de amostra denominada inventory2 com os seguintes 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 } ])
O pipeline a seguir desenrola a array sizes e agrupa os documentos resultantes pelos valores de tamanho de desenrolamento:
db.inventory2.aggregate( [ // First Stage { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } }, // Second Stage { $group: { _id: "$sizes", averagePrice: { $avg: "$price" } } }, // Third Stage { $sort: { "averagePrice": -1 } } ] )
- Primeiro estágio:
O estágio gera um novo documento para cada elemento
$unwinddasizesarray. O estágio usa a opção preserveNullAndEmptyArrays para incluir na saída os documentos em quesizeso campo está ausente, é nulo ou é uma array vazia. Este estágio passa os seguintes documentos para o próximo estágio:{ _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 } - Segundo estágio:
A etapa
$groupagrupa os documentos porsizese calcula o preço médio de cada tamanho. Este estágio passa os seguintes documentos para o próximo estágio:{ _id: "S", averagePrice: Decimal128("80") } { _id: "L", averagePrice: Decimal128("80") } { _id: "M", averagePrice: Decimal128("120") } { _id: null, averagePrice: Decimal128("45.25") } - Terceiro estágio:
O estágio
$sortclassifica os documentos poraveragePriceem ordem decrescente. A operação retorna o seguinte resultado:{ _id : "M", averagePrice: Decimal128("120") } { _id : "L", averagePrice: Decimal128("80") } { _id : "S", averagePrice: Decimal128("80") } { _id : null, averagePrice: Decimal128("45.25") }
Desenrole arrays incorporadas
mongoshNo, crie uma collection de amostra denominada sales com os seguintes 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") } ] } ])
A operação a seguir agrupa os itens vendidos por suas marcações e calcula o valor total de vendas por cada marcação.
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" ] } } } } ])
- Primeira etapa
O primeiro estágio gera um novo documento para cada elemento
$unwinddaitemsarray:{ _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
O segundo estágio gera um novo documento para cada elemento
$unwinddasitems.tagsarrays:{ _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 } } - Terceiro estágio
O estágio
$groupagrupa os documentos pela tag e calcula o valor total de vendas de itens com cada tag:{ _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") }
Os exemplos de C# nesta página utilizam o banco de dados sample_mflix a partir dos conjuntos de dados de amostra do Atlas. Para saber como criar um cluster MongoDB Atlas gratuito e carregar os conjuntos de dados de exemplo, consulte Introdução na documentação do driver MongoDB .NET/C#.
A seguinte classe Movie modela os documentos na collection 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; } }
Observação
ConventionPack para Pascal Case
As classes C# nesta página usam Pascal case para seus nomes de propriedade, mas os nomes de campo na coleção MongoDB usam Camel case. Para considerar essa diferença, você pode usar o seguinte código para registrar um ConventionPack quando o aplicativo iniciar:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
Para usar o driver MongoDB .NET/C# para adicionar um $unwind estágio a uma agregação pipeline, chame o método Unwind() em um PipelineDefinition objeto.
O exemplo a seguir cria um estágio de pipeline que itera sobre o campo Genres em cada documento de entrada Movie . Para cada valor no campo Genres , o estágio cria um novo documento Movie e preenche seu campo Genres com o valor Genres do documento de entrada.
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres);
Você pode usar um objeto AggregateUnwindOptions para personalizar o comportamento do Unwind() método. O exemplo a seguir executa a mesma operação que o exemplo anterior, mas também inclui as seguintes opções:
PreserveNullAndEmptyArraysgarante que documentos que contêm uma array vazia no campoGenressejam incluídos na saída.A opção
IncludeArrayIndexadiciona um novo campo denominadoIndexa cada documento de saída. O valor deste campo é o índice da array do valor do campoGenresna arrayGenresdo 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) });
Os exemplos do Node.js nesta página utilizam o banco de dados do sample_mflix a partir dos conjuntos de dados de amostra do Atlas. Para saber como criar um cluster gratuito do MongoDB Atlas e carregar os conjuntos de dados de exemplo, consulte Introdução na documentação do driver do MongoDB Node.js
Para usar o driver Node.js do MongoDB para adicionar um estágio $unwind a um pipeline de agregação , use o operador $unwind em um objeto de pipeline.
O exemplo a seguir cria um estágio de pipeline que itera sobre o campo genres em cada documento de entrada movie . Para cada valor no campo genres , o estágio cria um novo documento movie e preenche seu campo genres com o valor genres do documento de entrada. Em seguida, o exemplo executa o agregação pipeline:
const pipeline = [{ $unwind: "$genres" }]; const cursor = collection.aggregate(pipeline); return cursor;
Você pode personalizar o comportamento do método $unwind. O exemplo a seguir executa a mesma operação que o exemplo anterior, mas também inclui as seguintes opções:
preserveNullAndEmptyArraysgarante que documentos que contêm uma array vazia no campogenressejam incluídos na saída.includeArrayIndexadiciona um novo campo denominadoindexa cada documento de saída. O campo contém o índice da array do valorgenresno campogenresdo documento de entrada.
const pipeline = [ { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true, includeArrayIndex: "index" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Saiba mais
Para saber mais sobre métodos relacionados, consulte os guias $group, $sum e $multiply.
Para ver como usar em um $unwind exemplo completo, consulte o tutorial Desenrolar arrays e dados de grupo.