MongoDB-Aggregationen mit Java

1. Übersicht

In diesem Tutorial tauchen wir mit dem MongoDB Java-Treiber in das MongoDB Aggregation Framework ein .

Wir werden uns zunächst ansehen, was Aggregation konzeptionell bedeutet, und dann einen Datensatz einrichten. Schließlich werden wir verschiedene Aggregationstechniken mit dem Builder für Aggregate in Aktion sehen .

2. Was sind Aggregationen?

Aggregationen werden in MongoDB verwendet, um Daten zu analysieren und daraus aussagekräftige Informationen abzuleiten .

Diese werden normalerweise in verschiedenen Stufen ausgeführt, und die Stufen bilden eine Pipeline - so dass die Ausgabe einer Stufe als Eingabe an die nächste Stufe weitergegeben wird.

Die am häufigsten verwendeten Stufen können wie folgt zusammengefasst werden:

Bühne SQL-Äquivalent Beschreibung
Projekt WÄHLEN Wählt nur die erforderlichen Felder aus und kann auch zum Berechnen und Hinzufügen abgeleiteter Felder zur Sammlung verwendet werden
Spiel WO filtert die Sammlung nach festgelegten Kriterien
Gruppe GRUPPIERE NACH sammelt Eingaben gemäß den angegebenen Kriterien (z. B. Anzahl, Summe), um ein Dokument für jede einzelne Gruppierung zurückzugeben
Sortieren SORTIEREN NACH sortiert die Ergebnisse in aufsteigender oder absteigender Reihenfolge eines bestimmten Feldes
Anzahl ANZAHL zählt die Dokumente, die die Sammlung enthält
Grenze GRENZE beschränkt das Ergebnis auf eine bestimmte Anzahl von Dokumenten, anstatt die gesamte Sammlung zurückzugeben
aus SELECT IN NEW_TABLE schreibt das Ergebnis in eine benannte Sammlung; Diese Phase ist nur als letzte in einer Pipeline akzeptabel

Das SQL-Äquivalent für jede Aggregationsstufe ist oben enthalten, um uns eine Vorstellung davon zu geben, was diese Operation in der SQL-Welt bedeutet.

Wir werden uns in Kürze Java-Codebeispiele für all diese Phasen ansehen. Aber vorher brauchen wir eine Datenbank.

3. Datenbank-Setup

3.1. Datensatz

Die wichtigste Voraussetzung für das Erlernen von datenbankbezogenen Informationen ist der Datensatz selbst!

Für dieses Lernprogramm verwenden wir einen öffentlich verfügbaren Restful API-Endpunkt, der umfassende Informationen zu allen Ländern der Welt bietet. Diese API bietet uns viele Datenpunkte für ein Land in einem praktischen JSON-Format . Einige der Felder, die wir in unserer Analyse verwenden werden, sind:

  • Name - der Name des Landes; Zum Beispiel die Vereinigten Staaten von Amerika
  • alpha3Code - ein Shortcode für den Ländernamen; zum Beispiel IND (für Indien)

  • Region - die Region, zu der das Land gehört; zum Beispiel Europa
  • Gebiet - das geografische Gebiet des Landes
  • Sprachen - Amtssprachen des Landes in einem Array-Format; zum Beispiel Englisch
  • Grenzen - eine Anordnung von benachbarten Ländern alpha3Code s

Nun wollen wir sehen, wie diese Daten in eine Sammlung in einer MongoDB-Datenbank konvertiert werden .

3.2. Importieren in MongoDB

Zuerst müssen wir den API-Endpunkt erreichen, um alle Länder abzurufen und die Antwort lokal in einer JSON-Datei zu speichern . Der nächste Schritt besteht darin, es mit dem Befehl mongoimport in MongoDB zu importieren :

mongoimport.exe --db  --collection  --file  --jsonArray

Ein erfolgreicher Import sollte uns eine Sammlung mit 250 Dokumenten geben.

4. Aggregationsbeispiele in Java

Nachdem wir die Grundlagen abgedeckt haben, wollen wir einige aussagekräftige Erkenntnisse aus den Daten ableiten, die wir für alle Länder haben . Zu diesem Zweck werden wir mehrere JUnit-Tests verwenden.

