Spørring om Couchbase med MapReduce Views

1. Oversikt

I denne opplæringen vil vi introdusere noen enkle MapReduce-visninger og demonstrere hvordan du kan spørre dem ved hjelp av Couchbase Java SDK.

2. Maven avhengighet

Hvis du vil jobbe med Couchbase i et Maven-prosjekt, importerer du Couchbase SDK til din pom.xml:

 com.couchbase.client java-client 2.4.0 

Du finner den nyeste versjonen på Maven Central.

3. MapReduce Views

I Couchbase er en MapReduce-visning en type indeks som kan brukes til å spørre om en data-bøtte. Det er definert ved hjelp av JavaScript kart funksjon og en valgfri redusere funksjon.

3.1. De kart Funksjon

De kart funksjonen kjøres mot hvert dokument en gang. Når visningen er opprettet, blir kart funksjonen kjøres en gang mot hvert dokument i bøtta, og resultatene lagres i bøtta.

Når en visning er opprettet, vil kart funksjonen kjøres bare mot nylig innsatte eller oppdaterte dokumenter for å oppdatere visningen trinnvis.

Fordi det kart funksjonens resultater er lagret i dataen, spørringer mot en visning har lave ventetider.

La oss se på et eksempel på en kart funksjon som lager en indeks på Navn felt av alle dokumenter i bøtten hvis type feltet er lik “StudentGrade”:

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

De avgir funksjon forteller Couchbase hvilke datafelt (er) som skal lagres i indeksnøkkelen (første parameter) og hvilken verdi (andre parameter) som skal knyttes til det indekserte dokumentet.

I dette tilfellet lagrer vi bare dokumentet Navn eiendom i indeksnøkkelen. Og siden vi ikke er interessert i å knytte noen spesiell verdi til hver oppføring, passerer vi null som verdiparameter.

Når Couchbase behandler visningen, oppretter den en indeks over nøklene som sendes ut av kart funksjon, knytter hver nøkkel til alle dokumentene som nøkkelen ble sluppet for.

For eksempel hvis tre dokumenter har Navn eiendom satt til "John Doe", deretter indeksnøkkelen "John Doe" ville være assosiert med de tre dokumentene.

3.2. De redusere Funksjon

De redusere funksjonen brukes til å utføre samlede beregninger ved å bruke resultatene av a kart funksjon. Couchbase Admin UI gir en enkel måte å bruke den innebygde på redusere funksjoner “_Count”, “_sum”, og “_Statistikk”, til din kart funksjon.

Du kan også skrive dine egne redusere funksjoner for mer komplekse aggregeringer. Vi vil se eksempler på bruk av den innebygde redusere fungerer senere i opplæringen.

4. Arbeide med synspunkter og spørsmål

4.1. Organisere utsikten

Visninger er organisert i ett eller flere designdokumenter per bøtte. I teorien er det ingen grense for antall visninger per designdokument. For optimal ytelse er det imidlertid blitt foreslått at du bør begrense hvert designdokument til færre enn ti visninger.

Når du først oppretter en visning i et designdokument, betegner Couchbase den som en utvikling utsikt. Du kan kjøre spørsmål mot a utvikling for å teste funksjonaliteten. Når du er fornøyd med utsikten, ville du gjort det publisere designdokumentet, og utsikten blir en produksjon utsikt.

4.2. Konstruere spørringer

For å lage et spørsmål mot en Couchbase-visning, må du oppgi navnet på designdokumentet og visningsnavnet for å opprette en ViewQuery gjenstand:

ViewQuery spørring = ViewQuery.from ("design-document-name", "view-name");

Når dette spørsmålet kjøres, returneres alle rader i visningen. Vi vil se i senere seksjoner hvordan du kan begrense resultatsettet basert på nøkkelverdiene.

For å konstruere et spørsmål mot en utviklingsvisning, kan du bruke utvikling() metode når du oppretter spørringen:

ViewQuery spørring = ViewQuery.from ("design-doc-name", "view-name"). Utvikling ();

4.3. Gjennomføring av spørringen

Når vi har en ViewQuery objekt, kan vi utføre spørringen for å få et Vis resultat:

ViewResult resultat = bucket.query (spørring);

4.4. Behandler spørringsresultater

Og nå som vi har en Vis resultat, kan vi gjenta over radene for å få dokument-ID og / eller innhold:

for (ViewRow rad: result.allRows ()) {JsonDocument doc = row.document (); Streng-id = doc.id (); String json = doc.content (). ToString (); }

5. Eksempel på applikasjon

For resten av opplæringen vil vi skrive MapReduce-visninger og spørsmål for et sett med studentkarakterer med følgende format, med karakterer begrenset til området 0 til 100:

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

Vi lagrer disse dokumentene i “baeldung-tutorial”Bøtte og alle visninger i et designdokument kalt“studentkarakterer. ” La oss se på koden som trengs for å åpne bøtta, slik at vi kan spørre om den:

Bucket bucket = CouchbaseCluster.create ("127.0.0.1") .openBucket ("baeldung-tutorial");

6. Eksakt samsvarsspørsmål

