1. Прегледа
Spring Data MongoDB предоставя прости абстракции на високо ниво към роден език за заявки на MongoDB. В тази статия ще проучим поддръжката на рамката за прогнози и агрегиране.
Ако сте нов в тази тема, вижте нашата уводна статия Въведение в Spring Data MongoDB.
2. Прожекцията
В MongoDB проекциите са начин за извличане само на задължителните полета на документ от база данни. Това намалява количеството данни, които трябва да се прехвърлят от сървър на база данни към клиент и следователно повишава производителността.
С Spring Data MongDB проекциите могат да се използват както с MongoTemplate и MongoRepository.
Преди да продължим по-нататък, нека разгледаме модела на данни, който ще използваме:
@Document
public class User {
@Id
private String id;
private String name;
private Integer age;
// standard getters and setters
}
2.1. Проекции с помощта на MongoTemplate
include() и exclude() методи в Поле class се използва за включване и изключване на полета съответно:
Query query = new Query();
query.fields().include("name").exclude("id");
List<User> john = mongoTemplate.find(query, User.class);
Тези методи могат да бъдат свързани заедно, за да включат или изключат множество полета. Полето, отбелязано като @Id (_id в базата данни) винаги се извлича, освен ако не е изрично изключено.
Изключените полета са нулеви в моделния клас, когато записите се извличат с проекция. В случай, че полетата са от примитивен тип или техен клас обвивка, тогава стойността на изключените полета са стойности по подразбиране на примитивните типове.
Например String би било нулево , int /Цяло число ще бъде 0 и булеви /Булева би било невярно .
Така в горния пример име полето ще бъде Джон , идентификатор би било нулево и възраст ще бъде 0.
2.2. Проекции с помощта на MongoRepository
Докато използвате MongoRepositories, полето от @Query анотацията може да бъде дефинирана във формат JSON:
@Query(value="{}", fields="{name : 1, _id : 0}")
List<User> findNameAndExcludeId();
Резултатът ще бъде същият като използването на MongoTemplate. стойността=”{}” означава, че няма филтри и следователно всички документи ще бъдат извлечени.
3. Агрегация
Агрегацията в MongoDB е изградена за обработка на данни и връщане на изчислени резултати. Данните се обработват на етапи и изходът от един етап се предоставя като вход към следващия етап. Тази способност за прилагане на трансформации и извършване на изчисления върху данни на етапи прави агрегирането много мощен инструмент за анализ.
Spring Data MongoDB предоставя абстракция за собствени заявки за агрегиране, използвайки трите класа Агрегация която обвива заявка за агрегиране, AggregationOperation който обгръща отделните етапи на конвейера и AggregationResults който е контейнерът на резултата, произведен от агрегирането.
За да извършите и агрегирате, първо създайте тръбопроводи за агрегиране, като използвате методите на статичния конструктор на Агрегация клас, след което създайте екземпляр на Агрегация с помощта на newAggregation() метод на Агрегация клас и накрая стартирайте агрегирането с помощта на 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);
Моля, имайте предвид, че и двете MatchOperation и ProjectionOperation внедри AggregationOperation . Има подобни реализации за други конвейери за агрегиране. OutType е моделът на данни за очаквания изход.
Сега ще разгледаме няколко примера и техните обяснения, за да обхванем основните тръбопроводи за агрегация и оператори.
Наборът от данни, който ще използваме в тази статия, изброява подробности за всички пощенски кодове в САЩ, които могат да бъдат изтеглени от хранилището на MongoDB.
Нека разгледаме примерен документ, след като го импортираме в колекция, наречена zips в теста база данни.
{
"_id" : "01001",
"city" : "AGAWAM",
"loc" : [
-72.622739,
42.070206
],
"pop" : 15338,
"state" : "MA"
}
С цел простота и за да направим кода кратък, в следващите фрагменти от кода ще приемем, че всички статични методи за Агрегация клас са статично импортирани.
3.1. Вземете всички щати с население по-голямо от 10 милиона Подредете по низходящо население
Тук ще имаме три тръбопровода:
- $group етап сумиране на популацията на всички пощенски кодове
- $match етап за филтриране на държави с население над 10 милиона
- $сортиране етап за сортиране на всички документи в низходящ ред на населението
Очакваният изход ще има поле _id като състояние и поле statePop с цялото население на държавата. Нека създадем модел на данни за това и да изпълним агрегирането:
public class StatePoulation {
@Id
private String state;
private Integer statePop;
// standard getters and setters
}
@Id анотацията ще съпостави _id поле от изход към състояние в модела:
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);
Резултатите от агрегацията клас имплементира Iterable и следователно можем да го повторим и да отпечатаме резултатите.
Ако моделът на изходните данни не е известен, стандартният клас MongoDB Document може да се използва.
3.2. Вземете най-малкия щат по средно градско население
За този проблем ще ни трябват четири етапа:
- $group за сумиране на общото население на всеки град
- $group за изчисляване на средното население на всяка държава
- $сортиране етап на подреждане на щатите по средното им градско население във възходящ ред
- $limit да получите първия щат с най-ниско средно население на града
Въпреки че не е задължително, ще използваме допълнителен $project етап, за да преформатирате документа според изхода StatePopulation модел на данни.
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();
В този пример вече знаем, че ще има само един документ в резултата, тъй като ограничаваме броя на изходните документи до 1 в последния етап. Като такива можем да извикаме getUniqueMappedResult() за да получите необходимата Популация на състоянието пример.
Друго нещо, което трябва да забележите, е, че вместо да разчитате на @Id анотация към карта _id да заявя, ние изрично сме го направили на етап проекция.
3.3. Вземете щата с максимален и минимален пощенски коди
За този пример са ни необходими три етапа:
- $group за да преброите броя на пощенските кодове за всяка държава
- $сортиране да подредите щатите по броя на пощенските кодове
- $group за да намерите състоянието с максимални и мин. пощенски кодове, като използвате $first и $last оператори
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();
Тук не сме използвали никакъв модел, а използвахме Документа вече предоставен с драйвер за MongoDB.