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

Spring Data MongoDB:Proyecciones y Agregaciones

1. Resumen

Spring Data MongoDB proporciona abstracciones simples de alto nivel para el lenguaje de consulta nativo de MongoDB. En este artículo, exploraremos la compatibilidad con el marco de proyección y agregación.

Si es nuevo en este tema, consulte nuestro artículo introductorio Introducción a Spring Data MongoDB.

2. Proyección

En MongoDB, las proyecciones son una forma de obtener solo los campos obligatorios de un documento de una base de datos. Esto reduce la cantidad de datos que deben transferirse del servidor de la base de datos al cliente y, por lo tanto, aumenta el rendimiento.

Con Spring Data MongDB, las proyecciones se pueden usar tanto con MongoTemplate y Repositorio Mongo.

Antes de continuar, veamos el modelo de datos que usaremos:

@Document
public class User {
    @Id
    private String id;
    private String name;
    private Integer age;
    
    // standard getters and setters
}

2.1. Proyecciones usando MongoTemplate

El incluir() y excluir() métodos en el Field class se usa para incluir y excluir campos respectivamente:

Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);

Estos métodos se pueden encadenar para incluir o excluir múltiples campos. El campo marcado como @Id (_id en la base de datos) siempre se obtiene a menos que se excluya explícitamente.

Los campos excluidos son null en la instancia de clase modelo cuando los registros se recuperan con proyección. En el caso de que los campos sean de un tipo primitivo o su clase contenedora, el valor de los campos excluidos son los valores predeterminados de los tipos primitivos.

Por ejemplo, Cadena sería nulo , int /Entero sería 0 y booleano /Booleano sería falso .

Así, en el ejemplo anterior, el nombre el campo sería John , id sería nulo y edad sería 0.

2.2. Proyecciones utilizando MongoRepository

Al usar MongoRepositories, los campos de @Consulta la anotación se puede definir en formato JSON:

@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();

El resultado sería el mismo que usar MongoTemplate. El valor=”{}” indica que no hay filtros y, por lo tanto, se obtendrán todos los documentos.

3. Agregación

La agregación en MongoDB se creó para procesar datos y devolver resultados calculados. Los datos se procesan en etapas y la salida de una etapa se proporciona como entrada para la siguiente etapa. Esta capacidad de aplicar transformaciones y realizar cálculos sobre los datos en etapas hace que la agregación sea una herramienta muy poderosa para el análisis.

Spring Data MongoDB proporciona una abstracción para consultas de agregación nativa utilizando las tres clases Agregación que envuelve una consulta de agregación, AggregationOperation que envuelve etapas de canalización individuales y Resultados de agregación que es el contenedor del resultado producido por la agregación.

Para realizar una agregación, primero, cree canalizaciones de agregación utilizando los métodos de compilación estáticos en Agregación clase, luego cree una instancia de Agregación utilizando la nueva Agregación() método en la Agregación class y finalmente ejecute la agregación usando MongoTemplate :

MatchOperation matchStage = Aggregation.match(new Criteria("foo").is("bar"));
ProjectionOperation projectStage = Aggregation.project("foo", "bar.baz");
        
Aggregation aggregation 
  = Aggregation.newAggregation(matchStage, projectStage);

AggregationResults<OutType> output 
  = mongoTemplate.aggregate(aggregation, "foobar", OutType.class);

Tenga en cuenta que tanto MatchOperation y Operación de proyección implementar Operación de agregación . Hay implementaciones similares para otras canalizaciones de agregación. Fuera de tipo es el modelo de datos para la salida esperada.

Ahora, veremos algunos ejemplos y sus explicaciones para cubrir los principales canales y operadores de agregación.

El conjunto de datos que usaremos en este artículo enumera detalles sobre todos los códigos postales en los EE. UU. que se pueden descargar del repositorio de MongoDB.

Veamos un documento de muestra después de importarlo a una colección llamada zips en la prueba base de datos.

{
    "_id" : "01001",
    "city" : "AGAWAM",
    "loc" : [
        -72.622739,
        42.070206
    ],
    "pop" : 15338,
    "state" : "MA"
}

En aras de la simplicidad y para que el código sea conciso, en los siguientes fragmentos de código supondremos que todos los estáticos métodos de Agregación class se importan estáticamente.

