sql >> Database teknologi >  >> NoSQL >> MongoDB

Spring Data MongoDB:Fremskrivninger og aggregationer

1. Oversigt

Spring Data MongoDB leverer enkle abstraktioner på højt niveau til MongoDB's oprindelige forespørgselssprog. I denne artikel vil vi udforske støtten til Projections and Aggregation framework.

Hvis du er ny inden for dette emne, kan du se vores indledende artikel Introduktion til Spring Data MongoDB.

2. Projektion

I MongoDB er Projections en måde at hente kun de nødvendige felter i et dokument fra en database. Dette reducerer mængden af ​​data, der skal overføres fra databaseserver til klient og øger dermed ydeevnen.

Med Spring Data MongDB kan projektioner bruges både med MongoTemplate og MongoRepository.

Inden vi går videre, lad os se på den datamodel, vi vil bruge:

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

2.1. Projektioner ved hjælp af MongoTemplate

include() og exclude() metoder på feltet klasse bruges til at inkludere og ekskludere felter henholdsvis:

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

Disse metoder kan kædes sammen for at inkludere eller udelukke flere felter. Feltet markeret som @Id (_id i databasen) hentes altid, medmindre det er eksplicit udelukket.

Udelukkede felter er null i modelklasseforekomsten, når poster hentes med projektion. I det tilfælde, hvor felter er af en primitiv type eller deres indpakningsklasse, er værdien af ​​ekskluderede felter standardværdier for de primitive typer.

For eksempel String ville være nul , int /Heltal ville være 0 og boolesk /Boolesk ville være falsk .

I ovenstående eksempel er navnet således feltet ville være John , id ville være nul og alder ville være 0.

2.2. Projektioner ved hjælp af MongoRepository

Mens du bruger MongoRepositories, er felterne af @Query annotering kan defineres i JSON-format:

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

Resultatet ville være det samme som at bruge MongoTemplate. værdien="{}" angiver ingen filtre, og derfor vil alle dokumenter blive hentet.

3. Aggregation

Aggregation i MongoDB blev bygget til at behandle data og returnere beregnede resultater. Data behandles i trin, og output fra et trin leveres som input til det næste trin. Denne evne til at anvende transformationer og foretage beregninger på data i trin gør aggregering til et meget effektivt værktøj til analyse.

Spring Data MongoDB giver en abstraktion til native aggregeringsforespørgsler ved hjælp af de tre klasser Aggregation som ombryder en aggregeringsforespørgsel, AggregationOperation som omslutter individuelle pipeline-stadier og AggregationResults som er beholderen for resultatet produceret ved aggregering.

For at udføre og aggregere skal du først oprette aggregeringspipelines ved hjælp af de statiske builder-metoder på Aggregation klasse, og opret derefter en forekomst af Aggregation ved hjælp af newAggregation() metode på Aggregation klasse og til sidst køre aggregeringen ved hjælp af 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);

Bemærk venligst, at både MatchOperation og ProjectionOperation implementer AggregationOperation . Der er lignende implementeringer for andre aggregeringspipelines. OutType er datamodellen for forventet output.

Nu vil vi se på et par eksempler og deres forklaringer for at dække de store aggregeringsrørledninger og operatører.

Datasættet, som vi vil bruge i denne artikel, viser detaljer om alle postnumre i USA, som kan downloades fra MongoDB-lageret.

Lad os se på et eksempeldokument efter at have importeret det til en samling kaldet zips i testen database.

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

For enkelhedens skyld og for at gøre kode kortfattet, vil vi i de næste kodestykker antage, at alle de statiske metoder til Aggregation klasse er statisk importeret.

3.1. Få alle stater med en befolkning på mere end 10 millioner, sorteret efter befolkning faldende

Her vil vi have tre pipelines:

  1. $gruppe fase, der opsummerer populationen af ​​alle postnumre
  2. $match fase for at bortfiltrere stater med en befolkning på over 10 millioner
  3. $sort trin for at sortere alle dokumenter i faldende rækkefølge efter population

Det forventede output vil have et felt _id som tilstand og et felt statePop med den samlede statsbefolkning. Lad os oprette en datamodel for dette og køre aggregeringen:

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

@Id annotering vil kortlægge _id felt fra output til tilstand 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);

Aggregationsresultaterne klasse implementerer Iterable og derfor kan vi iterere over det og udskrive resultaterne.

Hvis outputdatamodellen ikke er kendt, er standard MongoDB-klassen Dokument kan bruges.

3.2. Få den mindste stat efter gennemsnitlig bybefolkning

Til dette problem har vi brug for fire trin:

  1. $gruppe for at summere den samlede befolkning i hver by
  2. $gruppe at beregne den gennemsnitlige befolkning i hver stat
  3. $sort fase for at sortere stater efter deres gennemsnitlige bybefolkning i stigende rækkefølge
  4. $limit for at få den første stat med den laveste gennemsnitlige bybefolkning

Selvom det ikke nødvendigvis er påkrævet, vil vi bruge et ekstra $project trin for at omformatere dokumentet i henhold til StatePopulation datamodel.

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 dette eksempel ved vi allerede, at der kun vil være ét dokument i resultatet, da vi begrænser antallet af outputdokumenter til 1 i sidste fase. Som sådan kan vi kalde getUniqueMappedResult() for at få den nødvendige StatePopulation eksempel.

En anden ting at bemærke er, at i stedet for at stole på @Id annotation til kort _id at sige, vi har eksplicit gjort det i projektionsstadiet.

3.3. Få staten med maksimum og minimum postnumre

Til dette eksempel har vi brug for tre trin:

  1. $gruppe at tælle antallet af postnumre for hver stat
  2. $sort at sortere staterne efter antallet af postnumre
  3. $gruppe for at finde staten med maks. og min. postnumre ved hjælp af $first og $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();

Her har vi ikke brugt nogen model, men brugt Dokumentet allerede leveret med MongoDB-driver.


  1. Hent data fra samling b ikke i samling a i en MongoDB shell-forespørgsel

  2. Mongodb indstilling unikt felt

  3. Spring Data MongoDB:Fremskrivninger og aggregationer

  4. Redis cluster failover:slave bliver ikke master