1. Panoramica
Spring Data MongoDB fornisce semplici astrazioni di alto livello al linguaggio di query nativo MongoDB. In questo articolo, esploreremo il supporto per il framework Projections and Aggregation.
Se non conosci questo argomento, consulta il nostro articolo introduttivo Introduzione a Spring Data MongoDB.
2. Proiezione
In MongoDB, le proiezioni sono un modo per recuperare solo i campi richiesti di un documento da un database. Ciò riduce la quantità di dati che devono essere trasferiti dal server del database al client e quindi aumenta le prestazioni.
Con Spring Data MongDB, le proiezioni possono essere utilizzate sia con MongoTemplate e MongoRepository.
Prima di andare oltre, diamo un'occhiata al modello di dati che utilizzeremo:
@Document
public class User {
@Id
private String id;
private String name;
private Integer age;
// standard getters and setters
}
2.1. Proiezioni utilizzando MongoTemplate
include() e exclude() metodi sul Campo class viene utilizzata rispettivamente per includere ed escludere i campi:
Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);
Questi metodi possono essere concatenati per includere o escludere più campi. Il campo contrassegnato come @Id (_id nel database) viene sempre recuperato se non esplicitamente escluso.
I campi esclusi sono null nell'istanza della classe del modello quando i record vengono recuperati con la proiezione. Nel caso in cui i campi siano di un tipo primitivo o della loro classe wrapper, i valori dei campi esclusi sono valori predefiniti dei tipi primitivi.
Ad esempio, Stringa sarebbe nullo , int /Intero sarebbe 0 e booleano /Booleano sarebbe falso .
Quindi nell'esempio sopra, il nome il campo sarebbe Giovanni , id sarebbe nullo e età sarebbe 0.
2.2. Proiezioni utilizzando MongoRepository
Durante l'utilizzo di MongoRepositories, i campi di @Query l'annotazione può essere definita in formato JSON:
@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();
Il risultato sarebbe lo stesso dell'utilizzo di MongoTemplate. Il valore=”{}” non denota filtri e quindi tutti i documenti verranno recuperati.
3. Aggregazione
L'aggregazione in MongoDB è stata creata per elaborare i dati e restituire risultati calcolati. I dati vengono elaborati in più fasi e l'output di una fase viene fornito come input per la fase successiva. Questa capacità di applicare trasformazioni ed eseguire calcoli sui dati in più fasi rende l'aggregazione uno strumento molto potente per l'analisi.
Spring Data MongoDB fornisce un'astrazione per le query di aggregazione native utilizzando le tre classi Aggregation che racchiude una query di aggregazione, AggregationOperation che racchiude le singole fasi della pipeline e AggregationResults che è il contenitore del risultato prodotto dall'aggregazione.
Per eseguire e aggregare, in primo luogo, crea pipeline di aggregazione utilizzando i metodi del builder statico su Aggregazione class, quindi crea un'istanza di Aggregazione utilizzando newAggregation() metodo sull'Aggregazione classe e infine eseguire l'aggregazione utilizzando 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);
Tieni presente che entrambi MatchOperation e Operazione di proiezione implementare Operazione di aggregazione . Esistono implementazioni simili per altre pipeline di aggregazione. OutType è il modello di dati per l'output atteso.
Ora esamineremo alcuni esempi e le relative spiegazioni per coprire le principali pipeline e operatori di aggregazione.
Il set di dati che utilizzeremo in questo articolo elenca i dettagli su tutti i codici postali negli Stati Uniti che possono essere scaricati dal repository MongoDB.
Diamo un'occhiata a un documento di esempio dopo averlo importato in una raccolta chiamata zips nel test banca dati.
{
"_id" : "01001",
"city" : "AGAWAM",
"loc" : [
-72.622739,
42.070206
],
"pop" : 15338,
"state" : "MA"
}
Per semplicità e per rendere il codice conciso, nei prossimi frammenti di codice assumeremo che tutti gli elementi statici metodi di Aggregazione le classi vengono importate staticamente.
3.1. Ottieni tutti gli Stati con una popolazione superiore a 10 milioni di ordini per popolazione in diminuzione
Qui avremo tre pipeline:
- $gruppo fase di riepilogo della popolazione di tutti i codici postali
- $corrispondenza fase per filtrare gli stati con popolazione superiore a 10 milioni
- $ordina fase per ordinare tutti i documenti in ordine decrescente di popolazione
L'output previsto avrà un campo _id come stato e un campo statePop con la popolazione totale dello Stato. Creiamo un modello di dati per questo ed eseguiamo l'aggregazione:
public class StatePoulation {
@Id
private String state;
private Integer statePop;
// standard getters and setters
}
L'@ID l'annotazione mapperà il _id campo dall'output allo stato nel modello:
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);
I Risultati di aggregazione la classe implementa Iterabile e quindi possiamo scorrere su di esso e stampare i risultati.
Se il modello dei dati di output non è noto, la classe MongoDB standard Document può essere utilizzato.
3.2. Ottieni lo stato più piccolo in base alla popolazione media della città
Per questo problema, avremo bisogno di quattro fasi:
- $gruppo per sommare la popolazione totale di ciascuna città
- $gruppo per calcolare la popolazione media di ogni stato
- $ordina fase per ordinare gli stati in base alla popolazione media della città in ordine crescente
- $limite per ottenere il primo stato con la popolazione cittadina media più bassa
Sebbene non sia necessariamente necessario, utilizzeremo un ulteriore $project fase di riformattare il documento come da StatePopulation modello di dati.
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();
In questo esempio, sappiamo già che ci sarà un solo documento nel risultato poiché limitiamo il numero di documenti di output a 1 nell'ultima fase. Pertanto, possiamo invocare getUniqueMappedResult() per ottenere la StatePopulation richiesta esempio.
Un'altra cosa da notare è che, invece di fare affidamento su @Id annotazione per mappare _id per affermare, lo abbiamo fatto esplicitamente in fase di proiezione.
3.3. Ottieni lo stato con codici postali massimi e minimi
Per questo esempio, abbiamo bisogno di tre fasi:
- $gruppo per contare il numero di codici postali per ogni stato
- $ordina per ordinare gli stati in base al numero di codici postali
- $gruppo per trovare lo stato con codici postali massimi e minimi utilizzando $first e $ultimo operatori
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();
Qui non abbiamo utilizzato alcun modello ma abbiamo utilizzato il Documento già fornito con il driver MongoDB.