MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Spring Data MongoDB:projeções e agregações

1. Visão geral


O Spring Data MongoDB fornece abstrações simples de alto nível para a linguagem de consulta nativa do MongoDB. Neste artigo, exploramos o suporte para a estrutura de projeções e agregação.

Se você é novo neste tópico, consulte nosso artigo introdutório Introdução ao Spring Data MongoDB.

2. Projeção


No MongoDB, as projeções são uma maneira de buscar apenas os campos obrigatórios de um documento de um banco de dados. Isso reduz a quantidade de dados que precisam ser transferidos do servidor de banco de dados para o cliente e, portanto, aumenta o desempenho.

Com Spring Data MongDB, as projeções podem ser usadas tanto com MongoTemplate e MongoRepository.

Antes de prosseguirmos, vamos ver o modelo de dados que usaremos:
@Document
public class User {
    @Id
    private String id;
    private String name;
    private Integer age;
    
    // standard getters and setters
}

2.1. Projeções usando MongoTemplate


O incluir() e excluir() métodos no Campo class é usado para incluir e excluir campos, respectivamente:
Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);

Esses métodos podem ser encadeados para incluir ou excluir vários campos. O campo marcado como @Id (_id no banco de dados) é sempre buscado, a menos que seja explicitamente excluído.

Os campos excluídos são nulos na instância da classe de modelo quando os registros são buscados com projeção. No caso em que os campos são de um tipo primitivo ou sua classe wrapper, o valor dos campos excluídos são os valores padrão dos tipos primitivos.

Por exemplo, String seria null , int /Inteiro seria 0 e booleano /Booleano seria falso .

Assim, no exemplo acima, o nome campo seria João , id seria null e idade seria 0.

2.2. Projeções usando MongoRepository


Ao usar o MongoRepositories, os campos de @Query anotação pode ser definida no formato JSON:
@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();

O resultado seria o mesmo que usar o MongoTemplate. O valor=”{” denota nenhum filtro e, portanto, todos os documentos serão buscados.

3. Agregação


A agregação no MongoDB foi construída para processar dados e retornar resultados computados. Os dados são processados ​​em estágios e a saída de um estágio é fornecida como entrada para o próximo estágio. Essa capacidade de aplicar transformações e fazer cálculos em dados em etapas torna a agregação uma ferramenta muito poderosa para análise.

Spring Data MongoDB fornece uma abstração para consultas de agregação nativa usando as três classes Agregação que envolve uma consulta de agregação, AggregationOperation que envolve estágios de pipeline individuais e AggregationResults que é o recipiente do resultado produzido pela agregação.

Para executar e agregar, primeiro, crie pipelines de agregação usando os métodos de construtor estáticos em Agregação class e crie uma instância de Agregação usando o newAggregation() método na Agregação class e, finalmente, execute a agregação 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);

Observe que tanto MatchOperation e Operação de Projeção implementar Operação de Agregação . Existem implementações semelhantes para outros pipelines de agregação. OutType é o modelo de dados para a saída esperada.

Agora, veremos alguns exemplos e suas explicações para cobrir os principais pipelines e operadores de agregação.

O conjunto de dados que usaremos neste artigo lista detalhes sobre todos os CEPs nos EUA que podem ser baixados do repositório MongoDB.

Vejamos um documento de amostra depois de importá-lo para uma coleção chamada zips no teste base de dados.
{
    "_id" : "01001",
    "city" : "AGAWAM",
    "loc" : [
        -72.622739,
        42.070206
    ],
    "pop" : 15338,
    "state" : "MA"
}

Por uma questão de simplicidade e para tornar o código conciso, nos próximos trechos de código, vamos supor que todos os static métodos de Agregação classe são importados estaticamente.

3.1. Obter todos os estados com uma população superior a 10 milhões de pedidos por população decrescente


Aqui teremos três pipelines:
  1. $grupo estágio resumindo a população de todos os códigos postais
  2. $match etapa para filtrar estados com população acima de 10 milhões
  3. $sort etapa para classificar todos os documentos em ordem decrescente de população

A saída esperada terá um campo _id como estado e um campo statePop com a população total do estado. Vamos criar um modelo de dados para isso e executar a agregação:
public class StatePoulation {
 
    @Id
    private String state;
    private Integer statePop;
 
    // standard getters and setters
}

O @Id anotação mapeará o _id campo de saída para estado no 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);

Os AgregaçãoResultados classe implementa Iterable e, portanto, podemos iterar sobre ele e imprimir os resultados.

Se o modelo de dados de saída não for conhecido, a classe padrão do MongoDB Document pode ser usado.

3.2. Obtenha o menor estado por população média da cidade


Para este problema, precisaremos de quatro etapas:
  1. $grupo para somar a população total de cada cidade
  2. $grupo para calcular a população média de cada estado
  3. $sort estágio para ordenar os estados pela população média da cidade em ordem crescente
  4. $limit para obter o primeiro estado com a menor população média da cidade

Embora não seja necessariamente obrigatório, usaremos um $project adicional estágio para reformatar o documento conforme StatePopulation modelo de dados.
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();

Neste exemplo, já sabemos que haverá apenas um documento no resultado, pois limitamos o número de documentos de saída a 1 no último estágio. Assim, podemos invocar getUniqueMappedResult() para obter a StatePopulation necessária instância.

Outra coisa a notar é que, em vez de confiar no @Id anotação para mapear _id para afirmar, fizemos isso explicitamente no estágio de projeção.

3.3. Obtenha o estado com códigos postais máximos e mínimos


Para este exemplo, precisamos de três etapas:
  1. $grupo para contar o número de CEPs de cada estado
  2. $sort para ordenar os estados pelo número de CEPs
  3. $grupo para encontrar o estado com códigos postais máximo e mínimo usando $first e $ú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();

Aqui não usamos nenhum modelo, mas usamos o Documento já fornecido com o driver MongoDB.