Couchbase opvragen met MapReduce-weergaven

1. Overzicht

In deze tutorial introduceren we enkele eenvoudige MapReduce-weergaven en demonstreren we hoe u deze kunt opvragen met de Couchbase Java SDK.

2. Maven Afhankelijkheid

Om met Couchbase in een Maven-project te werken, importeert u de Couchbase SDK in uw pom.xml:

 com.couchbase.client java-client 2.4.0 

U kunt de nieuwste versie vinden op Maven Central.

3. MapReduce-weergaven

In Couchbase is een MapReduce-weergave een type index dat kan worden gebruikt om een ​​gegevensbucket te doorzoeken. Het wordt gedefinieerd met behulp van JavaScript kaart functie en een optioneel verminderen functie.

3.1. De kaart Functie

De kaart functie wordt één keer tegen elk document uitgevoerd. Wanneer de weergave is gemaakt, wordt het kaart functie wordt eenmaal uitgevoerd voor elk document in de bucket, en de resultaten worden in de bucket opgeslagen.

Zodra een weergave is gemaakt, wordt het kaart functie wordt alleen uitgevoerd voor nieuw ingevoegde of bijgewerkte documenten om de weergave incrementeel bij te werken.

Omdat de kaart de resultaten van de functie worden opgeslagen in de data-bucket, queries tegen een weergave vertonen lage latenties.

Laten we eens kijken naar een voorbeeld van een kaart functie die een index maakt op de naam veld van alle documenten in de bucket waarvan type veld is gelijk aan "StudentGrade":

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

De uitstoten functie vertelt Couchbase welk dataveld (en) in de indexsleutel (eerste parameter) moet worden opgeslagen en welke waarde (tweede parameter) moet worden geassocieerd met het geïndexeerde document.

In dit geval slaan we alleen het document op naam eigenschap in de indexsleutel. En aangezien we niet geïnteresseerd zijn in het associëren van een bepaalde waarde aan elk item, slagen we erin nul als waardeparameter.

Terwijl Couchbase de weergave verwerkt, maakt het een index van de sleutels die worden uitgezonden door de kaart functie, waarbij elke sleutel wordt geassocieerd met alle documenten waarvoor die sleutel is verzonden.

Als drie documenten bijvoorbeeld de naam eigenschap ingesteld op "John Doe"en vervolgens de indexsleutel "John Doe" zou worden geassocieerd met die drie documenten.

3.2. De verminderen Functie

De verminderen functie wordt gebruikt om geaggregeerde berekeningen uit te voeren met behulp van de resultaten van een kaart functie. De Couchbase Admin UI biedt een gemakkelijke manier om de ingebouwde verminderen functies "_Count", "_sum", en "_Stats", aan jouw kaart functie.

U kunt ook uw eigen schrijven verminderen functies voor complexere aggregaties. We zullen voorbeelden zien van het gebruik van de ingebouwde verminderen functies later in de zelfstudie.

4. Werken met weergaven en zoekopdrachten

4.1. De weergaven organiseren

Weergaven worden georganiseerd in een of meer ontwerpdocumenten per bucket. In theorie is er geen limiet aan het aantal views per ontwerpdocument. Voor optimale prestaties is echter aangeraden om elk ontwerpdocument te beperken tot minder dan tien weergaven.

Wanneer u voor het eerst een weergave maakt binnen een ontwerpdocument, duidt Couchbase dit aan als een ontwikkeling visie. U kunt zoekopdrachten uitvoeren tegen een ontwikkeling weergave om de functionaliteit te testen. Als u eenmaal tevreden bent met het uitzicht, zou u dat doen publiceren het ontwerpdocument en de weergave wordt een productie visie.

4.2. Query's maken

Om een ​​query te maken op basis van een Couchbase-weergave, moet u de naam van het ontwerpdocument en de weergavenaam opgeven om een ViewQuery voorwerp:

ViewQuery query = ViewQuery.from ("ontwerp-document-naam", "kijk-naam");

