Upit za Couchbase s prikazima MapReduce

1. Pregled

U ovom uputstvu predstavit ćemo nekoliko jednostavnih prikaza MapReduce i demonstrirati kako ih postavljati pomoću Couchbase Java SDK-a.

2. Ovisnost Mavena

Da biste surađivali s Couchbaseom u projektu Maven, uvezite Couchbase SDK u svoj pom.xml:

 com.couchbase.client java-klijent 2.4.0 

Najnoviju verziju možete pronaći na Maven Central.

3. MapReduce Views

U Couchbaseu, prikaz MapReduce vrsta je indeksa koji se može koristiti za upit segmenta podataka. Definira se pomoću JavaScript-a karta funkcija i neobavezna smanjiti funkcija.

3.1. The karta Funkcija

The karta funkcija se pokreće protiv svakog dokumenta jednom. Kada se stvori pogled, karta funkcija pokreće se jednom za svaki dokument u segmentu, a rezultati se pohranjuju u segment.

Jednom kada se stvori pogled, karta funkcija se izvodi samo protiv novo umetnutih ili ažuriranih dokumenata kako bi se postupno ažuriralo prikaz.

Jer karta Rezultati funkcije pohranjuju se u skup podataka, upiti prema pogledu pokazuju malu latenciju.

Pogledajmo primjer a karta funkcija koja stvara indeks na Ime polje svih dokumenata u segmentu čiji tip polje je jednako “StudentGrade”:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.name) {emit (doc.name, null); }}

The emitirati funkcija govori Couchbaseu koja podatkovna polja treba pohraniti u indeksni ključ (prvi parametar) i koju vrijednost (drugi parametar) pridružiti indeksiranom dokumentu.

U ovom slučaju pohranjujemo samo dokument Ime svojstvo u ključu indeksa. A budući da nas ne zanima pridruživanje neke određene vrijednosti svakom unosu, prolazimo null kao parametar vrijednosti.

Dok Couchbase obrađuje prikaz, on stvara indeks tipki koje emitira karta funkcija, pridružujući svaki ključ svim dokumentima za koje je taj ključ emitiran.

Na primjer, ako tri dokumenta imaju Ime svojstvo postavljeno na "John Doe", zatim indeksni ključ "John Doe" bi bila povezana s ta tri dokumenta.

3.2. The smanjiti Funkcija

The smanjiti funkcija se koristi za izvođenje zbirnih izračuna pomoću rezultata a karta funkcija. Couchbase Admin UI pruža jednostavan način primjene ugrađenog smanjiti funkcije "_Broj", "_sum", i "_Stats", do vašeg karta funkcija.

Možete i sami napisati svoj smanjiti funkcije za složenije agregacije. Vidjet ćemo primjere korištenja ugrađenog smanjiti funkcije kasnije u vodiču.

4. Rad s pogledima i upitima

4.1. Organiziranje pogleda

Prikazi su organizirani u jedan ili više projektnih dokumenata po segmentu. U teoriji ne postoji ograničenje broja pregleda po dizajnerskom dokumentu. Međutim, za optimalne performanse predloženo je da svaki projektni dokument ograničite na manje od deset prikaza.

Kada prvi put stvorite pogled u dizajnerskom dokumentu, Couchbase ga označava kao razvoj pogled. Možete pokretati upite protiv a razvoj pogled kako biste testirali njegovu funkcionalnost. Jednom kada ste zadovoljni pogledom, i vi biste objaviti projektni dokument, a pogled postaje a proizvodnja pogled.

4.2. Konstruiranje upita

Da biste konstruirali upit prema prikazu Couchbase, morate navesti naziv dokumenta dizajna i naziv pogleda da biste stvorili ViewQuery objekt:

ViewQuery query = ViewQuery.from ("design-document-name", "view-name");

Kada se izvrši, ovaj će upit vratiti sve retke prikaza. U narednim odjeljcima vidjet ćemo kako ograničiti skup rezultata na temelju ključnih vrijednosti.

Da biste konstruirali upit prema razvojnom prikazu, možete primijeniti razvoj() metoda prilikom kreiranja upita:

ViewQuery query = ViewQuery.from ("design-doc-name", "view-name"). Development ();

4.3. Izvršenje upita

Jednom kad imamo ViewQuery objekt, možemo izvršiti upit za dobivanje Pregled rezultata:

Rezultat ViewResult = bucket.query (upit);

