$unwind (agregação)
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 $unwind
. Ao utilizar essa sintaxe, $unwind
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 passar um documento para $unwind
para especificar várias opções de 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 |
Comportamentos
Caminho do campo não está em formato de array
Quando o operando não for um array, mas não estiver ausente,
null
ou um array vazio,$unwind
trata o operando como um array que de um único elemento.Quando o operando for
null
, ausente ou uma array vazia,$unwind
segue o comportamento configurado para a opção preserveNullAndEmptyArrays.
Campo ausente
Se você indicar um caminho de um campo inexistente ou de um array vazio no documento de entrada, $unwind
, o padrão é que o sistema 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
No mongosh
, crie uma coleção de amostra denominada inventory
com o seguinte documento:
db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })
A agregação a seguir utiliza o estágio $unwind
para gerar um documento para cada elemento da array sizes
:
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 campo sizes
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 arrays do sizes
com $unwind
:
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 documento
"_id": 1
,sizes
é uma array preenchida.$unwind
retorna um documento para cada elemento no camposizes
.No documento,
"_id": 3
,sizes
for resolvido para uma array de elemento único.Documentos
"_id": 2, "_id": 4
e"_id": 5
não retornam nada porque o camposizes
não pode ser reduzido a uma única array de elementos.
Observação
A sintaxe { path: <FIELD> }
é opcional. As seguintes operações do $unwind
são equivalentes.
db.clothing.aggregate( [ { $unwind: "$sizes" } ] ) db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )
preserveNullAndEmptyArrays
e a includeArrayIndex
Os exemplos preserveNullAndEmptyArrays
e includeArrayIndex
utilizam a seguinte coleção:
db.inventory2.insertMany([ { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] }, { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] }, { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" }, { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") }, { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null } ])
preserveNullAndEmptyArrays
A operação $unwind
a seguir utiliza a opção preserveNullAndEmptyArrays para incluir documentos cujo campo sizes
é 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" : NumberDecimal("80"), "sizes" : "S" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" } { "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") } { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" } { "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") } { "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }
includeArrayIndex
A seguinte operação do $unwind
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" : NumberDecimal("80"), "sizes" : "S", "arrayIndex" : NumberLong(0) } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M", "arrayIndex" : NumberLong(1) } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L", "arrayIndex" : NumberLong(2) } { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M", "arrayIndex" : null }
Agrupar por valores desenrolados
No mongosh
, crie uma coleção de amostra denominada inventory2
com os seguintes documentos:
db.inventory2.insertMany([ { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] }, { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] }, { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" }, { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") }, { "_id" : 5, "item" : "XYZ", price: NumberDecimal("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
$unwind
gera um novo documento para cada elemento da arraysizes
. O estágio utiliza a opção preserveNullAndEmptyArrays para incluir na saída os documentos em que o camposizes
está ausente, é nulo ou é uma array vazia. Esta etapa passa os seguintes documentos para a próxima etapa:{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" } { "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") } { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" } { "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") } { "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null } - Segundo estágio:
A etapa
$group
agrupa os documentos porsizes
e 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" : NumberDecimal("80") } { "_id" : "L", "averagePrice" : NumberDecimal("80") } { "_id" : "M", "averagePrice" : NumberDecimal("120") } { "_id" : null, "averagePrice" : NumberDecimal("45.25") } - Terceiro estágio:
O estágio
$sort
classifica os documentos poraveragePrice
em ordem decrescente. A operação retorna o seguinte resultado:{ "_id" : "M", "averagePrice" : NumberDecimal("120") } { "_id" : "L", "averagePrice" : NumberDecimal("80") } { "_id" : "S", "averagePrice" : NumberDecimal("80") } { "_id" : null, "averagePrice" : NumberDecimal("45.25") }
Desenrole arrays incorporadas
No mongosh
, crie uma coleção de amostra denominada sales
com os seguintes documentos:
db.sales.insertMany([ { _id: "1", "items" : [ { "name" : "pens", "tags" : [ "writing", "office", "school", "stationary" ], "price" : NumberDecimal("12.00"), "quantity" : NumberInt("5") }, { "name" : "envelopes", "tags" : [ "stationary", "office" ], "price" : NumberDecimal("19.95"), "quantity" : NumberInt("8") } ] }, { _id: "2", "items" : [ { "name" : "laptop", "tags" : [ "office", "electronics" ], "price" : NumberDecimal("800.00"), "quantity" : NumberInt("1") }, { "name" : "notepad", "tags" : [ "stationary", "school" ], "price" : NumberDecimal("14.95"), "quantity" : NumberInt("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
$unwind
gera um novo documento para cada elemento da arrayitems
:{ "_id" : "1", "items" : { "name" : "pens", "tags" : [ "writing", "office", "school", "stationary" ], "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "envelopes", "tags" : [ "stationary", "office" ], "price" : NumberDecimal("19.95"), "quantity" : 8 } } { "_id" : "2", "items" : { "name" : "laptop", "tags" : [ "office", "electronics" ], "price" : NumberDecimal("800.00"), "quantity" : 1 } } { "_id" : "2", "items" : { "name" : "notepad", "tags" : [ "stationary", "school" ], "price" : NumberDecimal("14.95"), "quantity" : 3 } } - Segunda etapa
O segundo estágio
$unwind
gera um novo documento para cada elemento nas arraysitems.tags
:{ "_id" : "1", "items" : { "name" : "pens", "tags" : "writing", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "pens", "tags" : "office", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "pens", "tags" : "school", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "pens", "tags" : "stationary", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "envelopes", "tags" : "stationary", "price" : NumberDecimal("19.95"), "quantity" : 8 } } { "_id" : "1", "items" : { "name" : "envelopes", "tags" : "office", "price" : NumberDecimal("19.95"), "quantity" : 8 } } { "_id" : "2", "items" : { "name" : "laptop", "tags" : "office", "price" : NumberDecimal("800.00"), "quantity" : 1 } } { "_id" : "2", "items" : { "name" : "laptop", "tags" : "electronics", "price" : NumberDecimal("800.00"), "quantity" : 1 } } { "_id" : "2", "items" : { "name" : "notepad", "tags" : "stationary", "price" : NumberDecimal("14.95"), "quantity" : 3 } } { "_id" : "2", "items" : { "name" : "notepad", "tags" : "school", "price" : NumberDecimal("14.95"), "quantity" : 3 } } - Terceiro estágio
O estágio
$group
agrupa os documentos pela tag e calcula o valor total de vendas de itens com cada tag:{ "_id" : "writing", "totalSalesAmount" : NumberDecimal("60.00") } { "_id" : "stationary", "totalSalesAmount" : NumberDecimal("264.45") } { "_id" : "electronics", "totalSalesAmount" : NumberDecimal("800.00") } { "_id" : "school", "totalSalesAmount" : NumberDecimal("104.85") } { "_id" : "office", "totalSalesAmount" : NumberDecimal("1019.60") }