Als deze query wordt uitgevoerd, worden alle rijen van de weergave geretourneerd. We zullen in latere secties zien hoe we de resultatenset kunnen beperken op basis van de sleutelwaarden.

Als u een query wilt maken op basis van een ontwikkelingsweergave, kunt u de ontwikkeling() methode bij het maken van de query:

ViewQuery query = ViewQuery.from ("ontwerp-doc-naam", "kijk-naam"). Development ();

4.3. De zoekopdracht uitvoeren

Zodra we een ViewQuery object, kunnen we de query uitvoeren om een BekijkResultaat:

ViewResult resultaat = bucket.query (query);

4.4. Queryresultaten verwerken

En nu we een BekijkResultaat, kunnen we de rijen herhalen om de document-id's en / of inhoud te krijgen:

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

5. Voorbeeldtoepassing

Voor de rest van de tutorial zullen we MapReduce-weergaven en -vragen schrijven voor een set documenten met leerlingcijfers met de volgende indeling, met cijfers beperkt tot het bereik van 0 tot 100:

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

We slaan deze documenten op in de "baeldung-tutorial"Bucket en alle views in een ontwerpdocument met de naam"studentGrades. " Laten we eens kijken naar de code die nodig is om de bucket te openen, zodat we deze kunnen opvragen:

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

6. Exacte zoekvragen

Stel dat u alle cijfers van studenten wilt vinden voor een bepaalde cursus of reeks cursussen. Laten we een weergave schrijven met de naam 'findByCourse”Met behulp van het volgende kaart functie:

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

Merk op dat we in deze eenvoudige weergave alleen de Cursus veld.

6.1. Passend op een enkele sleutel

Om alle cijfers voor de cursus Geschiedenis te vinden, passen we de sleutel methode voor onze basisvraag:

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

6.2. Overeenkomen op meerdere sleutels

Als je alle cijfers voor wiskunde- en natuurwetenschappelijke vakken wilt vinden, kun je het sleutels methode aan de basisquery, door deze een reeks sleutelwaarden door te geven:

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

7. Bereikquery's

Om te kunnen zoeken naar documenten die een reeks waarden voor een of meer velden bevatten, hebben we een weergave nodig die de velden waarin we geïnteresseerd zijn, uitstraalt, en we moeten een onder- en / of bovengrens voor de zoekvraag specificeren.

Laten we eens kijken hoe bereikquery's met een enkel veld en meerdere velden kunnen worden uitgevoerd.

7.1. Query's die betrekking hebben op één enkel veld

Om alle documenten te vinden met een bereik van rang waarden ongeacht de waarde van de Cursus veld, hebben we een weergave nodig die alleen de rang veld. Laten we de kaart functie voor de “findByGrade" visie:

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

Laten we een vraag in Java schrijven met behulp van deze weergave om alle cijfers te vinden die gelijk zijn aan een "B" lettercijfer (80 t / m 89):

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

Houd er rekening mee dat de startsleutelwaarde in een bereikquery altijd als inclusief wordt behandeld.

En als bekend is dat alle cijfers gehele getallen zijn, levert de volgende query dezelfde resultaten op:

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

Om alle A-cijfers (90 en hoger) te vinden, hoeven we alleen de ondergrens op te geven:

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

En om alle onvoldoende cijfers te vinden (onder de 60), hoeven we alleen de bovengrens te specificeren:

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

7.2. Query's waarbij meerdere velden betrokken zijn

Stel nu dat we alle studenten in een specifieke cursus willen vinden waarvan het cijfer binnen een bepaald bereik valt. Deze query vereist een nieuwe weergave die zowel de Cursus en rang velden.

Bij weergaven met meerdere velden wordt elke indexsleutel verzonden als een reeks waarden. Omdat onze zoekopdracht een vaste waarde voor betreft Cursus en een reeks van rang waarden, zullen we de kaartfunctie schrijven om elke sleutel uit te zenden als een array met de vorm [Cursus, rang].

