sql >> Base de Datos >  >> NoSQL >> MongoDB

Agregaciones de MongoDB usando Java

1. Resumen

En este tutorial, nos sumergiremos en el marco de agregación de MongoDB usando el controlador Java de MongoDB .

Primero veremos qué significa conceptualmente la agregación y luego configuraremos un conjunto de datos. Finalmente, veremos varias técnicas de agregación en acción usando el generador de Agregados .

2. ¿Qué son las agregaciones?

Las agregaciones se utilizan en MongoDB para analizar datos y obtener información significativa de ellos .

Por lo general, se realizan en varias etapas, y las etapas forman una canalización, de modo que la salida de una etapa se pasa como entrada a la siguiente etapa.

Las etapas más utilizadas se pueden resumir en:

Escenario Equivalente de SQL Descripción
 proyecto SELECCIONAR selecciona solo los campos obligatorios, también se puede usar para calcular y agregar campos derivados a la colección
 coincidencia DÓNDE filtra la colección según los criterios especificados
 grupo GRUPO POR reúne las entradas según los criterios especificados (p. ej., recuento, suma) para devolver un documento para cada agrupación distinta
 ordenar ORDENAR POR ordena los resultados en orden ascendente o descendente de un campo determinado
 recuento COUNT cuenta los documentos que contiene la colección
 límite LIMIT limita el resultado a un número específico de documentos, en lugar de devolver la colección completa
 fuera SELECCIONAR EN NUEVA_TABLA escribe el resultado en una colección con nombre; esta etapa solo es aceptable como la última en una canalización


El equivalente de SQL para cada etapa de agregación se incluye arriba para darnos una idea de lo que significa dicha operación en el mundo de SQL.

Veremos ejemplos de código Java para todas estas etapas en breve. Pero antes de eso, necesitamos una base de datos.

3. Configuración de la base de datos

3.1. Conjunto de datos

¡El primer y más importante requisito para aprender cualquier cosa relacionada con la base de datos es el propio conjunto de datos!

A los efectos de este tutorial, utilizaremos un punto final de API de descanso disponible públicamente que proporciona información completa sobre todos los países del mundo. Esta API nos brinda muchos puntos de datos para un país en un conveniente formato JSON . Algunos de los campos que usaremos en nuestro análisis son:

  • nombre – el nombre del país; por ejemplo, Estados Unidos de América
  • código alfa3 – un código abreviado para el nombre del país; por ejemplo, IND (para India)
  • región – la región a la que pertenece el país; por ejemplo, Europa
  • área – la zona geográfica del país
  • idiomas – idiomas oficiales del país en un formato de matriz; por ejemplo, inglés
  • bordes – una serie de alpha3Code de los países vecinos s

Ahora veamos cómo convertir estos datos en una colección en una base de datos MongoDB .

3.2. Importando a MongoDB

Primero, debemos llegar al extremo de la API para obtener todos los países y guardar la respuesta localmente en un archivo JSON . El siguiente paso es importarlo a MongoDB usando mongoimport comando:

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

La importación exitosa debería darnos una colección con 250 documentos.

4. Muestras de agregación en Java

Ahora que tenemos las bases cubiertas, comencemos a obtener algunas ideas significativas de los datos que tenemos para todos los países . Usaremos varias pruebas JUnit para este propósito.

Pero antes de hacer eso, necesitamos hacer una conexión a la base de datos:

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

En todos los ejemplos que siguen, estaremos usando los Agregados clase auxiliar proporcionada por el controlador MongoDB Java.

Para una mejor legibilidad de nuestros fragmentos, podemos agregar una importación estática:

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

4.1. coincidir y contar

Para empezar, comencemos con algo simple. Anteriormente notamos que el conjunto de datos contiene información sobre idiomas.

Ahora, digamos que queremos comprobar la cantidad de países en el mundo donde el inglés es un idioma oficial :

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

Aquí estamos usando dos etapas en nuestra canalización de agregación:coincidencia y contar .

Primero, filtramos la colección para que coincida solo con aquellos documentos que contienen inglés en sus idiomas campo. Estos documentos se pueden imaginar como una colección temporal o intermedia que se convierte en el insumo para nuestra siguiente etapa, contar. Esto cuenta la cantidad de documentos en la etapa anterior.

Otro punto a tener en cuenta en esta muestra es el uso del método primero . Como sabemos que la salida de la última etapa, count , va a ser un único registro, esta es una forma garantizada de extraer el único documento resultante.

4.2. grupo (con suma ) y ordenar

En este ejemplo, nuestro objetivo es descubrir la región geográfica que contiene el máximo número de países :

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

Como es evidente, estamos usando grupo y ordenar para lograr nuestro objetivo aquí .

Primero, reunimos el número de países en cada región acumulando una suma de sus ocurrencias en una variable tally. Esto nos da una colección intermedia de documentos, cada uno con dos campos:la región y el recuento de países en ella. Luego lo clasificamos en orden descendente y extraemos el primer documento para darnos la región con el máximo de países.

4.3. clasificar, límite, y fuera

Ahora usemos ordenar , límite y fuera para extraer los siete países más grandes por área y escribirlos en una nueva colección :

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

Aquí, primero ordenamos la colección dada en orden descendente de área. Luego, usamos el Aggregates#limit para restringir el resultado a siete documentos solamente. Finalmente, usamos el out etapa para deserializar estos datos en una nueva colección llamada largest_seven . Esta colección ahora se puede usar de la misma manera que cualquier otra, por ejemplo, para buscar si contiene EE.UU.

4.4. proyecto, grupo (con máx.), partido

En nuestra última muestra, intentemos algo más complicado. Digamos que necesitamos descubrir cuántas fronteras comparte cada país con otros, y cuál es el número máximo de ese número .

Ahora, en nuestro conjunto de datos, tenemos un bordes campo, que es una lista de matriz alpha3Code s para todos los países limítrofes de la nación, pero no hay ningún campo que nos brinde directamente el conteo. Así que necesitaremos derivar el número de borderingCountries usando proyecto :

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

Después de eso, como vimos antes, agruparemos la colección proyectada para encontrar el max valor de borderingCountries . Una cosa a señalar aquí es que el máx acumulador nos da el valor máximo como un número , no el Documento completo que contiene el valor máximo. Necesitamos realizar match para filtrar el Documento deseado si se van a realizar más operaciones.