1. Przegląd
Spring Data MongoDB zapewnia proste abstrakcje wysokiego poziomu do natywnego języka zapytań MongoDB. W tym artykule przyjrzymy się obsłudze ram prognoz i agregacji.
Jeśli nie znasz tego tematu, zapoznaj się z naszym artykułem wstępnym Wprowadzenie do Spring Data MongoDB.
2. Projekcja
W MongoDB Projekcje to sposób na pobranie z bazy danych tylko wymaganych pól dokumentu. Zmniejsza to ilość danych, które muszą być przesyłane z serwera bazy danych do klienta, a tym samym zwiększa wydajność.
Dzięki Spring Data MongDB projekcje mogą być używane zarówno z MongoTemplate i MongoRepository.
Zanim przejdziemy dalej, spójrzmy na model danych, którego będziemy używać:
@Document
public class User {
@Id
private String id;
private String name;
private Integer age;
// standard getters and setters
}
2.1. Projekcje przy użyciu MongoTemplate
include() i wyklucz() metody w Polu class służy do włączania i wyłączania pól odpowiednio:
Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);
Metody te można łączyć ze sobą, aby uwzględnić lub wykluczyć wiele pól. Pole oznaczone jako @Id (_id w bazie danych) jest zawsze pobierana, chyba że jest wyraźnie wykluczona.
Wykluczone pola są null w instancji klasy modelu, gdy rekordy są pobierane z projekcją. W przypadku, gdy pola są typu podstawowego lub ich klasy opakowującej, wartości wykluczonych pól są domyślnymi wartościami typów pierwotnych.
Na przykład Ciąg byłoby null , int /Liczba całkowita byłoby 0 i boolowskie /Boole'a byłoby fałszem .
Tak więc w powyższym przykładzie nazwa pole to Jan , identyfikator byłoby null i wiek byłoby 0.
2.2. Projekcje za pomocą MongoRepository
Podczas korzystania z MongoRepositories pola z @Zapytania adnotację można zdefiniować w formacie JSON:
@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();
Wynik byłby taki sam, jak przy użyciu MongoTemplate. wartość=”{}” oznacza brak filtrów i dlatego wszystkie dokumenty zostaną pobrane.
3. Agregacja
Agregacja w MongoDB została zbudowana w celu przetwarzania danych i zwracania obliczonych wyników. Dane są przetwarzane etapami, a dane wyjściowe z jednego etapu są dostarczane jako dane wejściowe do następnego etapu. Ta zdolność do stosowania transformacji i wykonywania obliczeń na danych etapami sprawia, że agregacja jest bardzo potężnym narzędziem analitycznym.
Spring Data MongoDB zapewnia abstrakcję dla natywnych zapytań agregujących przy użyciu trzech klas Agregacja która zawiera zapytanie agregujące, AggregationOperation która obejmuje poszczególne etapy potoku i AgregationResults który jest kontenerem wyniku utworzonego przez agregację.
Aby wykonać i agregować, najpierw utwórz potoki agregacji za pomocą statycznych metod konstruktora w Agregacja klasy, a następnie utwórz instancję Agregacji za pomocą nowej agregacji() metoda w Agregacji klasę i na koniec uruchom agregację za pomocą 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);
Należy pamiętać, że zarówno MatchOperation i ProjectionOperation wdrożyć Operację Aggregacji . Istnieją podobne implementacje dla innych potoków agregacji. Typ zewnętrzny jest modelem danych dla oczekiwanego wyniku.
Teraz przyjrzymy się kilku przykładom i ich wyjaśnieniom, aby objąć główne potoki agregacji i operatorów.
Zestaw danych, którego będziemy używać w tym artykule, zawiera szczegółowe informacje o wszystkich kodach pocztowych w USA, które można pobrać z repozytorium MongoDB.
Spójrzmy na przykładowy dokument po zaimportowaniu go do kolekcji o nazwie zips w teście baza danych.
{
"_id" : "01001",
"city" : "AGAWAM",
"loc" : [
-72.622739,
42.070206
],
"pop" : 15338,
"state" : "MA"
}
Dla uproszczenia i zwięzłości kodu, w następnych fragmentach kodu założymy, że wszystkie statyczne metody agregacji klasy są importowane statycznie.
3.1. Zdobądź wszystkie stany z liczbą ludności większą niż 10 milionów według malejącej liczby ludności
Tutaj będziemy mieć trzy potoki:
- grupa $ etap podsumowujący populację wszystkich kodów pocztowych
- $match etap do odfiltrowania stanów o populacji powyżej 10 milionów
- $sortuj etap, aby posortować wszystkie dokumenty w porządku malejącym według populacji
Oczekiwany wynik będzie miał pole _id jako stan i pole statePop z całkowitą populacją państwa. Stwórzmy dla tego model danych i uruchommy agregację:
public class StatePoulation {
@Id
private String state;
private Integer statePop;
// standard getters and setters
}
@Id adnotacja zmapuje _id pole od wyjścia do stanu w modelu:
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);
Wyniki agregacji klasa implementuje Iterable i stąd możemy iterować i drukować wyniki.
Jeśli wyjściowy model danych nie jest znany, standardowa klasa MongoDB Dokument może być używany.
3.2. Uzyskaj najmniejszy stan według średniej populacji miasta
Aby rozwiązać ten problem, będziemy potrzebować czterech etapów:
- grupa $ sumując całkowitą populację każdego miasta
- grupa $ obliczyć średnią populację każdego stanu
- $sortuj etap, aby uporządkować stany według średniej liczby ludności miasta w kolejności rosnącej
- $limit zdobyć pierwszy stan z najniższą średnią populacją miasta
Chociaż nie jest to koniecznie wymagane, użyjemy dodatkowego $projektu etap, aby ponownie sformatować dokument zgodnie z naszym StatePopulation model danych.
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();
W tym przykładzie wiemy już, że w wyniku będzie tylko jeden dokument, ponieważ w ostatnim etapie ograniczyliśmy liczbę dokumentów wyjściowych do 1. W związku z tym możemy wywołać getUniqueMappedResult() aby uzyskać wymaganą StatePopulation przykład.
Inną rzeczą, na którą warto zwrócić uwagę, jest to, że zamiast polegać na @Id adnotacja do mapy _id aby stwierdzić, wyraźnie zrobiliśmy to na etapie projekcji.
3.3. Uzyskaj stan z maksymalnym i minimalnym kodem pocztowym
W tym przykładzie potrzebujemy trzech etapów:
- grupa $ aby policzyć liczbę kodów pocztowych dla każdego stanu
- $sortuj uporządkować stany według liczby kodów pocztowych
- grupa $ aby znaleźć stan z maksymalnym i minimalnym kodem pocztowym za pomocą $first i $ostatnie operatorzy
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();
Tutaj nie użyliśmy żadnego modelu, ale użyliśmy Dokumentu już dostarczony ze sterownikiem MongoDB.