1. Översikt
Spring Data MongoDB tillhandahåller enkla abstraktioner på hög nivå till MongoDBs inhemska frågespråk. I den här artikeln undersöker vi stödet för ramverket för projektioner och aggregering.
Om du är ny på det här ämnet, se vår inledande artikel Introduction to Spring Data MongoDB.
2. Projektion
I MongoDB är Projections ett sätt att bara hämta de obligatoriska fälten i ett dokument från en databas. Detta minskar mängden data som måste överföras från databasserver till klient och ökar därmed prestandan.
Med Spring Data MongDB kan projektioner användas både med MongoTemplate och MongoRepository.
Innan vi går vidare, låt oss titta på datamodellen vi kommer att använda:
@Document
public class User {
@Id
private String id;
private String name;
private Integer age;
// standard getters and setters
}
2.1. Projektioner med MongoTemplate
include() och exclude() metoder på fältet klass används för att inkludera respektive exkludera fält:
Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);
Dessa metoder kan kopplas samman för att inkludera eller exkludera flera fält. Fältet markerat som @Id (_id i databasen) hämtas alltid om det inte uttryckligen exkluderats.
Undantagna fält är null i modellklassinstansen när poster hämtas med projektion. I de fall där fält är av en primitiv typ eller deras omslagsklass, är värdet på uteslutna fält standardvärden för de primitiva typerna.
Till exempel Sträng skulle vara null , int /Heltal skulle vara 0 och boolesk /Boolesk skulle vara falskt .
I exemplet ovan är alltså namnet fältet skulle vara John , id skulle vara null och ålder skulle vara 0.
2.2. Projektioner med MongoRepository
När du använder MongoRepositories, fälten av @Query anteckning kan definieras i JSON-format:
@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();
Resultatet skulle bli detsamma som att använda MongoTemplate. värde="{}" anger inga filter och därför kommer alla dokument att hämtas.
3. Aggregation
Aggregation i MongoDB byggdes för att bearbeta data och returnera beräknade resultat. Data bearbetas i steg och utdata från ett steg tillhandahålls som input till nästa steg. Denna förmåga att tillämpa transformationer och göra beräkningar på data i steg gör aggregering till ett mycket kraftfullt verktyg för analys.
Spring Data MongoDB tillhandahåller en abstraktion för inbyggda aggregeringsfrågor med de tre klasserna Aggregation som omsluter en aggregeringsfråga, AggregationOperation som omsluter enskilda pipelinesteg och AggregationResults som är behållaren för resultatet som produceras genom aggregering.
För att utföra och aggregera, skapa först aggregeringspipelines med de statiska byggmetoderna på Aggregation klass och skapa sedan en instans av Aggregation med hjälp av newAggregation() metod på Aggregation klass och kör slutligen aggregeringen med 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);
Observera att både MatchOperation och ProjectionOperation implementera AggregationOperation . Det finns liknande implementeringar för andra aggregeringspipelines. OutType är datamodellen för förväntad produktion.
Nu kommer vi att titta på några exempel och deras förklaringar för att täcka de stora aggregeringspipelines och operatörer.
Datauppsättningen som vi kommer att använda i den här artikeln listar detaljer om alla postnummer i USA som kan laddas ner från MongoDB-förrådet.
Låt oss titta på ett exempeldokument efter att ha importerat det till en samling som heter zips i testet databas.
{
"_id" : "01001",
"city" : "AGAWAM",
"loc" : [
-72.622739,
42.070206
],
"pop" : 15338,
"state" : "MA"
}
För enkelhetens skull och för att göra koden kortfattad kommer vi i nästa kodavsnitt att anta att alla statiska metoder för Aggregation klass importeras statiskt.
3.1. Få alla stater med en befolkning som är större än 10 miljoner i ordning efter befolkning minskande
Här kommer vi att ha tre pipelines:
- $grupp steg som summerar populationen av alla postnummer
- $match steg för att filtrera bort stater med en befolkning på över 10 miljoner
- $sort steg för att sortera alla dokument i fallande befolkningsordning
Den förväntade utdata kommer att ha ett fält _id som tillstånd och ett fält statePop med den totala statens befolkning. Låt oss skapa en datamodell för detta och köra aggregeringen:
public class StatePoulation {
@Id
private String state;
private Integer statePop;
// standard getters and setters
}
@Id anteckning kommer att mappa _id fält från utdata till tillstånd i modellen:
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);
Aggregationsresultaten klass implementerar Iterable och därför kan vi iterera över det och skriva ut resultaten.
Om utdatamodellen inte är känd, standard MongoDB-klassen Document kan användas.
3.2. Få minsta delstat efter genomsnittlig stadsbefolkning
För detta problem behöver vi fyra steg:
- $grupp för att summera den totala befolkningen i varje stad
- $grupp för att beräkna genomsnittlig befolkning i varje stat
- $sort steg för att ordna tillstånd efter deras genomsnittliga stadsbefolkning i stigande ordning
- $limit för att få den första delstaten med lägst genomsnittlig stadsbefolkning
Även om det inte nödvändigtvis är nödvändigt, kommer vi att använda ytterligare ett $project steg för att formatera om dokumentet enligt StatePopulation datamodell.
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();
I det här exemplet vet vi redan att det bara kommer att finnas ett dokument i resultatet eftersom vi begränsar antalet utdatadokument till 1 i det sista steget. Som sådan kan vi anropa getUniqueMappedResult() för att få den nödvändiga StatePopulation instans.
En annan sak att lägga märke till är att istället för att lita på @Id anteckning till kartan _id att säga, vi har uttryckligen gjort det i projektionsstadiet.
3.3. Få staten med högsta och minsta postnummer
För det här exemplet behöver vi tre steg:
- $grupp för att räkna antalet postnummer för varje delstat
- $sort för att beställa delstaterna efter antalet postnummer
- $grupp för att hitta delstaten med max och min postnummer med $first och $last operatörer
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();
Här har vi inte använt någon modell utan använt Dokumentet redan försedd med MongoDB-drivrutin.