4.4. Obrada rezultata upita

A sada kad imamo Pregled rezultata, možemo prelistavati retke kako bismo dobili ID-ove dokumenta i / ili sadržaj:

za (ViewRow redak: result.allRows ()) {JsonDocument doc = row.document (); Niz niza = doc.id (); Niz json = doc.content (). ToString (); }

5. Primjer uzorka

Za ostatak tutorijala napisat ćemo poglede MapReduce i upite za niz dokumenata studentskog razreda sljedećeg formata, s ocjenama ograničenim na raspon 0 do 100:

{"type": "StudentGrade", "name": "John Doe", "course": "History", "hours": 3, "grade": 95}

Te ćemo dokumente pohraniti u "baeldung-tutorial”Kanta i svi pogledi u projektnom dokumentu nazvanom“studentskeOcjene. " Pogledajmo kod potreban za otvaranje segmenta kako bismo ga mogli upitati:

Segment kante = CouchbaseCluster.create ("127.0.0.1") .openBucket ("baeldung-tutorial");

6. Upiti s točnim podudaranjem

Pretpostavimo da želite pronaći sve ocjene učenika za određeni predmet ili skup predmeta. Napišimo pogled pod nazivom „findByCourse”Koristeći sljedeće karta funkcija:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.course && doc.grade) {emit (doc.course, null); }}

Imajte na umu da u ovom jednostavnom prikazu trebamo emitirati samo tečaj polje.

6.1. Podudaranje na jednom ključu

Da bismo pronašli sve ocjene za tečaj povijesti, primjenjujemo ključ metoda za naš osnovni upit:

ViewQuery query = ViewQuery.from ("studentGrades", "findByCourse"). Key ("History");

6.2. Podudaranje na više tipki

Ako želite pronaći sve ocjene za tečajeve matematike i znanosti, možete prijaviti tipke metodu osnovnom upitu, prosljeđujući mu niz vrijednosti ključeva:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourse") .keys (JsonArray.from ("Math", "Science"));

7. Upit o rasponu

Da bismo tražili dokumente koji sadrže raspon vrijednosti za jedno ili više polja, potreban nam je prikaz koji emitira polja koja nas zanimaju, a za upit moramo navesti donju i / ili gornju granicu.

Pogledajmo kako izvoditi upite raspona koji uključuju jedno polje i više polja.

7.1. Upiti koji uključuju jedno polje

Da biste pronašli sve dokumente s nizom razred vrijednosti bez obzira na vrijednost tečaj polje, potreban nam je pogled koji emitira samo razred polje. Napišimo karta funkcija za „findByGrade”Pogled:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.grade) {emit (doc.grade, null); }}

Napišimo upit na Javi koristeći ovaj prikaz kako bismo pronašli sve ocjene ekvivalentne ocjeni slova „B“ (od 80 do 89):

ViewQuery query = ViewQuery.from ("studentGrades", "findByGrade") .startKey (80) .endKey (89) .inclusiveEnd (true);

Imajte na umu da se vrijednost početnog ključa u upitu raspona uvijek tretira kao uključiva.

A ako se zna da su sve ocjene cijeli brojevi, tada će sljedeći upit dati iste rezultate:

ViewQuery query = ViewQuery.from ("studentGrades", "findByGrade") .startKey (80) .endKey (90) .inclusiveEnd (false);

Da bismo pronašli sve ocjene "A" (90 i više), trebamo navesti samo donju granicu:

ViewQuery query = ViewQuery .from ("studentGrades", "findByGrade") .startKey (90);

Da bismo pronašli sve ocjene koje propadaju (ispod 60), trebamo navesti samo gornju granicu:

ViewQuery query = ViewQuery .from ("studentGrades", "findByGrade") .endKey (60) .inclusiveEnd (false);

7.2. Upiti koji uključuju više polja

Pretpostavimo sada da želimo pronaći sve studente na određenom tečaju čija ocjena spada u određeni raspon. Ovaj upit zahtijeva novi pogled koji emitira i tečaj i razred polja.

U prikazima s više polja svaki ključ indeksa emitira se kao niz vrijednosti. Budući da naš upit uključuje fiksnu vrijednost za tečaj i niz razred vrijednosti, napisat ćemo funkciju map koja emitira svaki ključ kao niz oblika [tečaj, razred].

Pogledajmo karta funkcija za prikaz “findByCourseAndGrade“:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.course && doc.grade) {emit ([doc.course, doc.grade], null); }}