3.1. Obtener todos los estados con una población superior a 10 millones Ordenar por población descendente

Aquí tendremos tres pipelines:

  1. $grupo etapa que resume la población de todos los códigos postales
  2. $coincidencia etapa para filtrar estados con una población de más de 10 millones
  3. $ordenar etapa para ordenar todos los documentos en orden descendente de población

La salida esperada tendrá un campo _id como estado y un campo statePop con la población total del estado. Vamos a crear un modelo de datos para esto y ejecutar la agregación:

public class StatePoulation {
 
    @Id
    private String state;
    private Integer statePop;
 
    // standard getters and setters
}

El @ID la anotación asignará el _id campo de salida a estado en el modelo:

GroupOperation groupByStateAndSumPop = group("state")
  .sum("pop").as("statePop");
MatchOperation filterStates = match(new Criteria("statePop").gt(10000000));
SortOperation sortByPopDesc = sort(Sort.by(Direction.DESC, "statePop"));

Aggregation aggregation = newAggregation(
  groupByStateAndSumPop, filterStates, sortByPopDesc);
AggregationResults<StatePopulation> result = mongoTemplate.aggregate(
  aggregation, "zips", StatePopulation.class);

Los Resultados de agregación la clase implementa Iterable y por lo tanto podemos iterar sobre él e imprimir los resultados.

Si no se conoce el modelo de datos de salida, la clase MongoDB estándar Documento se puede usar.

3.2. Obtenga el estado más pequeño por población promedio de la ciudad

Para este problema, necesitaremos cuatro etapas:

  1. $grupo para sumar la población total de cada ciudad
  2. $grupo para calcular la población promedio de cada estado
  3. $ordenar etapa para ordenar los estados por su población promedio de la ciudad en orden ascendente
  4. $límite para obtener el primer estado con la población de ciudad promedio más baja

Aunque no es necesariamente necesario, utilizaremos un $proyecto adicional etapa para reformatear el documento según fuera StatePopulation modelo de datos.

GroupOperation sumTotalCityPop = group("state", "city")
  .sum("pop").as("cityPop");
GroupOperation averageStatePop = group("_id.state")
  .avg("cityPop").as("avgCityPop");
SortOperation sortByAvgPopAsc = sort(Sort.by(Direction.ASC, "avgCityPop"));
LimitOperation limitToOnlyFirstDoc = limit(1);
ProjectionOperation projectToMatchModel = project()
  .andExpression("_id").as("state")
  .andExpression("avgCityPop").as("statePop");

Aggregation aggregation = newAggregation(
  sumTotalCityPop, averageStatePop, sortByAvgPopAsc,
  limitToOnlyFirstDoc, projectToMatchModel);

AggregationResults<StatePopulation> result = mongoTemplate
  .aggregate(aggregation, "zips", StatePopulation.class);
StatePopulation smallestState = result.getUniqueMappedResult();

En este ejemplo, ya sabemos que solo habrá un documento en el resultado, ya que limitamos el número de documentos de salida a 1 en la última etapa. Como tal, podemos invocar getUniqueMappedResult() para obtener la StatePopulation requerida instancia.

Otra cosa a tener en cuenta es que, en lugar de confiar en @Id anotación en el mapa _id decir, lo hemos hecho explícitamente en etapa de proyección.

3.3. Obtenga el estado con los códigos postales máximo y mínimo

Para este ejemplo, necesitamos tres etapas:

  1. $grupo para contar el número de códigos postales de cada estado
  2. $ordenar para ordenar los estados por el número de códigos postales
  3. $grupo para encontrar el estado con códigos postales máximo y mínimo usando $first y $último operadores
GroupOperation sumZips = group("state").count().as("zipCount");
SortOperation sortByCount = sort(Direction.ASC, "zipCount");
GroupOperation groupFirstAndLast = group().first("_id").as("minZipState")
  .first("zipCount").as("minZipCount").last("_id").as("maxZipState")
  .last("zipCount").as("maxZipCount");

Aggregation aggregation = newAggregation(sumZips, sortByCount, groupFirstAndLast);

AggregationResults<Document> result = mongoTemplate
  .aggregate(aggregation, "zips", Document.class);
Document document= result.getUniqueMappedResult();

Aquí no hemos usado ningún modelo sino que usamos el Documento ya proporcionado con el controlador MongoDB.