MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Aggregazioni MongoDB utilizzando Java

1. Panoramica

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

Esamineremo prima cosa significa concettualmente l'aggregazione, quindi imposteremo 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 trasmesso come input alla fase successiva.

Le fasi più comunemente utilizzate possono essere riassunte in:

Fase equivalente SQL Descrizione
 progetto SELECT seleziona solo i campi obbligatori, può essere utilizzato anche per calcolare e aggiungere campi derivati ​​alla raccolta
 corrispondenza DOVE filtra la raccolta secondo criteri specificati
 gruppo GRUPPO PER raccoglie gli input in base ai criteri specificati (ad es. conteggio, somma) per restituire un documento per ogni raggruppamento distinto
 ordina ORDINA PER ordina i risultati in ordine crescente o decrescente di un determinato campo
 conta COUNT conta i documenti contenuti nella raccolta
 limite LIMIT limita il risultato a un numero specificato di documenti, invece di restituire l'intera collezione
 fuori SELEZIONARE IN NEW_TABLE scrive il risultato in una raccolta denominata; questa fase è accettabile solo come l'ultima in una pipeline


L'equivalente SQL per ogni fase di aggregazione è incluso sopra per darci un'idea del significato di tale 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 di appartenenza del Paese; ad esempio, Europa
  • area – l'area geografica del Paese
  • lingue – lingue ufficiali del paese in formato array; ad esempio, inglese
  • confini – una serie di alpha3Code dei paesi vicini s

Ora vediamo 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 consiste nell'importarlo in MongoDB utilizzando mongoimport comando:

mongoimport.exe --db <db_name> --collection <collection_name> --file <path_to_file> --jsonArray

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

4. Esempi di aggregazione in Java

Ora che abbiamo coperto le basi, entriamo nel trarre alcune informazioni significative dai dati di cui disponiamo per tutti i paesi . Utilizzeremo diversi test JUnit per questo scopo.

Ma prima di farlo, dobbiamo stabilire 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 gli aggregati classe helper fornita dal driver Java MongoDB.

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

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

4.1. corrispondenza e conta

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

Ora, supponiamo 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"));
}

Qui stiamo utilizzando due fasi nella nostra pipeline di aggregazione:corrispondenza e conta .

Innanzitutto, filtriamo la raccolta in modo che corrisponda solo ai documenti che contengono inglese nelle loro lingue campo. Questi documenti possono essere immaginati come una raccolta temporanea o intermedia che diventa l'input per la nostra fase successiva, contare. Questo conta il numero di documenti nella fase precedente.

Un altro punto da notare in questo esempio è l'uso del metodo first . Poiché sappiamo che l'output dell'ultima fase, conta , sarà un singolo record, questo è un modo garantito per estrarre l'unico documento risultante.

4.2. gruppo (con somma ) e ordina

In questo esempio, il nostro obiettivo è scoprire la regione geografica che contiene il numero massimo di paesi :

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

Come è evidente, stiamo utilizzando gruppo e ordina per raggiungere il nostro obiettivo qui .

Innanzitutto, raccogliamo il numero di paesi in ciascuna regione accumulando una somma delle loro occorrenze in una variabile tally. Questo ci fornisce una raccolta intermedia di documenti, ciascuno contenente due campi:la regione e il conteggio dei paesi in essa contenuti. Quindi lo ordiniamo in ordine decrescente ed estraiamo il primo documento per darci la regione con il numero massimo di paesi.

4.3. ordina, limite e fuori

Ora usiamo ordina , limite e fuori per estrarre i sette paesi più grandi per area e scriverli in una nuova raccolta :

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

Qui, abbiamo prima ordinato la raccolta data in ordine decrescente di area. Quindi, abbiamo utilizzato Aggregates#limit metodo per limitare il risultato a soli sette documenti. Infine, abbiamo utilizzato out fase per deserializzare questi dati in una nuova raccolta denominata largest_seven . Questa raccolta ora può essere utilizzata allo stesso modo di qualsiasi altra, ad esempio per trovare se contiene USA.

4.4. progetto, gruppo (con max), partita

Nel nostro ultimo esempio, proviamo qualcosa di più complicato. Supponiamo di dover scoprire quanti confini ogni paese condivide con gli altri e qual è il numero massimo di questo tipo .

Ora nel nostro set di dati abbiamo un bordi campo, che è un array che elenca alpha3Code s per tutti i paesi confinanti della nazione, ma ​​non c'è nessun campo che ci dia direttamente il conteggio. Quindi dovremo ricavare il numero di paesi confinanti utilizzando progetto :

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

Dopodiché, come abbiamo visto prima, raggrupperemo la collezione prevista per trovare il max valore di paesi confinanti . Una cosa da sottolineare qui è che il max accumulatore ci dà il valore massimo come numero , non l'intero Documento contenente il valore massimo. Dobbiamo eseguire corrispondenza per filtrare il Documento desiderato se devono essere eseguite ulteriori operazioni.