Laten we eens kijken naar de kaart functie voor het uitzicht “findByCourseAndGrade“:

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

Wanneer deze weergave wordt gevuld in Couchbase, worden de indexitems gesorteerd op Cursus en rang. Hier is een subset van sleutels in de "findByCourseAndGrade”Weergave weergegeven in hun natuurlijke sorteervolgorde:

["Geschiedenis", 80] ["Geschiedenis", 90] ["Geschiedenis", 94] ["Wiskunde", 82] ["Wiskunde", 88] ["Wiskunde", 97] ["Wetenschap", 78] [ "Science", 86] ["Science", 92]

Aangezien de sleutels in deze weergave arrays zijn, zou u ook arrays van deze indeling gebruiken bij het specificeren van de onder- en bovengrenzen van een bereikquery voor deze weergave.

Dit betekent dat om alle studenten te vinden die een "B" -cijfer (80 tot 89) hebben behaald in de wiskundecursus, u de ondergrens zou instellen op:

["Wiskunde", 80]

en de bovengrens naar:

["Wiskunde", 89]

Laten we de bereikquery in Java schrijven:

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

Als we alle studenten willen vinden die een "A" -cijfer (90 en hoger) hebben behaald in wiskunde, dan zouden we schrijven:

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

Merk op dat omdat we de cursuswaarde vastleggen op "Wiskunde“, We moeten een bovengrens opnemen met de hoogst mogelijke rang waarde. Anders zou onze resultatenset ook alle documenten bevatten waarvan Cursus waarde is lexicografisch groter dan 'Wiskunde“.

En om alle falende wiskundige cijfers te vinden (onder de 60):

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

Net als in het vorige voorbeeld, moeten we een ondergrens specificeren met het laagst mogelijke cijfer. Anders zou onze resultatenset ook alle cijfers bevatten waarbij de Cursus waarde is lexicografisch kleiner dan 'Wiskunde“.

Tot slot, om de vijf hoogste wiskundecijfers te vinden (behoudens gelijkspel), kun je Couchbase vertellen om een ​​aflopende sortering uit te voeren en de grootte van de resultatenset te beperken:

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

Merk op dat wanneer u een aflopende sortering uitvoert, de startKey en endKey waarden worden omgekeerd, omdat Couchbase de sortering toepast voordat deze de limiet.

8. Geaggregeerde zoekopdrachten

Een belangrijk voordeel van MapReduce-weergaven is dat ze zeer efficiënt zijn voor het uitvoeren van geaggregeerde query's op grote datasets. In onze dataset studentcijfers kunnen we bijvoorbeeld eenvoudig de volgende aggregaten berekenen:

  • aantal studenten in elke cursus
  • som van kredieturen voor elke student
  • cijferpuntgemiddelde voor elke student over alle cursussen

Laten we een weergave en query maken voor elk van deze berekeningen met behulp van ingebouwde verminderen functies.

8.1. De ... gebruiken tellen () Functie

Laten we eerst het kaart functie voor een weergave om het aantal studenten in elke cursus te tellen:

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

We noemen deze weergave 'countStudentsByCourse”En geef aan dat het de ingebouwde "_Count" functie. En aangezien we slechts een simpele telling uitvoeren, kunnen we nog steeds uitzenden nul als de waarde voor elk item.

Om het aantal studenten in elke cursus te tellen:

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

Het extraheren van gegevens uit verzamelde zoekopdrachten is anders dan wat we tot nu toe hebben gezien. In plaats van voor elke rij in het resultaat een overeenkomend Couchbase-document te extraheren, extraheren we de geaggregeerde sleutels en resultaten.

Laten we de query uitvoeren en de tellingen extraheren in een java.util.Map:

ViewResult resultaat = bucket.query (query); Map numStudentsByCourse = nieuwe HashMap (); voor (ViewRow row: result.allRows ()) {JsonArray keyArray = (JsonArray) row.key (); String course = keyArray.getString (0); long count = Long.valueOf (row.value (). toString ()); numStudentsByCourse.put (cursus, aantal); }

