1. Overzicht
In deze zelfstudie duiken we in het MongoDB Aggregation-framework met behulp van het MongoDB Java-stuurprogramma .
We bekijken eerst wat aggregatie conceptueel betekent en stellen vervolgens een dataset op. Ten slotte zullen we verschillende aggregatietechnieken in actie zien met behulp van Aggregates builder .
2. Wat zijn aggregaties?
Aggregaties worden gebruikt in MongoDB om gegevens te analyseren en er zinvolle informatie uit te halen .
Deze worden meestal in verschillende fasen uitgevoerd en de fasen vormen een pijplijn - zodat de output van de ene fase wordt doorgegeven als invoer naar de volgende fase.
De meest gebruikte fasen kunnen worden samengevat als:
Podium | SQL-equivalent | Beschrijving |
---|---|---|
project | SELECTEER | selecteert alleen de vereiste velden, kan ook worden gebruikt om te berekenen en afgeleide velden toe te voegen aan de verzameling |
overeenkomst | WAAR | filtert de collectie volgens gespecificeerde criteria |
groep | GROUP BY | verzamelt invoer volgens de gespecificeerde criteria (bijv. aantal, som) om een document te retourneren voor elke afzonderlijke groepering |
sorteer | ORDER BY | sorteert de resultaten in oplopende of aflopende volgorde van een bepaald veld |
count | COUNT | telt de documenten die de collectie bevat |
limiet | LIMIET | beperkt het resultaat tot een bepaald aantal documenten, in plaats van de hele verzameling terug te sturen |
uit | SELECTEER IN NIEUWE_TABLE | schrijft het resultaat naar een benoemde verzameling; deze fase is alleen acceptabel als de laatste in een pijplijn |
Het SQL-equivalent voor elke aggregatiefase is hierboven opgenomen om ons een idee te geven van wat de genoemde bewerking betekent in de SQL-wereld.
We zullen binnenkort naar Java-codevoorbeelden kijken voor al deze fasen. Maar daarvoor hebben we een database nodig.
3. Database instellen
3.1. Gegevensset
De eerste en belangrijkste vereiste om iets met databases te leren, is de dataset zelf!
Voor deze zelfstudie gebruiken we een openbaar beschikbaar rustgevend API-eindpunt dat uitgebreide informatie biedt over alle landen van de wereld. Deze API geeft ons veel datapunten voor een land in een handig JSON-formaat . Enkele van de velden die we in onze analyse zullen gebruiken zijn:
- naam – de naam van het land; bijvoorbeeld Verenigde Staten van Amerika
- alpha3Code – een shortcode voor de naam van het land; bijvoorbeeld IND (voor India)
- regio – de regio waartoe het land behoort; bijvoorbeeld Europa
- gebied – het geografische gebied van het land
- talen – officiële talen van het land in een array-indeling; bijvoorbeeld Engels
- grenzen – een reeks alpha3Code . van buurlanden s
Laten we nu eens kijken hoe we deze gegevens kunnen converteren naar een verzameling in een MongoDB-database .
3.2. Importeren naar MongoDB
Eerst moeten we het API-eindpunt raken om alle landen te krijgen en het antwoord lokaal opslaan in een JSON-bestand . De volgende stap is om het in MongoDB te importeren met behulp van de mongoimport commando:
mongoimport.exe --db <db_name> --collection <collection_name> --file <path_to_file> --jsonArray
Een succesvolle import zou ons een collectie met 250 documenten moeten opleveren.
4. Aggregatievoorbeelden in Java
Nu we de basis hebben behandeld, gaan we een aantal zinvolle inzichten afleiden uit de gegevens die we voor alle landen hebben . Hiervoor gebruiken we verschillende JUnit-tests.
Maar voordat we dat doen, moeten we een verbinding maken met de database:
@BeforeClass
public static void setUpDB() throws IOException {
mongoClient = MongoClients.create();
database = mongoClient.getDatabase(DATABASE);
collection = database.getCollection(COLLECTION);
}
In alle voorbeelden die volgen, gebruiken we de aggregaten helperklasse geleverd door het MongoDB Java-stuurprogramma.
Voor een betere leesbaarheid van onze fragmenten kunnen we een statische import toevoegen:
import static com.mongodb.client.model.Aggregates.*;
4.1. overeenkomst en tel
Laten we om te beginnen met iets eenvoudigs beginnen. Eerder merkten we op dat de dataset informatie over talen bevat.
Laten we nu zeggen dat we het aantal landen in de wereld willen controleren waar Engels een officiële taal is :
@Test
public void givenCountryCollection_whenEnglishSpeakingCountriesCounted_thenNinetyOne() {
Document englishSpeakingCountries = collection.aggregate(Arrays.asList(
match(Filters.eq("languages.name", "English")),
count())).first();
assertEquals(91, englishSpeakingCountries.get("count"));
}
Hier gebruiken we twee fasen in onze aggregatiepijplijn:match en tel .
Eerst filteren we de verzameling uit zodat deze alleen overeenkomt met die documenten die Engels . bevatten in hun talen veld. Deze documenten kunnen worden voorgesteld als een tijdelijke of tussentijdse verzameling die de input wordt voor onze volgende fase, telling. Dit telt het aantal documenten in de vorige fase.
Een ander punt om op te merken in dit voorbeeld is het gebruik van de methode first . Omdat we weten dat de uitvoer van de laatste fase, tel , wordt een enkel record, dit is een gegarandeerde manier om het enige resulterende document eruit te halen.
4.2. groep (met som ) en sorteer
In dit voorbeeld is ons doel om de geografische regio te vinden die het maximale aantal landen bevat :
@Test
public void givenCountryCollection_whenCountedRegionWise_thenMaxInAfrica() {
Document maxCountriedRegion = collection.aggregate(Arrays.asList(
group("$region", Accumulators.sum("tally", 1)),
sort(Sorts.descending("tally")))).first();
assertTrue(maxCountriedRegion.containsValue("Africa"));
}
Zoals duidelijk is, gebruiken we groep en sorteer om ons doel hier te bereiken .
Eerst verzamelen we het aantal landen in elke regio door een som . op te tellen van hun voorkomen in een variabele tally. Dit geeft ons een tussenliggende verzameling documenten, die elk twee velden bevatten:de regio en het aantal landen daarin. Dan sorteren we het in aflopende volgorde en extraheren we het eerste document om ons de regio met het maximale aantal landen te geven.
4.3. sorteer, limiet, en uit
Laten we nu sorteren . gebruiken , limiet en uit om de zeven grootste landen qua oppervlakte te extraheren en ze in een nieuwe verzameling te schrijven :
@Test
public void givenCountryCollection_whenAreaSortedDescending_thenSuccess() {
collection.aggregate(Arrays.asList(
sort(Sorts.descending("area")),
limit(7),
out("largest_seven"))).toCollection();
MongoCollection<Document> largestSeven = database.getCollection("largest_seven");
assertEquals(7, largestSeven.countDocuments());
Document usa = largestSeven.find(Filters.eq("alpha3Code", "USA")).first();
assertNotNull(usa);
}
Hier hebben we eerst de gegeven verzameling gesorteerd in de aflopende volgorde van gebied. Vervolgens gebruikten we de Aggregates#limit methode om het resultaat te beperken tot slechts zeven documenten. Ten slotte gebruikten we de out fase om deze gegevens te deserialiseren in een nieuwe verzameling genaamd largest_seven . Deze verzameling kan nu op dezelfde manier worden gebruikt als elke andere - bijvoorbeeld om vinden als het VS. . bevat
4.4. project, groep (met max), match
Laten we in ons laatste voorbeeld iets lastiger proberen. Stel dat we moeten weten hoeveel grenzen elk land deelt met anderen, en wat het maximale aantal is .
Nu hebben we in onze dataset een borders veld, dat een array is met alpha3Code s voor alle aangrenzende landen van het land, maar er is geen veld dat ons rechtstreeks de telling geeft. We moeten dus het aantal borderingCountries . afleiden met behulp van project :
@Test
public void givenCountryCollection_whenNeighborsCalculated_thenMaxIsFifteenInChina() {
Bson borderingCountriesCollection = project(Projections.fields(Projections.excludeId(),
Projections.include("name"), Projections.computed("borderingCountries",
Projections.computed("$size", "$borders"))));
int maxValue = collection.aggregate(Arrays.asList(borderingCountriesCollection,
group(null, Accumulators.max("max", "$borderingCountries"))))
.first().getInteger("max");
assertEquals(15, maxValue);
Document maxNeighboredCountry = collection.aggregate(Arrays.asList(borderingCountriesCollection,
match(Filters.eq("borderingCountries", maxValue)))).first();
assertTrue(maxNeighboredCountry.containsValue("China"));
}
Daarna, zoals we eerder zagen, gaan we groeperen de geprojecteerde collectie om de max . te vinden waarde van grenslanden . Een ding om hier op te wijzen is dat de max accumulator geeft ons de maximale waarde als een getal , niet het hele Document met de maximale waarde. We moeten wedstrijd uitvoeren om het gewenste Document eruit te filteren als er verdere bewerkingen moeten worden uitgevoerd.