Aber bevor wir das tun, müssen wir eine Verbindung zur Datenbank herstellen:

@BeforeClass public static void setUpDB() throws IOException { mongoClient = MongoClients.create(); database = mongoClient.getDatabase(DATABASE); collection = database.getCollection(COLLECTION); } 

In allen folgenden Beispielen verwenden wir die Aggregates- Hilfsklasse, die vom MongoDB-Java-Treiber bereitgestellt wird.

Zur besseren Lesbarkeit unserer Snippets können wir einen statischen Import hinzufügen:

import static com.mongodb.client.model.Aggregates.*;

4.1. übereinstimmen und zählen

Beginnen wir zunächst mit etwas Einfachem. Zuvor haben wir festgestellt, dass der Datensatz Informationen zu Sprachen enthält.

Nehmen wir nun an, wir möchten die Anzahl der Länder auf der Welt überprüfen, in denen Englisch eine offizielle Sprache ist :

@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 verwenden wir zwei Stufen in unserer Aggregationspipeline: Match und Count .

Zunächst filtern wir die Sammlung heraus, um nur die Dokumente abzugleichen, deren Sprachfeld Englisch enthält . Diese Dokumente können als temporäre oder Zwischensammlung betrachtet werden, die als Input für unsere nächste Stufe, count, dient. Dies zählt die Anzahl der Dokumente in der vorherigen Stufe.

Ein weiterer in diesem Beispiel zu beachtender Punkt ist die Verwendung der Methode zuerst . Da wir wissen, dass die Ausgabe der letzten Stufe, count , ein einzelner Datensatz sein wird, ist dies eine garantierte Möglichkeit, das einzige resultierende Dokument zu extrahieren.

4.2. group (with sum) and sort

In this example, our objective is to find out the geographical region containing the maximum number of countries:

@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")); }

As is evident, we are using group and sort to achieve our objective here.

First, we gather the number of countries in each region by accumulating a sum of their occurrences in a variable tally. This gives us an intermediate collection of documents, each containing two fields: the region and the tally of countries in it. Then we sort it in the descending order and extract the first document to give us the region with maximum countries.

4.3. sort,limit, and out

Now let's use sort, limit and out to extract the seven largest countries area-wise and write them into a new collection:

@Test public void givenCountryCollection_whenAreaSortedDescending_thenSuccess() { collection.aggregate(Arrays.asList( sort(Sorts.descending("area")), limit(7), out("largest_seven"))).toCollection(); MongoCollection largestSeven = database.getCollection("largest_seven"); assertEquals(7, largestSeven.countDocuments()); Document usa = largestSeven.find(Filters.eq("alpha3Code", "USA")).first(); assertNotNull(usa); }

Here, we first sorted the given collection in the descending order of area. Then, we used the Aggregates#limit method to restrict the result to seven documents only. Finally, we used the out stage to deserialize this data into a new collection called largest_seven. This collection can now be used in the same way as any other – for example, to find if it contains USA.

4.4. project, group (with max), match

In our last sample, let's try something trickier. Say we need to find out how many borders each country shares with others, and what is the maximum such number.

Now in our dataset, we have a borders field, which is an array listing alpha3Codes for all bordering countries of the nation, but there isn't any field directly giving us the count. So we'll need to derive the number of borderingCountries using 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")); }

After that, as we saw before, we'll group the projected collection to find the max value of borderingCountries. One thing to point out here is that the max accumulator gives us the maximum value as a number, not the entire Document containing the maximum value. We need to perform match to filter out the desired Document if any further operations are to be performed.

5. Conclusion

In this article, we saw what are MongoDB aggregations, and how to apply them in Java using an example dataset.

Wir haben vier Beispiele verwendet, um die verschiedenen Aggregationsstufen zu veranschaulichen und ein grundlegendes Verständnis des Konzepts zu erhalten. Dieses Framework bietet unzählige Möglichkeiten für die Datenanalyse, die weiter untersucht werden können .

Zur weiteren Lektüre bietet Spring Data MongoDB eine alternative Möglichkeit, Projektionen und Aggregationen in Java zu verarbeiten.

Wie immer ist der Quellcode über GitHub verfügbar.