MongoDB je popularna NoSQL baza podataka koja podatke organizuje u kolekcijama. Ove kolekcije sadrže jedan ili više dokumenata, a svaki dokument predstavlja zapravo podatke u JSON formatu. Možemo reći da su dokumenti slični redovima u tradicionalnim, relacijskim SQL bazama, dok kolekcije odgovaraju tabelama.
Ključna funkcionalnost svake baze podataka je mogućnost postavljanja upita nad sačuvanim podacima. Upiti nam omogućavaju da efikasno pronađemo specifične informacije, analiziramo podatke, kreiramo izveštaje, i omogućimo integraciju podataka.
Da bismo mogli efikasno postavljati upite, veoma je važno da možemo kombinovati podatke iz različitih tabela (u slučaju SQL baza) ili iz različitih kolekcija (u NoSQL bazama) u jedinstven rezultat.
U MongoDB, za ovu svrhu se koristi operator $lookup
, koji omogućava kombinovanje informacija iz dve kolekcije tokom upita. On funkcioniše slično kao *left outer join* u SQL bazama podataka.
Primena i svrha $lookup
operatora
Jedna od bitnih funkcija baza podataka je obrada podataka, kako bi se od sirovih podataka dobile relevantne i korisne informacije.
Na primer, ako ste vlasnik restorana, verovatno biste želeli da analizirate svoje podatke kako biste saznali koliko zarađujete svakog dana, koja jela su najtraženija vikendom, ili koliko šoljica kafe prodate u određenom satu tokom dana.
Za ovakve zahteve, osnovni upiti baze podataka nisu dovoljni. Potrebno je izvršavati napredne upite nad sačuvanim podacima. Za rešavanje tih potreba, MongoDB nudi mehanizam koji se zove *agregacioni cevovod*.
Agregacioni cevovod je sistem koji se sastoji od različitih operacija, nazvanih faze, koje se koriste za obradu podataka i dobijanje konačnog rezultata. Primeri faza u agregacionom cevovodu su $sort
, $match
, $group
, $merge
, $count
i $lookup
, između ostalih.
Ove faze se mogu primenjivati u bilo kom redosledu unutar agregacionog cevovoda. U svakoj fazi, različite operacije se izvršavaju nad podacima koji prolaze kroz cevovod.
$lookup
je faza unutar agregacionog cevovoda u MongoDB. Koristi se za izvršavanje left outer join operacije između dve kolekcije. Left outer join kombinuje sve dokumente (ili zapise) iz leve kolekcije sa odgovarajućim dokumentima iz desne kolekcije.
Na primer, uzmimo dve kolekcije prikazane ispod, koje su radi lakšeg razumevanja predstavljene u tabelarnom formatu:
orders_collection:
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 |
customer_collection:
customer_num |
customer_name |
customer_email |
customer_phone |
100 | John | [email protected] | [email protected] |
Ako izvršimo left outer join na ove dve kolekcije koristeći polje customer_id
iz orders_collection
, gde je orders_collection
leva kolekcija, a customer_collection
desna, rezultat će sadržati sve dokumente iz kolekcije narudžbina i one dokumente iz kolekcije kupaca koji imaju customer_num
koji se podudara sa customer_id
iz bilo kog zapisa u kolekciji narudžbina.
Konačan rezultat left outer join operacije nad kolekcijama narudžbina i kupaca izgleda ovako, prikazan u tabelarnom formatu:
Važno je primetiti da za kupca sa customer_id
101 u kolekciji narudžbina, koji nije imao odgovarajuću vrednost customer_num
u kolekciji kupaca, nedostajuće vrednosti iz tabele kupaca su popunjene sa `null`.
$lookup
vrši strogo poređenje jednakosti između polja i preuzima ceo dokument koji se podudara, a ne samo polja koja se podudaraju.
$lookup
sintaksa
Sintaksa za $lookup
je sledeća:
{ $lookup: { from: <kolekcija za spajanje>, localField: <polje iz ulaznih dokumenata>, foreignField: <polje iz dokumenata "from" kolekcije>, as: <polje izlaznog niza> } }
$lookup
ima četiri parametra:
from
– označava kolekciju iz koje želimo da preuzmemo dokumente. U našem prethodnom primeru saorders_collection
icustomer_collection
, ovde bismo postavilicustomer_collection
.localField
– ovo je polje u radnoj ili primarnoj kolekciji koje koristimo da uporedimo sa poljima u našojfrom
kolekciji (u ovom slučaju,customer_collection
). U gornjem primeru,localField
bi biocustomer_id
koji se nalazi uorders_collection
.foreignField
– ovo je polje sa kojim želimo da vršimo poređenje u kolekciji specificiranoj ufrom
parametru. U našem primeru, ovo bi biocustomer_num
izcustomer_collection
.as
– ovo je naziv novog polja koje navodimo kako bi predstavljalo polje koje će se pojaviti u našem dokumentu, a koje sadrži dokumente koji su rezultat podudaranja između lokalnog i stranog polja. Sva ova podudaranja se smeštaju u niz unutar ovog polja. Ako nema podudaranja, ovo polje će sadržati prazan niz.
Za prethodne dve kolekcije, koristili bismo sledeći kod da izvršimo operaciju $lookup
, sa orders_collection
kao našom radnom ili primarnom kolekcijom:
{ $lookup: { from: "customers_collection", localField: "customer_id", foreignField: "customer_num", as: "customer_info" } }
Imajte na umu da polje as
može imati bilo koju vrednost niza. Međutim, ako mu date ime koje već postoji u radnom dokumentu, to polje će biti prebrisano.
Spajanje podataka iz više kolekcija
MongoDB $lookup
je korisna faza u agregacionom cevovodu. Iako nije neophodno da cevovod mora imati fazu $lookup
, ona je ključna kada je potrebno izvršavati složene upite koji zahtevaju spajanje podataka iz više kolekcija.
Faza $lookup
izvršava left outer join nad dve kolekcije, što rezultira kreiranjem novog polja ili prebrisavanjem vrednosti postojećeg polja nizom koji sadrži dokumente iz druge kolekcije.
Ovi dokumenti se biraju na osnovu toga da li imaju vrednosti koje odgovaraju vrednostima polja sa kojima se porede. Konačni rezultat je polje koje sadrži niz dokumenata u slučaju da su pronađena podudaranja, ili prazan niz ako nije pronađeno podudaranje.
Razmotrite kolekcije zaposlenih i projekata prikazane ispod:
Možemo koristiti sledeći kod da spojimo ove dve kolekcije:
db.projects.aggregate([ { $lookup: { from: "employees", localField: "employees", foreignField: "_id", as: "assigned_employees" } } ])
Rezultat ove operacije je kombinacija dve kolekcije. Rezultat su projekti i svi zaposleni dodeljeni svakom projektu. Zaposleni su predstavljeni u nizu.
Faze cevovoda koje se mogu koristiti sa $lookup
Kao što je ranije pomenuto, $lookup
je faza u agregacionom cevovodu MongoDB, i može se koristiti zajedno sa drugim fazama. Da bismo demonstrirali kako se ove faze mogu koristiti zajedno sa $lookup
, koristimo sledeće dve kolekcije za ilustraciju.
U MongoDB, ovi podaci se čuvaju u JSON formatu. Ovako izgledaju gore navedene kolekcije u MongoDB:
Neki primeri faza agregacionog cevovoda koji se mogu koristiti zajedno sa $lookup
uključuju:
$match
$match
je faza agregacionog cevovoda koja se koristi za filtriranje toka dokumenata, propuštajući samo one dokumente koji ispunjavaju dati uslov u narednu fazu. Preporučuje se da se ova faza koristi na početku procesa za uklanjanje dokumenata koji neće biti potrebni, i na taj način optimizovati agregacioni cevovod.
Koristeći dve prethodne kolekcije, možete kombinovati $match
i $lookup
na sledeći način:
db.users.aggregate([ { $match: { country: "USA" } }, { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } } ])
$match
se koristi za filtriranje korisnika iz SAD. Rezultat $match
se zatim kombinuje sa $lookup
da bi se dobili detalji narudžbina korisnika iz SAD. Rezultat gore navedene operacije je prikazan ispod:
$project
$project
je faza koja se koristi za preoblikovanje dokumenata, određujući koja polja treba uključiti, isključiti ili dodati dokumentima. Na primer, u slučaju da obrađujete dokumente sa deset polja, ali samo četiri polja sadrže podatke koji su vam potrebni, možete koristiti $project
da filtrirate polja koja nisu potrebna.
Ovo vam omogućava da izbegnete slanje nepotrebnih podataka u narednu fazu vašeg agregacionog cevovoda.
Možemo kombinovati $lookup
i $project
na sledeći način:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $project: { name: 1, _id: 0, total_spent: { $sum: "$orders.price" } } } ])
Gore navedeno kombinuje kolekcije korisnika i narudžbina koristeći $lookup
, a zatim se $project
koristi samo za prikaz imena svakog korisnika i iznosa koji je svaki korisnik potrošio. $project
se takođe koristi za uklanjanje polja _id
iz rezultata. Rezultat gore navedene operacije je prikazan ispod:
$unwind
$unwind
je faza agregacije koja se koristi za dekonstruisanje ili razmotavanje polja niza, kreirajući nove dokumente za svaki element u nizu. Ovo je korisno u slučajevima kada želite da pokrenete neku agregaciju nad vrednostima polja niza.
Na primer, u donjem primeru, ako želite da pokrenete agregaciju nad poljem hobija, to nije moguće jer je to niz. Međutim, možete ga koristiti za razmotavanje pomoću $unwind
, a zatim izvršiti agregacije nad rezultirajućim dokumentima.
Koristeći kolekcije korisnika i narudžbina, možemo koristiti $lookup
i $unwind
zajedno na sledeći način:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $unwind: "$orders" } ])
U gornjem kodu, $lookup
vraća polje niza nazvano „narudžbine“. $unwind
se zatim koristi za razmotavanje ovog polja niza. Rezultat ove operacije je prikazan ispod: Primetićete da se Alice pojavljuje dva puta jer je imala dve narudžbine.
Primeri slučajeva upotrebe $lookup
Prilikom obrade podataka, $lookup
je koristan alat. Na primer, možda imate dve kolekcije koje želite da spojite na osnovu polja u kolekcijama koja sadrže slične podatke. Jednostavna faza $lookup
se može koristiti za ovo, dodajući novo polje u primarne kolekcije, koje sadrže dokumente dobijene iz druge kolekcije.
Uzmite u obzir kolekcije korisnika i narudžbina prikazane ispod:
Ove dve kolekcije se mogu kombinovati koristeći $lookup
da bi se dobio rezultat prikazan ispod:
$lookup
se takođe može koristiti za složenija spajanja. Nije ograničen samo na spajanje dve kolekcije. Možete da primenite više faza $lookup
da biste izvršili spajanja na više od dve kolekcije. Razmotrite tri kolekcije prikazane ispod:
Možemo koristiti sledeći kod da izvršimo složenije spajanje nad ove tri kolekcije, i dobijemo sve narudžbine koje su kreirane, kao i detalje o proizvodima koji su naručeni.
Sledeći kod nam omogućava da uradimo upravo to:
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" } } } ])
Rezultat gore navedene operacije je prikazan ispod:
Zaključak
Kada obrađujete podatke koji uključuju više kolekcija, $lookup
može biti veoma koristan, jer vam omogućava da spojite podatke i izvučete zaključke na osnovu podataka smeštenih u više kolekcija. Obrada podataka se retko oslanja na samo jednu kolekciju.
Za izvođenje smislenih zaključaka iz podataka, spajanje podataka iz više kolekcija je ključan korak. Stoga, razmotrite korišćenje $lookup
faze u vašem MongoDB agregacionom cevovodu, kako biste mogli bolje obrađivati svoje podatke i izvući smislene uvide iz sirovih podataka smeštenih u različitim kolekcijama.
Takođe možete istražiti i neke druge MongoDB komande i upite.