8.2. De ... gebruiken som() Functie

Laten we vervolgens een overzicht schrijven dat de som van de kredieturen van elke student berekent. We noemen deze weergave 'sumHoursByStudent”En geef aan dat het de ingebouwde "_som" functie:

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

Merk op dat bij het aanbrengen van de "_som" functie, we moeten uitstoten de waarde die moet worden opgeteld - in dit geval het aantal studiepunten - voor elk item.

Laten we een vraag schrijven om het totale aantal studiepunten voor elke student te vinden:

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

En laten we nu de query uitvoeren en de geaggregeerde sommen extraheren in een java.util.Map:

ViewResult resultaat = bucket.query (query); Map hoursByStudent = nieuwe HashMap (); voor (ViewRow row: result.allRows ()) {String name = (String) row.key (); long sum = Long.valueOf (row.value (). toString ()); hoursByStudent.put (naam, som); }

8.3. Berekenen van Grade Point-gemiddelden

Stel dat we het Grade Point Average (GPA) van elke student voor alle cursussen willen berekenen, met behulp van de conventionele cijferpuntschaal op basis van de behaalde cijfers en het aantal credit-uren dat de cursus waard is (A = 4 punten per credit-uur, B = 3 punten per kredietuur, C = 2 punten per kredietuur en D = 1 punt per kredietuur).

Er is geen ingebouwde verminderen functie om gemiddelde waarden te berekenen, dus we combineren de uitvoer van twee weergaven om de GPA te berekenen.

We hebben al de "SumHoursByStudent" weergave die het aantal kredieturen dat elke student heeft geprobeerd bij elkaar optelt. Nu hebben we het totale aantal punten nodig dat elke student heeft verdiend.

Laten we een weergave maken met de naam "SumGradePointsByStudent" dat het aantal behaalde punten voor elke gevolgde cursus berekent. We gebruiken de ingebouwde "_som" functie om het volgende te verminderen kaart functie:

function (doc, meta) {if (doc.type == "StudentGrade" && doc.name && doc.hours && doc.grade) {if (doc.grade> = 90) {emit (doc.name, 4 * doc .uren); } else if (doc.grade> = 80) {emit (doc.name, 3 * doc.hours); } else if (doc.grade> = 70) {emit (doc.name, 2 * doc.hours); } else if (doc.grade> = 60) {emit (doc.name, doc.hours); } else {emit (doc.name, 0); }}}

Laten we nu deze weergave onderzoeken en de sommen extraheren in een java.util.Map:

ViewQuery query = ViewQuery.from ("studentGrades", "sumGradePointsByStudent") .reduce () .groupLevel (1); ViewResult resultaat = bucket.query (query); Kaart gradePointsByStudent = nieuwe HashMap (); voor (ViewRow row: result.allRows ()) {String course = (String) row.key (); long sum = Long.valueOf (row.value (). toString ()); gradePointsByStudent.put (cursus, som); }

Laten we tot slot de twee combineren Kaarts om de GPA voor elke student te berekenen:

Kaartresultaat = nieuwe HashMap (); voor (Entry creditHoursEntry: hoursByStudent.entrySet ()) {String name = creditHoursEntry.getKey (); lang totalHours = creditHoursEntry.getValue (); lang totalGradePoints = gradePointsByStudent.get (naam); result.put (naam, ((float) totalGradePoints / totalHours)); }

9. Conclusie

We hebben gedemonstreerd hoe u enkele basis MapReduce-weergaven in Couchbase kunt schrijven, en hoe u query's tegen de weergaven kunt construeren en uitvoeren en de resultaten kunt extraheren.

De code die in deze tutorial wordt gepresenteerd, is te vinden in het GitHub-project.

U kunt meer leren over MapReduce-weergaven en hoe u ze in Java kunt opvragen op de officiële Couchbase-documentatie voor ontwikkelaars.