MongoDB, un sistem de gestiune a bazelor de date NoSQL foarte popular, organizează informațiile în colecții. Aceste colecții sunt alcătuite din documente individuale, fiecare conținând datele propriu-zise în format JSON. Documentele pot fi asemănate cu rândurile dintr-o bază de date relațională SQL, în timp ce colecțiile corespund tabelelor.
O funcționalitate cheie a oricărei baze de date este capacitatea de a interoga datele stocate. Interogarea datelor permite extragerea informațiilor specifice, analizarea datelor, generarea rapoartelor și integrarea datelor în diverse sisteme.
Pentru a interoga eficient o bază de date, este crucial să se poată combina date din mai multe tabele, în cazul bazelor de date SQL, sau din multiple colecții, în cazul bazelor de date NoSQL, într-un singur set de rezultate.
În MongoDB, operațiunea $lookup
este folosită pentru a combina informații din două colecții în timpul interogării. Aceasta realizează un efect similar cu o îmbinare externă stângă într-o bază de date SQL.
Utilizarea și scopul operațiunii $lookup
Un aspect fundamental al bazelor de date este abilitatea de a procesa datele pentru a extrage informații valoroase din datele brute.
De exemplu, într-un context de afaceri, cum ar fi un restaurant, ar putea fi necesar să analizați datele pentru a determina veniturile zilnice, cele mai căutate feluri de mâncare din weekend, sau chiar numărul de cești de cafea vândute la fiecare oră a zilei.
Pentru astfel de necesități, interogările simple ale bazei de date sunt insuficiente. Este necesară efectuarea unor interogări avansate asupra datelor stocate. Pentru a satisface aceste cerințe, MongoDB oferă o caracteristică numită conductă de agregare.
O conductă de agregare este un sistem compus din operațiuni modulare, denumite etape, care procesează datele în vederea producerii unui rezultat agregat final. Exemple de etape ale conductei de agregare includ $sort
, $match
, $group
, $merge
, $count
și $lookup
, printre altele.
Aceste etape pot fi aplicate în orice ordine într-o conductă de agregare. În fiecare etapă, se efectuează operații specifice asupra datelor care sunt transmise prin conductă.
$lookup
este, așadar, o etapă a conductei de agregare MongoDB, folosită pentru a efectua o îmbinare externă stângă între două colecții. O îmbinare externă stângă combină toate documentele sau intrările dintr-o colecție sursă cu documentele sau intrările corespunzătoare din colecția țintă.
De exemplu, să presupunem că avem următoarele două colecții, prezentate sub formă tabelară pentru o mai bună înțelegere:
colectare_comenzi
:
order_id | customer_id | order_date | total_amount |
1 | 100 | 2022-05-01 | 50.00 |
2 | 101 | 2022-05-02 | 75.00 |
3 | 102 | 2022-05-03 | 100.00 |
colecție_clienți
:
customer_num | customer_name | customer_email | customer_phone |
100 | John Doe | [email protected] | [email protected] |
Dacă efectuăm o îmbinare externă stângă pe cele două colecții folosind câmpul customer_id
din colectare_comenzi
și customer_num
din colecție_clienți
, rezultatul va conține toate documentele din colectare_comenzi
, împreună cu documentele din colecție_clienți
care au un customer_num
corespunzător cu customer_id
din colectare_comenzi
.
Rezultatul operațiunii de îmbinare externă stângă a colecțiilor de comenzi și clienți arată astfel, sub formă tabelară:
order_id | customer_id | order_date | total_amount | customer_num | customer_name | customer_email | customer_phone |
1 | 100 | 2022-05-01 | 50.00 | 100 | John Doe | [email protected] | [email protected] |
2 | 101 | 2022-05-02 | 75.00 | null | null | null | null |
3 | 102 | 2022-05-03 | 100.00 | null | null | null | null |
Observați că pentru clientul cu customer_id
101 în colecția de comenzi, care nu avea un customer_num
corespunzător în colecția de clienți, valorile corespunzătoare lipsă din tabelul clienți au fost completate cu null
.
Operațiunea $lookup
realizează o comparație strictă de egalitate între câmpuri și returnează întregul document care se potrivește, nu doar câmpurile potrivite.
Sintaxa $lookup
Sintaxa pentru $lookup
este următoarea:
{ $lookup: { from: <colecția de îmbinat>, localField: <câmpul din documentele de intrare>, foreignField: <câmpul din documentele colecției "from">, as: <câmpul matrice de ieșire> } }
$lookup
are patru parametri:
from
– reprezintă colecția din care se caută documente. În exemplul anterior, folosindcolectare_comenzi
șicolecție_clienți
, am specificacolecție_clienți
ca fiind colecțiafrom
.localField
– reprezintă câmpul din colecția de lucru sau principală folosit pentru a compara cu câmpurile din colecția secundară (colecție_clienți
în cazul nostru). În exemplul de mai sus,localField
ar ficustomer_id
dincolectare_comenzi
.foreignField
– este câmpul cu care se compară în colecția specificată prinfrom
. În exemplul nostru, acesta ar ficustomer_num
dincolecție_clienți
.as
– reprezintă un nou nume de câmp specificat pentru a include un câmp care conține documentele rezultate din potrivirile dintrelocalField
șiforeignField
. Toate aceste potriviri sunt plasate într-o matrice în acest câmp. Dacă nu există potriviri, acest câmp va conține o matrice goală.
Din cele două colecții anterioare, am utiliza următorul cod pentru a efectua o operațiune $lookup
, având colectare_comenzi
ca și colecție principală:
{ $lookup: { from: "colecție_clienți", localField: "customer_id", foreignField: "customer_num", as: "customer_info" } }
Câmpul as
poate avea orice valoare string. Totuși, dacă i se dă un nume care există deja în documentul de lucru, acel câmp va fi suprascris.
Combinarea datelor din mai multe colecții
$lookup
este o etapă utilă într-o conductă de agregare MongoDB. Deși prezența acestei etape nu este obligatorie, ea devine crucială atunci când se efectuează interogări complexe care necesită combinarea datelor din mai multe colecții.
Etapa $lookup
realizează o îmbinare externă stângă între două colecții, ceea ce duce la crearea unui câmp nou sau la suprascrierea unui câmp existent cu o matrice care conține documente dintr-o altă colecție.
Documentele respective sunt selectate în funcție de existența unor valori potrivite cu valorile câmpului cu care se compară. Rezultatul final este un câmp care conține o matrice de documente în cazul în care s-au găsit potriviri, sau o matrice goală în cazul în care nu au fost găsite potriviri.
Să luăm ca exemplu colecțiile de angajați și proiecte, prezentate mai jos:
Putem folosi următorul cod pentru a combina cele două colecții:
db.projects.aggregate([ { $lookup: { from: "employees", localField: "employees", foreignField: "_id", as: "assigned_employees" } } ])
Rezultatul acestei operațiuni este o combinație a celor două colecții. Rezultatul prezintă proiectele și toți angajații alocați fiecărui proiect, angajații fiind reprezentați într-o matrice.
Etape ale conductei care pot fi utilizate împreună cu $lookup
După cum am menționat, $lookup
este o etapă dintr-o conductă de agregare MongoDB și poate fi utilizată în conjuncție cu alte etape. Pentru a exemplifica modul în care aceste etape pot fi combinate cu $lookup
, vom folosi următoarele două colecții:
În MongoDB, acestea sunt stocate în format JSON. Iată cum arată colecțiile de mai sus în MongoDB:
Câteva exemple de etape ale conductei de agregare care pot fi utilizate împreună cu $lookup
includ:
$match
$match
este o etapă folosită pentru a filtra fluxul de documente, permițând doar acelor documente care îndeplinesc o condiție specificată să treacă la etapa următoare a conductei. Această etapă este cel mai bine utilizată la începutul procesului, pentru a elimina documentele care nu vor fi necesare, optimizând astfel conducta de agregare.
Folosind cele două colecții anterioare, putem combina $match
și $lookup
astfel:
db.users.aggregate([ { $match: { country: "USA" } }, { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } } ])
$match
este folosit pentru a filtra utilizatorii din SUA. Rezultatul de la $match
este apoi combinat cu $lookup
pentru a obține detaliile comenzilor utilizatorilor din SUA. Rezultatul operațiunii de mai sus este prezentat mai jos:
$project
$project
este o etapă folosită pentru a remodela documentele prin specificarea câmpurilor care trebuie incluse, excluse sau adăugate. De exemplu, dacă procesați documente cu zece câmpuri fiecare, dar doar patru câmpuri conțin datele necesare pentru prelucrare, puteți utiliza $project
pentru a filtra câmpurile inutile.
Acest lucru permite evitarea trimiterii datelor inutile la etapa următoare a conductei de agregare.
Putem combina $lookup
și $project
astfel:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $project: { name: 1, _id: 0, total_spent: { $sum: "$orders.price" } } } ])
Codul de mai sus combină colecțiile utilizatorilor și comenzilor folosind $lookup
, apoi $project
este folosit pentru a afișa doar numele fiecărui utilizator și suma cheltuită de fiecare. $project
este folosit și pentru a elimina câmpul _id
din rezultate. Rezultatul operațiunii de mai sus este prezentat mai jos:
$unwind
$unwind
este o etapă de agregare folosită pentru a deconstrui sau a derula un câmp de tip matrice, creând noi documente pentru fiecare element al matricei. Acest lucru este util în cazul în care se dorește rularea unei agregări pe valorile unui câmp matrice.
De exemplu, în exemplul de mai jos, în cazul în care doriți să rulați agregarea în câmpul hobbies
, nu se poate face direct deoarece este o matrice. Totuși, puteți utiliza $unwind
și apoi efectua agregări pe documentele rezultate.
Folosind colecțiile de utilizatori și comenzi, putem combina $lookup
și $unwind
astfel:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $unwind: "$orders" } ])
În codul de mai sus, $lookup
returnează un câmp matrice numit orders
. Apoi, $unwind
este folosit pentru a derula câmpul matricei. Rezultatul acestei operațiuni este prezentat mai jos: observăm că Alice apare de două ori, deoarece a avut două comenzi.
Exemple de cazuri de utilizare a $lookup
Operațiunea $lookup
este un instrument util pentru procesarea datelor. De exemplu, pot exista două colecții care trebuie combinate pe baza unor câmpuri care conțin date similare. O etapă simplă $lookup
poate fi folosită în acest scop și pentru a adăuga un nou câmp colecțiilor primare, care conține documente obținute dintr-o altă colecție.
Să luăm în considerare colecțiile de utilizatori și comenzi de mai jos:
Cele două colecții pot fi combinate folosind $lookup
pentru a da rezultatul prezentat mai jos:
$lookup
poate fi folosit și pentru a efectua îmbinări mai complexe. Operațiunea nu se limitează doar la combinarea a două colecții. Se pot implementa mai multe etape $lookup
pentru a efectua îmbinări pe mai mult de două colecții. Să luăm în considerare cele trei colecții prezentate mai jos:
Putem folosi următorul cod pentru a efectua o îmbinare complexă între cele trei colecții, pentru a obține toate comenzile efectuate și, de asemenea, detaliile despre produsele comandate.
Codul de mai jos ne permite să facem exact asta:
db.orders.aggregate([ { $lookup: { from: "order_items", localField: "_id", foreignField: "order_id", as: "order_items" } }, { $unwind: "$order_items" }, { $lookup: { from: "products", localField: "order_items.product_id", foreignField: "_id", as: "product_details" } }, { $group: { _id: "$_id", customer: { $first: "$customer" }, total: { $sum: "$order_items.price" }, products: { $push: "$product_details" } } } ])
Rezultatul operațiunii de mai sus este prezentat mai jos:
Concluzie
În procesarea datelor care implică mai multe colecții, $lookup
poate fi util, deoarece permite asocierea datelor și extragerea concluziilor pe baza datelor stocate în mai multe colecții. Prelucrarea datelor rareori se bazează pe o singură colecție.
Pentru a extrage concluzii relevante din date, combinarea datelor din mai multe colecții este un pas cheie. Prin urmare, se recomandă utilizarea etapei $lookup
în cadrul conductei de agregare MongoDB pentru a procesa mai eficient datele și a obține informații valoroase din datele brute stocate în colecții.
De asemenea, se recomandă să explorați și alte comenzi și interogări MongoDB.