Kada se ovaj prikaz napuni u Couchbaseu, unosi indeksa se sortiraju po tečaj i razred. Ovdje je podskup tipki u "findByCourseAndGrade”Prikaz prikazan njihovim prirodnim redoslijedom sortiranja:

["Povijest", 80] ["Povijest", 90] ["Povijest", 94] ["Matematika", 82] ["Matematika", 88] ["Matematika", 97] ["Znanost", 78] [ "Znanost", 86] ["Znanost", 92]

Budući da su ključevi u ovom prikazu nizovi, također biste koristili nizove ovog formata kada specificirate donju i gornju granicu upita raspona prema ovom prikazu.

To znači da biste, kako biste pronašli sve učenike koji su na tečaju matematike dobili ocjenu "B" (80 do 89), postavili donju granicu na:

["Matematika", 80]

a gornja granica na:

["Matematika", 89]

Napišimo upit za raspon na Javi:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade") .startKey (JsonArray.from ("Math", 80)) .endKey (JsonArray.from ("Math", 89)) .inclusiveEnd (true);

Ako želimo pronaći sve učenike koji su iz matematike dobili ocjenu "A" (90 i više), tada bismo napisali:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade") .startKey (JsonArray.from ("Math", 90)) .endKey (JsonArray.from ("Math", 100));

Imajte na umu da zato što popravljamo vrijednost tečaja na „Matematika“, Moramo uključiti gornju granicu s najvišim mogućim razred vrijednost. Inače, naš skup rezultata također bi obuhvaćao sve dokumente čiji tečaj vrijednost je leksikografski veća od „Matematika“.

Da biste pronašli sve neuspjele ocjene iz matematike (ispod 60):

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade") .startKey (JsonArray.from ("Math", 0)) .endKey (JsonArray.from ("Math", 60)) .inclusiveEnd (false);

Slično kao i u prethodnom primjeru, moramo odrediti donju granicu s najnižom mogućom ocjenom. Inače, naš bi skup rezultata također obuhvaćao sve ocjene u kojima je tečaj vrijednost je leksikografski manja od “Matematika“.

Konačno, da biste pronašli pet najviših ocjena iz matematike (zabranjujući bilo kakve veze), Couchbaseu možete reći da izvede silazno sortiranje i da ograniči veličinu skupa rezultata:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade") .descending () .startKey (JsonArray.from ("Math", 100)) .endKey (JsonArray.from ("Math", 0)) .inclusiveEnd ( true) .limit (5);

Imajte na umu da prilikom izvođenja silazne sorte startKljuč i krajKljuč vrijednosti su obrnute, jer Couchbase primjenjuje sortiranje prije nego što primijeni ograničiti.

8. Zbirni upiti

Glavna snaga prikaza MapReducea je u tome što su vrlo učinkoviti za pokretanje skupnih upita protiv velikih skupova podataka. Na primjer, u našem skupu podataka ocjena učenika možemo lako izračunati sljedeće agregate:

  • broj studenata u svakom kolegiju
  • zbroj kreditnih sati za svakog studenta
  • prosjek ocjena za svakog studenta u svim tečajevima

Izgradimo prikaz i upit za svaki od ovih izračuna pomoću ugrađenog smanjiti funkcije.

8.1. Koristiti računati() Funkcija

Prvo, napišimo karta funkcija za prikaz koji broji broj studenata na svakom tečaju:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.course && doc.name) {emit ([doc.course, doc.name], null); }}

Nazvat ćemo ovaj pogled “countStudentsByCourse"I odredite da se koristi ugrađenim "_računati" funkcija. A budući da vršimo samo jednostavno brojanje, još uvijek možemo emitirati null kao vrijednost za svaki unos.

Da biste prebrojali broj studenata na svakom tečaju:

ViewQuery query = ViewQuery .from ("studentGrades", "countStudentsByCourse") .reduce () .groupLevel (1);

Izdvajanje podataka iz skupnih upita razlikuje se od onoga što smo vidjeli do sada. Umjesto izdvajanja odgovarajućeg Couchbase dokumenta za svaki redak u rezultatu, izdvajamo skupne ključeve i rezultate.

Pokrenimo upit i izvucimo brojeve u java.util.Map:

Rezultat ViewResult = bucket.query (upit); Karta numStudentsByCourse = nova HashMap (); za (ViewRow redak: result.allRows ()) {JsonArray keyArray = (JsonArray) row.key (); Niz tečaja = keyArray.getString (0); dugo brojanje = Long.valueOf (row.value (). toString ()); numStudentsByCourse.put (tečaj, broj); }

8.2. Koristiti iznos() Funkcija

Dalje, napišimo pogled koji izračunava zbroj pokušanih bodova svakog studenta. Nazvat ćemo ovaj pogled “sumHoursByStudent”I odredite da se koristi ugrađenim "_iznos" funkcija:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.name && doc.course && doc.hours) {emit ([doc.name, doc.course], doc.hours); }}

Imajte na umu da prilikom primjene "_iznos" funkcija, moramo emitirati vrijednost koja se zbraja - u ovom slučaju, broj bodova - za svaki unos.

Napišimo upit kako bismo pronašli ukupan broj bodova za svakog učenika:

ViewQuery query = ViewQuery .from ("studentGrades", "sumCreditsByStudent") .reduce () .groupLevel (1);

A sada, pokrenimo upit i izdvojimo agregirane sume u java.util.Map:

Rezultat ViewResult = bucket.query (upit); Mapa satiByStudent = novi HashMap (); za (ViewRow redak: result.allRows ()) {Naziv niza = (Niz) row.key (); duga suma = Long.valueOf (row.value (). toString ()); hoursByStudent.put (ime, zbroj); }

8.3. Izračunavanje prosjeka bodova

Pretpostavimo da želimo izračunati prosjek ocjena svakog studenta (GPA) za sve tečajeve, koristeći konvencionalnu ljestvicu ocjena na temelju dobivenih ocjena i broja sati bodova koje predmet vrijedi (A = 4 boda po satu kredita, B = 3 boda po kreditnom satu, C = 2 boda po kreditnom satu i D = 1 bod po kreditnom satu).

Nema ugrađenog smanjiti funkcija za izračunavanje prosječnih vrijednosti, pa ćemo kombinirati izlaz iz dva prikaza kako bismo izračunali GPA.

Već imamo “SumHoursByStudent” pogled koji zbraja broj kreditnih sati koje je svaki student pokušao. Sada nam treba ukupan broj bodova koje je svaki student stekao.

Stvorimo prikaz tzv “SumGradePointsByStudent” koji izračunava broj osvojenih bodova za svaki položeni tečaj. Upotrijebit ćemo ugrađeni "_iznos" funkcija za smanjenje sljedećeg karta funkcija:

funkcija (doc, meta) {if (doc.type == "StudentGrade" && doc.name && doc.hours && doc.grade) {if (doc.grade> = 90) {emit (doc.name, 4 * doc .sati); } else if (doc.grade> = 80) {emit (ime doc.dr., 3 * doc.sata); } else if (doc.grade> = 70) {emit (ime doc.dva, 2 * doc.sata); } else if (doc.grade> = 60) {emit (ime doc.dok, sati); } else {emitirati (doc.name, 0); }}}

Sad ispitajmo ovaj prikaz i izdvojimo zbrojeve u java.util.Map:

ViewQuery query = ViewQuery.from ("studentGrades", "sumGradePointsByStudent") .reduce () .groupLevel (1); Rezultat ViewResult = bucket.query (upit); Karta gradePointsByStudent = novi HashMap (); za (ViewRow redak: result.allRows ()) {Tečaj niza = (Niz) row.key (); duga suma = Long.valueOf (row.value (). toString ()); gradePointsByStudent.put (tečaj, zbroj); }

Napokon, kombinirajmo to dvoje Kartas da bi se izračunao prosjek uspjeha za svakog učenika:

Rezultat karte = novi HashMap (); for (Entry creditHoursEntry: hoursByStudent.entrySet ()) {Naziv niza = creditHoursEntry.getKey (); long totalHours = creditHoursEntry.getValue (); long totalGradePoints = gradePointsByStudent.get (ime); result.put (ime, ((plutajući) totalGradePoints / totalHours)); }

9. Zaključak

Demonstrirali smo kako napisati neke osnovne poglede MapReduce u Couchbaseu, te kako konstruirati i izvršiti upite prema pogledima i izvući rezultate.

Kôd predstavljen u ovom vodiču može se naći u projektu GitHub.

Možete saznati više o prikazima MapReduce i kako ih pitati na Javi na službenom mjestu dokumentacije za programere Couchbase.


$config[zx-auto] not found$config[zx-overlay] not found