Како користити $лоокуп у МонгоДБ-у

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 sa orders_collection i customer_collection, ovde bismo postavili customer_collection.
  • localField – ovo je polje u radnoj ili primarnoj kolekciji koje koristimo da uporedimo sa poljima u našoj from kolekciji (u ovom slučaju, customer_collection). U gornjem primeru, localField bi bio customer_id koji se nalazi u orders_collection.
  • foreignField – ovo je polje sa kojim želimo da vršimo poređenje u kolekciji specificiranoj u from parametru. U našem primeru, ovo bi bio customer_num iz customer_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.