$unwind în MongoDB & NoSQL

Agregare & Analytics MongoDB 2.2

Etapa $unwind desface un tablou: produce câte un document pentru fiecare element, ca să-l poți grupa sau filtra.

Sintaxă
{ $unwind: '$<camp>' }
{ $unwind: { path: '$<camp>', includeArrayIndex: <nume>, preserveNullAndEmptyArrays: <bool> } }

Ce face $unwind

$unwind: '$produse' transformă o comandă cu trei produse în trei documente, fiecare cu un singur produs în câmpul produse. Așa poți număra câte comenzi conțin fiecare produs, cu $group după element.

Implicit, documentele cu tablou gol, null sau fără câmp dispar. Cu preserveNullAndEmptyArrays: true (MongoDB 3.2+) ele rămân, fără câmpul respectiv. includeArrayIndex adaugă poziția elementului în tablou.

Parametri

Parametru Descriere
path Câmpul tablou, cu $ în față.
preserveNullAndEmptyArrays Păstrează documentele fără elemente (MongoDB 3.2+).
includeArrayIndex Numele câmpului în care se pune indicele elementului (MongoDB 3.2+).

Ce returnează

Câte un document pentru fiecare element al tabloului.

Exemple

Exemplele de pe această pagină pornesc de la datele de mai jos. Rulează mai întâi acest cod, apoi exemplul.

use magazin
db.comenzi.insertMany([
  { _id: 1, client: 'Ana', oras: 'Iași', total: 120, status: 'livrata', produse: ['caiet', 'pix'] },
  { _id: 2, client: 'Dan', oras: 'Cluj', total: 85, status: 'noua', produse: ['carte'] },
  { _id: 3, client: 'Ioana', oras: 'Iași', total: 240, status: 'livrata', produse: ['rucsac', 'caiet', 'pix'] },
  { _id: 4, client: 'Mihai', oras: 'Brașov', total: 60, status: 'anulata', produse: [] }
])

Exemplul 1: O comandă cu trei produse devine trei documente

Restul câmpurilor se copiază în fiecare document.

db.comenzi.aggregate([{ $match: { _id: 3 } }, { $unwind: '$produse' }, { $project: { client: 1, produse: 1 } }])

Ieșire în mongosh

[
  { _id: 3, client: 'Ioana', produse: 'rucsac' },
  { _id: 3, client: 'Ioana', produse: 'caiet' },
  { _id: 3, client: 'Ioana', produse: 'pix' }
]

Exemplul 2: Cele mai vândute produse

$unwind + $group numără aparițiile fiecărui produs.

db.comenzi.aggregate([{ $unwind: '$produse' }, { $group: { _id: '$produse', comenzi: { $sum: 1 } } }, { $sort: { comenzi: -1, _id: 1 } }])

Ieșire în mongosh

[
  { _id: 'caiet', comenzi: 2 },
  { _id: 'pix', comenzi: 2 },
  { _id: 'carte', comenzi: 1 },
  { _id: 'rucsac', comenzi: 1 }
]

Exemplul 3: Păstrarea comenzilor fără produse

Comanda lui Mihai are tabloul gol; cu preserveNullAndEmptyArrays rămâne, fără câmpul produse.

db.comenzi.aggregate([{ $match: { _id: 4 } }, { $unwind: { path: '$produse', preserveNullAndEmptyArrays: true } }, { $project: { client: 1, produse: 1 } }])

Ieșire în mongosh

[ { _id: 4, client: 'Mihai' } ]

Greșeli frecvente

  • Fără preserveNullAndEmptyArrays, numărătorile pe documente „pierd” comenzile fără produse.
  • $unwind pe tablouri mari multiplică numărul de documente; filtrează cu $match înainte.

Comenzi înrudite

  • $group: Etapa $group grupează documentele după o cheie și calculează totaluri: $sum, $avg, $min, $max, $push, $addToSet.
  • $lookup: Etapa $lookup aduce documente dintr-o altă colecție, ca un LEFT JOIN, și le pune într-un câmp tablou.
  • $all: Operatorul $all găsește documentele în care un tablou conține toate valorile date, în orice ordine.