sql >> Database >  >> NoSQL >> MongoDB

Spring Data MongoDB:projecties en aggregaties

1. Overzicht

Spring Data MongoDB biedt eenvoudige abstracties op hoog niveau voor MongoDB native query-taal. In dit artikel zullen we de ondersteuning voor het raamwerk voor projecties en aggregatie onderzoeken.

Als dit onderwerp nieuw voor u is, raadpleeg dan ons inleidende artikel Inleiding tot Spring Data MongoDB.

2. Projectie

In MongoDB zijn projecties een manier om alleen de vereiste velden van een document uit een database op te halen. Dit vermindert de hoeveelheid gegevens die van de databaseserver naar de client moet worden overgebracht en verhoogt zo de prestaties.

Met Spring Data MongDB kunnen projecties zowel met MongoTemplate . worden gebruikt en MongoRepository.

Laten we, voordat we verder gaan, eens kijken naar het datamodel dat we gaan gebruiken:

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

2.1. Projecties met behulp van MongoTemplate

De include() en exclude() methoden op het Veld class wordt gebruikt om respectievelijk velden op te nemen en uit te sluiten:

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

Deze methoden kunnen aan elkaar worden gekoppeld om meerdere velden op te nemen of uit te sluiten. Het veld gemarkeerd als @Id (_id in de database) wordt altijd opgehaald, tenzij expliciet uitgesloten.

Uitgesloten velden zijn null in de modelklasse-instantie wanneer records worden opgehaald met projectie. In het geval dat velden van een primitief type of hun wrapper-klasse zijn, dan zijn de waarde van uitgesloten velden de standaardwaarden van de primitieve typen.

Bijvoorbeeld String zou null zijn , int /Geheel getal zou 0 . zijn en booleaans /Booleaans zou onwaar zijn .

Dus in het bovenstaande voorbeeld is de naam veld zou zijn John , id zou null zijn en leeftijd zou 0 zijn.

2.2. Projecties met behulp van MongoRepository

Tijdens het gebruik van MongoRepositories, de velden van @Query annotatie kan worden gedefinieerd in JSON-formaat:

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

Het resultaat zou hetzelfde zijn als het gebruik van de MongoTemplate. De waarde="{}" geeft geen filters aan en daarom worden alle documenten opgehaald.

3. Aggregatie

Aggregatie in MongoDB is gebouwd om gegevens te verwerken en berekende resultaten te retourneren. Gegevens worden in fasen verwerkt en de output van de ene fase wordt geleverd als invoer voor de volgende fase. Deze mogelijkheid om transformaties toe te passen en berekeningen op gegevens in fasen uit te voeren, maakt aggregatie tot een zeer krachtig hulpmiddel voor analyses.

Spring Data MongoDB biedt een abstractie voor native aggregatiequery's met behulp van de drie klassen Aggregation die een aggregatiequery omhult, AggregationOperation die individuele pijplijnfasen omvat en AggregationResults dat is de container van het resultaat geproduceerd door aggregatie.

Om aggregatie uit te voeren en te aggregeren, maakt u eerst aggregatiepipelines met behulp van de statische buildermethoden op Aggregation class en maak vervolgens een instantie van Aggregation met behulp van de newAggregation() methode op de Aggregatie class en voer ten slotte de aggregatie uit met behulp van 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);

Houd er rekening mee dat zowel MatchOperation en ProjectionOperation implementeer AggregationOperation . Er zijn vergelijkbare implementaties voor andere aggregatiepijplijnen. OutType is het datamodel voor de verwachte output.

Nu zullen we een paar voorbeelden en hun uitleg bekijken om de belangrijkste aggregatiepijplijnen en -operators te dekken.

De dataset die we in dit artikel zullen gebruiken, bevat details over alle postcodes in de VS die kunnen worden gedownload van de MongoDB-repository.

Laten we een voorbeelddocument bekijken nadat we het in een verzameling met de naam zips hebben geïmporteerd in de test database.

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

Voor de eenvoud en om de code beknopt te houden, gaan we er in de volgende codefragmenten van uit dat alle statische methoden van Aggregatie klasse worden statisch geïmporteerd.

3.1. Krijg alle staten met een bevolking van meer dan 10 miljoen Volgorde van aflopende bevolking

Hier hebben we drie pijplijnen:

  1. $group fase waarin de populatie van alle postcodes wordt samengevat
  2. $match stadium om staten met een bevolking van meer dan 10 miljoen eruit te filteren
  3. $sort fase om alle documenten in aflopende volgorde van populatie te sorteren

De verwachte output heeft een veld _id als staat en een veld statePop met de totale staatsbevolking. Laten we hiervoor een gegevensmodel maken en de aggregatie uitvoeren:

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

De @Id annotatie zal de _id . toewijzen veld van uitvoer naar status in het model:

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);

De Aggregatieresultaten class implementeert Iterable en daarom kunnen we het herhalen en de resultaten afdrukken.

Als het uitvoergegevensmodel niet bekend is, wordt de standaard MongoDB-klasse Document kan worden gebruikt.

3.2. Krijg de kleinste staat op basis van de gemiddelde stadsbevolking

Voor dit probleem hebben we vier fasen nodig:

  1. $group om de totale bevolking van elke stad op te tellen
  2. $group om de gemiddelde bevolking van elke staat te berekenen
  3. $sort fase om staten te ordenen op hun gemiddelde stadsbevolking in oplopende volgorde
  4. $limit om de eerste staat te krijgen met de laagste gemiddelde stadsbevolking

Hoewel het niet per se vereist is, gebruiken we een extra $project stap om het document opnieuw op te maken volgens StatePopulation gegevensmodel.

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 dit voorbeeld weten we al dat er slechts één document in het resultaat zal zijn, aangezien we het aantal uitvoerdocumenten beperken tot 1 in de laatste fase. Als zodanig kunnen we getUniqueMappedResult() . aanroepen om de vereiste StatePopulation . te krijgen instantie.

Een ander ding om op te merken is dat, in plaats van te vertrouwen op de @Id annotatie op kaart _id om te zeggen, we hebben het expliciet gedaan in de projectiefase.

3.3. Krijg de staat met maximale en minimale postcodes

Voor dit voorbeeld hebben we drie fasen nodig:

  1. $group om het aantal postcodes voor elke staat te tellen
  2. $sort om de staten te bestellen op het aantal postcodes
  3. $group om de staat met max en min postcodes te vinden met $first en $last operators
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();

Hier hebben we geen enkel model gebruikt, maar het Document al voorzien van MongoDB-stuurprogramma.


  1. redis dump.rdb / kleine bestanden opslaan

  2. Opvragen van ingebedde objecten in Mongoid/rails 3 (lager dan, min-operators en sorteren)

  3. MongoDB:Is het mogelijk om een ​​hoofdletterongevoelige query te maken?

  4. Hoe verwijder je in mongoDb een array-element door zijn index?