Anta at du vil finne alle studentkarakterer for et bestemt kurs eller sett med kurs. La oss skrive et syn som heter “findByCourse”Ved å bruke følgende kart funksjon:

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

Merk at i denne enkle visningen trenger vi bare å sende ut kurs felt.

6.1. Matching på en enkelt nøkkel

For å finne alle karakterene for Historie-kurset, bruker vi nøkkel metode til vårt grunnleggende spørsmål:

ViewQuery spørring = ViewQuery.from ("studentGrades", "findByCourse"). Nøkkel ("Historie");

6.2. Matching på flere taster

Hvis du vil finne alle karakterene for matematikk og naturfag, kan du bruke nøklene metode til basisspørringen, og sender den en rekke nøkkelverdier:

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

7. Range Queries

For å spørre etter dokumenter som inneholder et verdiområde for ett eller flere felt, trenger vi en visning som sender ut feltet (e) vi er interessert i, og vi må spesifisere en nedre og / eller øvre grense for spørringen.

La oss ta en titt på hvordan du kan utføre områdespørsmål som involverer et enkelt felt og flere felt.

7.1. Spørsmål som involverer et enkelt felt

For å finne alle dokumenter med en rekke karakter verdier uavhengig av verdien av kurs felt, trenger vi en visning som bare sender ut karakter felt. La oss skrive kart funksjon for “findByGrade”Visning:

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

La oss skrive et spørsmål i Java ved hjelp av denne visningen for å finne alle karakterer som tilsvarer en "B" bokstavkarakter (inkludert 80 til 89):

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

Merk at startnøkkelverdien i en rekkeforespørsel alltid blir behandlet som inkluderende.

Og hvis alle karakterene er kjent for å være heltall, vil følgende spørsmål gi de samme resultatene:

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

For å finne alle A-karakterene (90 og over) trenger vi bare å spesifisere nedre grense:

ViewQuery spørring = ViewQuery. Fra ("studentGrades", "findByGrade") .startKey (90);

Og for å finne alle sviktende karakterer (under 60), trenger vi bare å spesifisere øvre grense:

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

7.2. Spørsmål som involverer flere felt

Anta at vi ønsker å finne alle studenter i et bestemt emne hvis karakter faller innenfor et bestemt område. Dette spørsmålet krever en ny visning som sender ut både kurs og karakter Enger.

Med flerfeltvisninger sendes hver indeksnøkkel ut som en rekke verdier. Siden spørringen vår innebærer en fast verdi for kurs og en rekke karakter verdier, vil vi skrive kartfunksjonen for å sende ut hver nøkkel som en matrise av skjemaet [kurs, karakter].

La oss se på kart funksjon for visningen “findByCourseAndGrade“:

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

Når denne visningen er fylt ut i Couchbase, sorteres indeksoppføringene etter kurs og karakter. Her er et delsett av nøkler i “findByCourseAndGrade”Visning vist i deres naturlige sorteringsrekkefølge:

["History", 80] ["History", 90] ["History", 94] ["Math", 82] ["Math", 88] ["Math", 97] ["Science", 78] [ "Science", 86] ["Science", 92]

Siden tastene i denne visningen er matriser, vil du også bruke matriser i dette formatet når du spesifiserer nedre og øvre grenser for et områdespørsmål mot denne visningen.

Dette betyr at for å finne alle studenter som fikk karakteren "B" (80 til 89) i matematikk-kurset, ville du sette nedre grense til:

["Matematikk", 80]

og øvre grense til:

["Matematikk", 89]

La oss skrive områdespørringen i Java:

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

Hvis vi vil finne for alle studenter som fikk karakteren “A” (90 og høyere) i matematikk, ville vi skrive:

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

Merk at fordi vi setter kursverdien til “Matte“, Vi må inkludere en øvre grense med høyest mulig karakter verdi. Ellers vil resultatsettet også omfatte alle dokumenter hvis kurs verdien er leksikografisk større enn “Matte“.

Og for å finne alle matematikkkarakterer som ikke består (under 60):

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

I likhet med forrige eksempel, må vi spesifisere en nedre grense med lavest mulig karakter. Ellers vil resultatsettet vårt også omfatte alle karakterer der kurs verdien er leksikografisk mindre enn “Matte“.

Til slutt, for å finne de fem høyeste matematiske karakterene (uten noen tilknytning), kan du be Couchbase om å utføre en synkende sortering og begrense størrelsen på resultatsettet:

ViewQuery query = ViewQuery .from ("studentGrades", "findByCourseAndGrade"). Descending () .startKey (JsonArray.from ("Math", 100)) .endKey (JsonArray.from ("Math", 0)) .inclusiveEnd ( sant) .begrens (5);

Merk at når du utfører en synkende sortering, startKey og endKey verdiene blir reversert, fordi Couchbase bruker sorteringen før den bruker grense.

8. Samlede spørsmål

En viktig styrke ved MapReduce-visninger er at de er svært effektive for å kjøre samlede spørsmål mot store datasett. I vårt datasett for studentkarakterer kan vi for eksempel enkelt beregne følgende aggregater:

  • antall studenter på hvert kurs
  • sum av studiepoeng for hver student
  • karakter gjennomsnitt for hver student på tvers av alle kurs

