1. Обзор
Spring Data MongoDB предоставляет простые абстракции высокого уровня для родного языка запросов MongoDB. В этой статье мы рассмотрим поддержку структуры прогнозов и агрегирования.
Если вы новичок в этой теме, обратитесь к нашей вводной статье Введение в Spring Data MongoDB .
2. Проекция
В MongoDB проекции-это способ извлечения только необходимых полей документа из базы данных. Это уменьшает объем данных, которые должны быть переданы с сервера базы данных на клиент, и, следовательно, повышает производительность.
С помощью Spring Data Mongodb проекции можно использовать как с 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"); Listjohn = mongoTemplate.find(query, User.class);
Эти методы могут быть объединены в цепочку, чтобы включать или исключать несколько полей. Поле, помеченное как @Id ( _id в базе данных), всегда извлекается, если явно не исключено.
Исключенные поля являются null в экземпляре класса модели, когда записи извлекаются с помощью проекции. В случае, когда поля имеют примитивный тип или их класс-оболочку, значения исключенных полей являются значениями по умолчанию примитивных типов.
Например, String будет null , int |/Integer будет 0 и boolean |/Boolean будет false .
Таким образом, в приведенном выше примере поле name будет John , id будет null и возраст будет 0.
2.2. Проекции С Использованием MongoRepository
При использовании репозиториев Mongo поля аннотации @Query могут быть определены в формате JSON:
@Query(value="{}", fields="{name : 1, _id : 0}") ListfindNameAndExcludeId();
Результат будет таким же, как при использовании MongoTemplate. Значение =” {}” означает отсутствие фильтров, и, следовательно, все документы будут извлечены.
3. Агрегация
Агрегация в MongoDB была построена для обработки данных и возврата вычисленных результатов. Данные обрабатываются поэтапно, и выходные данные одного этапа предоставляются в качестве входных данных для следующего этапа. Эта способность применять преобразования и выполнять вычисления с данными поэтапно делает агрегацию очень мощным инструментом для аналитики.
Spring Data MongoDB предоставляет абстракцию для собственных запросов агрегации с использованием трех классов Aggregation , которые обертывают запрос агрегации, AggregationOperation , который обертывает отдельные этапы конвейера, и AggregationResults , который является контейнером результата, полученного агрегацией.
Для выполнения и агрегации сначала создайте конвейеры агрегации с помощью методов статического построителя в классе Aggregation , затем создайте экземпляр Aggregation с помощью метода Newagregation() в классе Aggregation и, наконец, запустите агрегацию с помощью MongoTemplate :
MatchOperation matchStage = Aggregation.match(new Criteria("foo").is("bar")); ProjectionOperation projectStage = Aggregation.project("foo", "bar.baz"); Aggregation aggregation = Aggregation.newAggregation(matchStage, projectStage); AggregationResultsoutput = mongoTemplate.aggregate(aggregation, "foobar", OutType.class);
Обратите внимание, что как Операция сопоставления , так и Операция проекции реализуют Операцию агрегирования . Существуют аналогичные реализации для других конвейеров агрегации. Тип выхода – это модель данных для ожидаемого вывода.
Теперь мы рассмотрим несколько примеров и их объяснения, чтобы охватить основные конвейеры и операторы агрегации.
Набор данных, который мы будем использовать в этой статье, содержит подробную информацию обо всех почтовых кодах в США, которые можно загрузить из репозитория MongoDB .
Давайте рассмотрим пример документа после импорта его в коллекцию под названием zips в базе данных test .
{ "_id" : "01001", "city" : "AGAWAM", "loc" : [ -72.622739, 42.070206 ], "pop" : 15338, "state" : "MA" }
Для простоты и краткости кода в следующих фрагментах кода мы будем предполагать, что все статические методы класса Aggregation статически импортируются.
3.1. Получить Все Штаты С Населением Более 10 Миллионов В Порядке Убывания Численности Населения
Здесь у нас будет три трубопровода:
- $group этап подведения итогов по населению всех почтовых индексов
- $match этап фильтрации штатов с населением более 10 миллионов человек
- $sort этап сортировки всех документов в порядке убывания численности населения
Ожидаемый результат будет иметь поле _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); AggregationResultsresult = mongoTemplate.aggregate( aggregation, "zips", StatePopulation.class);
Класс Aggregation Results реализует Iterable , и, следовательно, мы можем перебирать его и печатать результаты.
Если модель выходных данных неизвестна, можно использовать стандартный класс MongoDB Document .
3.2. Получить наименьшее государство по среднему населению города
Для решения этой задачи нам понадобится четыре этапа:
- $group для суммирования общей численности населения каждого города
- $group для расчета средней численности населения каждого штата
- $sort stage для упорядочения штатов по их среднему городскому населению в порядке возрастания
- $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); AggregationResultsresult = mongoTemplate .aggregate(aggregation, "zips", StatePopulation.class); StatePopulation smallestState = result.getUniqueMappedResult();
В этом примере мы уже знаем, что в результате будет только один документ, так как на последнем этапе мы ограничиваем количество выходных документов 1. Таким образом, мы можем вызвать get Unique Mapped Result () , чтобы получить требуемый экземпляр State Population .
Еще одна вещь, на которую следует обратить внимание, заключается в том, что вместо того, чтобы полагаться на аннотацию @Id для отображения _id в состояние, мы явно сделали это на этапе проекции.
3.3. Получить штат С Максимальным и Минимальным Почтовыми кодами
Для этого примера нам нужно три этапа:
- $group для подсчета количества почтовых индексов для каждого штата
- $sort упорядочить штаты по количеству почтовых индексов
- $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); AggregationResultsresult = mongoTemplate .aggregate(aggregation, "zips", Document.class); Document document= result.getUniqueMappedResult();
Здесь мы не использовали какую-либо модель, но использовали Документ , уже поставляемый с драйвером MongoDB.
4. Заключение
В этой статье мы узнали, как извлечь указанные поля документа в MongoDB, используя проекции в Spring Data MongoDB.
Мы также узнали о поддержке платформы агрегации MongoDB в Spring Data. Мы рассмотрели основные этапы агрегирования – группирование, проект, сортировка, ограничение и сопоставление, а также рассмотрели некоторые примеры его практического применения. Полный исходный код доступен на GitHub .