Spring Data MongoDB: Прогнози и агрегиране

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 () в класа Field се използват за включване и изключване на полета съответно:

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

Тези методи могат да бъдат свързани заедно, за да включват или изключват множество полета. Полето, маркирано като @Id ( _id в базата данни), винаги се извлича, освен ако не е изрично изключено.

Изключените полета са нула в екземпляра на класа на модела, когато записите се извличат с проекция. В случая, когато полетата са от примитивен тип или от техния клас обвивка, тогава стойността на изключените полета са стойности по подразбиране на примитивните типове.

Например, String ще бъде null , int / Integer ще бъде 0 и boolean / Boolean ще бъде false .

По този начин в горния пример полето за име ще бъде John , id ще бъде null и възрастта ще бъде 0.

2.2. Проекции с помощта на MongoRepository

Докато използвате MongoRepositories, полетата на анотацията @Query могат да бъдат дефинирани във формат JSON:

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

Резултатът ще бъде същият като използването на MongoTemplate. На стойност = "{}" означава не филтри и следователно всички документи ще бъдат извлечени.

3. Агрегация

Агрегирането в MongoDB е създадено за обработка на данни и връщане на изчислени резултати. Данните се обработват на етапи и изходът от един етап се предоставя като вход за следващия етап. Тази способност да се прилагат трансформации и да се правят изчисления на данни на етапи, прави агрегирането много мощен инструмент за анализ.

Spring Data MongoDB осигурява абстракция за собствени заявки за агрегиране, използвайки трите класа Aggregation, които обгръщат заявка за агрегиране, 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 output = mongoTemplate.aggregate(aggregation, "foobar", OutType.class);

Моля, имайте предвид, че MatchOperation и ProjectionOperation изпълняват AggregationOperation . Има подобни изпълнения и за други агрегиращи тръбопроводи. OutType е моделът на данни за очаквана продукция.

Сега ще разгледаме няколко примера и техните обяснения, за да обхванем основните агрегационни тръбопроводи и оператори.

Наборът от данни, който ще използваме в тази статия, съдържа подробности за всички пощенски кодове в САЩ, които могат да бъдат изтеглени от хранилището на MongoDB.

Нека да разгледаме примерен документ, след като го импортираме в колекция, наречена ципове в тестовата база данни.

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

За по-голяма простота и за да направим кода кратък, в следващите кодови фрагменти ще приемем, че всички статични методи от класа агрегиране са статично импортирани.

3.1. Вземете всички щати с население, по-голямо от 10 милиона подредени по население

Тук ще имаме три тръбопровода:

  1. $ групов етап, обобщаващ популацията на всички пощенски кодове
  2. $ етап на мач за филтриране на държави с население над 10 милиона
  3. $ етап на сортиране, за да сортирате всички документи в низходящ ред на популацията

Очакваният изход ще има поле _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 result = mongoTemplate.aggregate( aggregation, "zips", StatePopulation.class);

Класът AggregationResults изпълнява Iterable и следователно можем да го итерираме и да отпечатаме резултатите.

Ако моделът на изходните данни не е известен, може да се използва стандартният MongoDB клас Document .

3.2. Вземете най-малката държава по средно население на града

За този проблем ще са ни необходими четири етапа:

  1. $ група за сумиране на общото население на всеки град
  2. $ група за изчисляване на средното население на всяка държава
  3. $ етап на сортиране, за да подредите щатите по средното население на града във възходящ ред
  4. $ limit, за да получите първия щат с най-ниско средно население на града

Although it's not necessarily required, we will use an additional $project stage to reformat the document as per out StatePopulation data model.

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 result = mongoTemplate .aggregate(aggregation, "zips", StatePopulation.class); StatePopulation smallestState = result.getUniqueMappedResult();

In this example, we already know that there will be only one document in the result since we limit the number of output documents to 1 in the last stage. As such, we can invoke getUniqueMappedResult() to get the required StatePopulation instance.

Another thing to notice is that, instead of relying on the @Id annotation to map _id to state, we have explicitly done it in projection stage.

3.3. Get the State With Maximum and Minimum Zip Codes

For this example, we need three stages:

  1. $ group, за да преброи броя на пощенските кодове за всеки щат
  2. $ sort, за да подредите щатите по броя на пощенските кодове
  3. $ group за намиране на състоянието с max и min пощенски кодове, използвайки $ 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 result = mongoTemplate .aggregate(aggregation, "zips", Document.class); Document document= result.getUniqueMappedResult();

Тук не сме използвали нито един модел, но използваме документа, вече предоставен с драйвер за MongoDB.

4. Заключение

В тази статия научихме как да извлечем определени полета на документ в MongoDB, използвайки проекции в Spring Data MongoDB.

Също така научихме за поддръжката на MongoDB за агрегиране в Spring Data. Обхванахме основните фази на агрегиране - група, проект, сортиране, ограничаване и съвпадение и разгледахме някои примери за практическите му приложения. Пълният изходен код е достъпен в GitHub.