Ce face $lookup
$lookup caută în colecția from documentele la care foreignField este egal cu localField din documentul curent și le adaugă în tabloul as. Dacă nu găsește nimic, tabloul este gol, dar documentul rămâne (ca la LEFT JOIN în SQL).
Din MongoDB 3.6 există și forma cu pipeline (și let pentru variabile), care permite condiții mai complexe și filtrarea documentelor aduse; din 5.0 poți combina localField/foreignField cu un pipeline. Pune un index pe foreignField, altfel fiecare căutare scanează toată colecția străină.
Parametri
| Parametru | Descriere |
|---|---|
from |
Colecția din care se aduc documente (aceeași bază de date). |
localField / foreignField |
Câmpurile comparate pentru egalitate. |
as |
Numele câmpului tablou cu rezultatele. |
let / pipeline |
Variabile și pipeline pentru condiții complexe (MongoDB 3.6+). |
Ce returnează
Documentele de intrare, fiecare cu un tablou nou (eventual gol).
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: [] }
])
db.clienti.insertMany([
{ _id: 'Ana', email: 'ana@exemplu.ro', nivel: 'gold' },
{ _id: 'Dan', email: 'dan@exemplu.ro', nivel: 'standard' }
])
Exemplul 1: Datele clientului lângă comandă
Pentru fiecare comandă se caută clientul cu _id egal cu numele din comandă.
db.comenzi.aggregate([
{ $match: { _id: { $in: [1, 2] } } },
{ $lookup: { from: 'clienti', localField: 'client', foreignField: '_id', as: 'detaliiClient' } },
{ $project: { client: 1, total: 1, detaliiClient: 1 } }
])
Ieșire în mongosh
[
{
_id: 1,
client: 'Ana',
total: 120,
detaliiClient: [ { _id: 'Ana', email: 'ana@exemplu.ro', nivel: 'gold' } ]
},
{
_id: 2,
client: 'Dan',
total: 85,
detaliiClient: [ { _id: 'Dan', email: 'dan@exemplu.ro', nivel: 'standard' } ]
}
]
Exemplul 2: Fără potrivire: tablou gol
Ioana nu există în clienti, dar comanda ei rămâne în rezultat.
db.comenzi.aggregate([{ $match: { _id: 3 } }, { $lookup: { from: 'clienti', localField: 'client', foreignField: '_id', as: 'detaliiClient' } }, { $project: { client: 1, detaliiClient: 1 } }])
Ieșire în mongosh
[ { _id: 3, client: 'Ioana', detaliiClient: [] } ]
Exemplul 3: Un singur câmp din documentul adus
Cu $unwind transformăm tabloul într-un obiect, apoi luăm doar emailul.
db.comenzi.aggregate([
{ $match: { _id: 1 } },
{ $lookup: { from: 'clienti', localField: 'client', foreignField: '_id', as: 'c' } },
{ $unwind: '$c' },
{ $project: { client: 1, email: '$c.email' } }
])
Ieșire în mongosh
[ { _id: 1, client: 'Ana', email: 'ana@exemplu.ro' } ]
Greșeli frecvente
- Fără index pe
foreignField,$lookupdevine foarte lent pe colecții mari. - Rezultatul este mereu un tablou, chiar și când există un singur document potrivit; folosește
$unwindsau$firstca să-l scoți din tablou. - Dacă ai nevoie mereu de datele împreună, ia în calcul să le ții în același document (modelare încorporată) în loc de
$lookupla fiecare citire.
Comenzi înrudite
$unwind: Etapa $unwind desface un tablou: produce câte un document pentru fiecare element, ca să-l poți grupa sau filtra.$match: Etapa $match păstrează în pipeline doar documentele care se potrivesc filtrului, cu aceeași sintaxă ca find().$expr: Operatorul $expr permite expresii de agregare în find(), de exemplu compararea a două câmpuri din același document.