Aggregazioni MongoDB che utilizzano Java

1. Panoramica

In questo tutorial, faremo un tuffo nel framework MongoDB Aggregation utilizzando il driver Java MongoDB .

Vedremo prima cosa significa aggregazione concettualmente, quindi configureremo un set di dati. Infine, vedremo varie tecniche di aggregazione in azione utilizzando il builder Aggregates .

2. Cosa sono le aggregazioni?

Le aggregazioni vengono utilizzate in MongoDB per analizzare i dati e ricavarne informazioni significative .

Questi vengono solitamente eseguiti in varie fasi e le fasi formano una pipeline, in modo tale che l'output di una fase venga trasmessa come input alla fase successiva.

Le fasi più comunemente utilizzate possono essere riassunte come:

Palcoscenico Equivalente SQL Descrizione
progetto SELEZIONARE seleziona solo i campi obbligatori, può essere utilizzato anche per calcolare e aggiungere campi derivati ​​alla raccolta
incontro DOVE filtra la raccolta secondo i criteri specificati
gruppo RAGGRUPPA PER raccoglie input insieme secondo i criteri specificati (ad esempio count, sum) per restituire un documento per ogni raggruppamento distinto
ordinare ORDINATO DA ordina i risultati in ordine crescente o decrescente di un dato campo
contare CONTARE conta i documenti contenuti nella collezione
limite LIMITE limita il risultato a un numero specificato di documenti, invece di restituire l'intera raccolta
su SELEZIONA IN NEW_TABLE scrive il risultato in una raccolta denominata; questa fase è accettabile solo come l'ultima di una pipeline

L'equivalente SQL per ogni fase di aggregazione è incluso sopra per darci un'idea di cosa significhi detta operazione nel mondo SQL.

A breve esamineremo esempi di codice Java per tutte queste fasi. Ma prima abbiamo bisogno di un database.

3. Configurazione del database

3.1. Set di dati

Il primo e più importante requisito per apprendere qualsiasi cosa relativa al database è il set di dati stesso!

Ai fini di questo tutorial, utilizzeremo un endpoint API riposante pubblicamente disponibile che fornisce informazioni complete su tutti i paesi del mondo. Questa API ci fornisce molti punti dati per un paese in un comodo formato JSON . Alcuni dei campi che utilizzeremo nella nostra analisi sono:

  • nome : il nome del paese; ad esempio, Stati Uniti d'America
  • alpha3Code - uno shortcode per il nome del paese; ad esempio, IND (per l'India)

  • regione - la regione a cui appartiene il paese; per esempio, l' Europa
  • area - l'area geografica del paese
  • lingue - lingue ufficiali del paese in un formato array; ad esempio, l' inglese
  • le frontiere - una serie di paesi confinanti alpha3Code s

Vediamo ora come convertire questi dati in una raccolta in un database MongoDB .

3.2. Importazione in MongoDB

Innanzitutto, dobbiamo raggiungere l'endpoint API per ottenere tutti i paesi e salvare la risposta localmente in un file JSON . Il passaggio successivo è importarlo in MongoDB utilizzando il comando mongoimport :

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

Un'importazione riuscita dovrebbe darci una raccolta con 250 documenti.

4. Esempi di aggregazione in Java

Ora che abbiamo coperto le basi, proviamo a ricavare alcune informazioni significative dai dati che abbiamo per tutti i paesi . Useremo diversi test JUnit per questo scopo.

Ma prima di farlo, dobbiamo effettuare una connessione al database:

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

In tutti gli esempi che seguono, utilizzeremo la classe helper Aggregates fornita dal driver Java MongoDB.

Per una migliore leggibilità dei nostri snippet, possiamo aggiungere un'importazione statica:

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

4.1. abbinare e contare

Per cominciare, iniziamo con qualcosa di semplice. In precedenza abbiamo notato che il set di dati contiene informazioni sulle lingue.

Supponiamo ora di voler controllare il numero di paesi nel mondo in cui l'inglese è una lingua ufficiale :

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

Here we are using two stages in our aggregation pipeline: match and count.

First, we filter out the collection to match only those documents that contain English in their languages field. These documents can be imagined as a temporary or intermediate collection that becomes the input for our next stage, count. This counts the number of documents in the previous stage.

Another point to note in this sample is the use of the method first. Since we know that the output of the last stage, count, is going to be a single record, this is a guaranteed way to extract out the lone resulting document.

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.

Abbiamo utilizzato quattro esempi per illustrare le varie fasi di aggregazione per formare una comprensione di base del concetto. Ci sono innumerevoli possibilità per l'analisi dei dati offerte da questo framework che possono essere esplorate ulteriormente .

Per ulteriori letture, Spring Data MongoDB fornisce un modo alternativo per gestire le proiezioni e le aggregazioni in Java.

Come sempre, il codice sorgente è disponibile su GitHub.