$facet în MongoDB & NoSQL

Agregare & Analytics MongoDB 3.4

Etapa $facet rulează mai multe sub-pipeline-uri pe aceleași documente și întoarce toate rezultatele într-un singur document.

Sintaxă
{ $facet: { <nume1>: [ <etape> ], <nume2>: [ <etape> ], ... } }

Ce face $facet

$facet (MongoDB 3.4+) primește documentele o singură dată și le trimite în paralel prin mai multe pipeline-uri, fiecare cu numele lui. Rezultatul este un singur document, cu câte un tablou pentru fiecare nume.

E folosit des pentru pagini de căutare: într-o singură interogare obții rezultatele paginii curente, numărul total și filtrele laterale (câte produse pe categorie, pe interval de preț). În interiorul unui facet nu poți folosi $out, $merge sau alt $facet.

Parametri

Parametru Descriere
nume: [ etape ] Câte un sub-pipeline pentru fiecare rezultat dorit.

Ce returnează

Un singur document, cu câte un câmp tablou pentru fiecare sub-pipeline.

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: Un tablou de bord într-o singură interogare

Vânzări pe oraș, statistici generale și cea mai mare comandă, calculate din aceleași date.

db.comenzi.aggregate([
  { $facet: {
    peOrase: [{ $group: { _id: '$oras', comenzi: { $sum: 1 } } }, { $sort: { comenzi: -1, _id: 1 } }],
    statistici: [{ $group: { _id: null, total: { $sum: '$total' }, comenzi: { $sum: 1 } } }],
    top: [{ $sort: { total: -1 } }, { $limit: 1 }, { $project: { client: 1, total: 1 } }]
  } }
])

Ieșire în mongosh

[
  {
    peOrase: [
      { _id: 'Iași', comenzi: 2 },
      { _id: 'Brașov', comenzi: 1 },
      { _id: 'Cluj', comenzi: 1 }
    ],
    statistici: [ { _id: null, total: 505, comenzi: 4 } ],
    top: [ { _id: 3, client: 'Ioana', total: 240 } ]
  }
]

Exemplul 2: Paginare cu numărul total

Pagina întâi (2 rezultate) și numărul total de comenzi livrate sau noi.

db.comenzi.aggregate([
  { $match: { status: { $in: ['livrata', 'noua'] } } },
  { $facet: {
    pagina: [{ $sort: { _id: 1 } }, { $limit: 2 }, { $project: { client: 1 } }],
    total: [{ $count: 'n' }]
  } }
])

Ieșire în mongosh

[
  {
    pagina: [ { _id: 1, client: 'Ana' }, { _id: 2, client: 'Dan' } ],
    total: [ { n: 3 } ]
  }
]

Greșeli frecvente

  • Tot rezultatul $facet este un singur document, deci trebuie să încapă în 16 MB; nu pune în facet tablouri foarte lungi.
  • Sub-pipeline-urile din $facet nu folosesc indexuri; pune $match-ul comun înainte de $facet.

Comenzi înrudite

  • $bucket / $bucketAuto: Etapa $bucket grupează documentele pe intervale de valori (de exemplu de preț); $bucketAuto alege singură intervalele.
  • $count / $sortByCount: Etapa $count numără documentele ajunse în acel punct al pipeline-ului; $sortByCount numără pe valori și ordonează descrescător.
  • $group: Etapa $group grupează documentele după o cheie și calculează totaluri: $sum, $avg, $min, $max, $push, $addToSet.