MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Spring Data MongoDB:Projektionen und Aggregationen

1. Übersicht

Spring Data MongoDB bietet einfache High-Level-Abstraktionen für die native Abfragesprache von MongoDB. In diesem Artikel untersuchen wir die Unterstützung für das Projektions- und Aggregations-Framework.

Wenn Sie neu in diesem Thema sind, lesen Sie unseren Einführungsartikel Einführung in Spring Data MongoDB.

2. Projektion

In MongoDB sind Projektionen eine Möglichkeit, nur die erforderlichen Felder eines Dokuments aus einer Datenbank abzurufen. Dies reduziert die Datenmenge, die vom Datenbankserver zum Client übertragen werden muss, und erhöht somit die Leistung.

Mit Spring Data MongDB können Projektionen sowohl mit MongoTemplate verwendet werden und MongoRepository.

Bevor wir weitermachen, schauen wir uns das Datenmodell an, das wir verwenden werden:

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

2.1. Projektionen mit MongoTemplate

Das include() und ausschließen() Methoden auf dem Feld Klasse wird zum Einschließen bzw. Ausschließen von Feldern verwendet:

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

Diese Methoden können miteinander verkettet werden, um mehrere Felder ein- oder auszuschließen. Das als @Id gekennzeichnete Feld (_id in der Datenbank) wird immer abgerufen, sofern nicht explizit ausgeschlossen.

Ausgeschlossene Felder sind null in der Modellklasseninstanz, wenn Datensätze mit Projektion abgerufen werden. In dem Fall, in dem Felder von einem primitiven Typ oder ihrer Wrapper-Klasse sind, dann sind die Werte der ausgeschlossenen Felder Standardwerte der primitiven Typen.

Beispiel:String wäre null , int /Ganzzahl wäre 0 und boolesch /Boolean wäre falsch .

Also im obigen Beispiel der name Feld wäre John , Kennung wäre null und Alter wäre 0.

2.2. Projektionen mit MongoRepository

Bei der Verwendung von MongoRepositories werden die Felder von @Abfrage Anmerkung kann im JSON-Format definiert werden:

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

Das Ergebnis wäre dasselbe wie bei der Verwendung von MongoTemplate. Der value="{}" bedeutet keine Filter und daher werden alle Dokumente abgerufen.

3. Aggregation

Die Aggregation in MongoDB wurde entwickelt, um Daten zu verarbeiten und berechnete Ergebnisse zurückzugeben. Daten werden in Stufen verarbeitet und die Ausgabe einer Stufe wird als Eingabe für die nächste Stufe bereitgestellt. Diese Fähigkeit, Transformationen anzuwenden und Daten stufenweise zu berechnen, macht die Aggregation zu einem sehr leistungsfähigen Analysetool.

Spring Data MongoDB bietet eine Abstraktion für native Aggregationsabfragen mit den drei Klassen Aggregation die eine Aggregationsabfrage umschließt, AggregationOperation die einzelne Pipeline-Stufen und AggregationResults umschließt Dies ist der Container des durch die Aggregation erzeugten Ergebnisses.

Um eine Aggregation durchzuführen, erstellen Sie zunächst Aggregationspipelines mit den statischen Builder-Methoden in Aggregation Klasse, und erstellen Sie dann eine Instanz von Aggregation mit newAggregation() Methode auf der Aggregation Klasse und führen Sie schließlich die Aggregation mit MongoTemplate aus :

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

Bitte beachten Sie, dass sowohl MatchOperation und ProjectionOperation AggregationOperation implementieren . Es gibt ähnliche Implementierungen für andere Aggregationspipelines. OutType ist das Datenmodell für die erwartete Ausgabe.

Nun sehen wir uns einige Beispiele und ihre Erläuterungen an, um die wichtigsten Aggregationspipelines und -operatoren abzudecken.

Der Datensatz, den wir in diesem Artikel verwenden, listet Details zu allen Postleitzahlen in den USA auf, die aus dem MongoDB-Repository heruntergeladen werden können.

Sehen wir uns ein Beispieldokument an, nachdem es in eine Sammlung namens zips importiert wurde im Test Datenbank.

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

Der Einfachheit halber und um den Code kurz zu halten, gehen wir in den nächsten Codeausschnitten davon aus, dass alle statischen Methoden der Aggregation Klasse werden statisch importiert.

3.1. Holen Sie sich alle Staaten mit einer Bevölkerung von mehr als 10 Millionen, sortiert nach absteigender Bevölkerung

Hier haben wir drei Pipelines:

  1. $gruppe Stufe, die die Bevölkerung aller Postleitzahlen aufsummiert
  2. $match Stufe zum Herausfiltern von Bundesstaaten mit mehr als 10 Millionen Einwohnern
  3. $sortieren Schritt, um alle Dokumente in absteigender Reihenfolge der Bevölkerung zu sortieren

Die erwartete Ausgabe hat ein Feld _id als state und ein Feld statePop mit der gesamten Landesbevölkerung. Lassen Sie uns dafür ein Datenmodell erstellen und die Aggregation ausführen:

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

Die @Id Annotation ordnet die _id zu Feld von der Ausgabe zum Zustand im Modell:

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

Die Aggregationsergebnisse Klasse implementiert Iterable und daher können wir darüber iterieren und die Ergebnisse drucken.

Wenn das Ausgabedatenmodell nicht bekannt ist, die Standard-MongoDB-Klasse Document verwendet werden.

3.2. Erhalten Sie den kleinsten Staat nach der durchschnittlichen Stadtbevölkerung

Für dieses Problem benötigen wir vier Stufen:

  1. $gruppe um die Gesamtbevölkerung jeder Stadt zu summieren
  2. $gruppe um die durchschnittliche Bevölkerung jedes Staates zu berechnen
  3. $sortieren Stufe, um Staaten nach ihrer durchschnittlichen Stadtbevölkerung in aufsteigender Reihenfolge zu ordnen
  4. $limit den ersten Staat mit der niedrigsten durchschnittlichen Stadtbevölkerung zu bekommen

Obwohl es nicht unbedingt erforderlich ist, verwenden wir ein zusätzliches $project Phase, um das Dokument gemäß unserer StatePopulation neu zu formatieren Datenmodell.

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 diesem Beispiel wissen wir bereits, dass das Ergebnis nur ein Dokument enthalten wird, da wir im letzten Schritt die Anzahl der Ausgabedokumente auf 1 beschränken. Als solches können wir getUniqueMappedResult() aufrufen um die erforderliche StatePopulation zu erhalten Beispiel.

Eine weitere zu beachtende Sache ist, dass Sie sich nicht auf die @Id verlassen müssen Anmerkung zur Zuordnung _id um zu sagen, wir haben es explizit in der Projektionsphase gemacht.

3.3. Holen Sie sich den Staat mit maximalen und minimalen Postleitzahlen

Für dieses Beispiel benötigen wir drei Stufen:

  1. $gruppe um die Anzahl der Postleitzahlen für jeden Staat zu zählen
  2. $sortieren um die Staaten nach der Anzahl der Postleitzahlen zu ordnen
  3. $gruppe um den Bundesstaat mit maximalen und minimalen Postleitzahlen mit $first zu finden und $last Operatoren
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 haben wir kein Modell verwendet, sondern das Dokument verwendet bereits mit MongoDB-Treiber versehen.