La oss bygge en visning og spørring for hver av disse beregningene ved hjelp av innebygd redusere funksjoner.

8.1. Bruker telle() Funksjon

La oss først skrive kart funksjon for å vise antall studenter i hvert kurs:

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

Vi kaller denne visningen “countStudentsByCourse”Og angi at det er å bruke den innebygde "_telle" funksjon. Og siden vi bare utfører en enkel telling, kan vi fortsatt sende ut null som verdien for hver oppføring.

Å telle antall studenter på hvert kurs:

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

Å hente ut data fra samlede spørsmål er forskjellig fra det vi har sett til dette punktet. I stedet for å trekke ut et matchende Couchbase-dokument for hver rad i resultatet, trekker vi ut de samlede nøklene og resultatene.

La oss kjøre spørringen og trekke ut tellingene til en java.util.Kart:

ViewResult resultat = bucket.query (spørring); Kart numStudentsByCourse = ny HashMap (); for (ViewRow rad: result.allRows ()) {JsonArray keyArray = (JsonArray) rad.key (); Strengkurs = keyArray.getString (0); lang telling = Long.valueOf (row.value (). toString ()); numStudentsByCourse.put (kurs, antall); }

8.2. Bruker sum() Funksjon

Deretter la oss skrive en visning som beregner summen av hver students studiepoeng. Vi vil kalle denne visningen “sumHoursByStudent”Og angi at det er å bruke den innebygde "_sum" funksjon:

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

Merk at når du bruker "_sum" funksjon, må vi avgir verdien som skal summeres - i dette tilfellet antall studiepoeng - for hver oppføring.

La oss skrive et spørsmål for å finne det totale antallet studiepoeng for hver student:

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

Og nå, la oss kjøre spørringen og trekke ut de samlede summene til a java.util.Kart:

ViewResult resultat = bucket.query (spørring); Kart timerByStudent = nytt HashMap (); for (ViewRow rad: result.allRows ()) {String navn = (String) rad.key (); lang sum = Long.valueOf (row.value (). toString ()); hoursByStudent.put (navn, sum); }

8.3. Beregning av gjennomsnittsverdier

Anta at vi vil beregne hver elevs karakterpoengsnitt (GPA) på tvers av alle emner, ved å bruke den konvensjonelle karakterskalaen basert på karakterene som er oppnådd og antall studiepoeng som kurset er verdt (A = 4 poeng per studiepoeng, B = 3 poeng per kredittime, C = 2 poeng per kredittime, og D = 1 poeng per kredittime).

Det er ingen innebygd redusere funksjon for å beregne gjennomsnittsverdier, så vi kombinerer utdata fra to visninger for å beregne GPA.

Vi har allerede “SumHoursByStudent” syn som summerer antall studiepoeng hver student forsøkte. Nå trenger vi det totale antall karakterpoeng hver student oppnådde.

La oss lage en visning som heter “SumGradePointsByStudent” som beregner antall opptjente karakterpoeng for hvert emne som tas. Vi bruker den innebygde "_sum" funksjon for å redusere følgende kart funksjon:

funksjon (doc, meta) {if (doc.type == "StudentGrade" && doc.name && doc.hours && doc.grade) {if (doc.grade> = 90) {emit (doc.name, 4 * doc .timer); } annet hvis (doc.grad> = 80) {emit (doc.name, 3 * doc.hours); } annet hvis (doc.grad> = 70) {emit (doc.name, 2 * doc.hours); } annet hvis (doc.grad> = 60) {emit (doc.name, doc.hours); } annet {emit (doc.name, 0); }}}

La oss spørre denne visningen og trekke ut summene til a java.util.Kart:

ViewQuery spørring = ViewQuery.from ("studentGrades", "sumGradePointsByStudent") .reduce () .groupLevel (1); ViewResult resultat = bucket.query (spørring); Map gradePointsByStudent = ny HashMap (); for (ViewRow rad: result.allRows ()) {String kurs = (String) rad.key (); lang sum = Long.valueOf (row.value (). toString ()); gradePointsByStudent.put (kurs, sum); }

Til slutt, la oss kombinere de to Karts for å beregne GPA for hver student:

Kartresultat = nytt HashMap (); for (Entry creditHoursEntry: hoursByStudent.entrySet ()) {String name = creditHoursEntry.getKey (); lang totalHours = creditHoursEntry.getValue (); lang totalGradePoints = gradePointsByStudent.get (navn); result.put (navn, ((float) totalGradePoints / totalHours)); }

9. Konklusjon

Vi har demonstrert hvordan du skriver noen grunnleggende MapReduce-visninger i Couchbase, og hvordan du konstruerer og utfører spørsmål mot visningene, og trekker ut resultatene.

Koden som presenteres i denne veiledningen, finner du i GitHub-prosjektet.

Du kan lære mer om MapReduce-visninger og hvordan du spørre dem i Java på det offisielle Couchbase-utviklerens dokumentasjonsside.