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");
List john = 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}")
List findNameAndExcludeId(); Результат будет таким же, как при использовании 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);
AggregationResults output
= 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);
AggregationResults result = 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);
AggregationResults result = 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);
AggregationResults result = mongoTemplate
.aggregate(aggregation, "zips", Document.class);
Document document= result.getUniqueMappedResult(); Здесь мы не использовали какую-либо модель, но использовали Документ , уже поставляемый с драйвером MongoDB.
4. Заключение
В этой статье мы узнали, как извлечь указанные поля документа в MongoDB, используя проекции в Spring Data MongoDB.
Мы также узнали о поддержке платформы агрегации MongoDB в Spring Data. Мы рассмотрели основные этапы агрегирования – группирование, проект, сортировка, ограничение и сопоставление, а также рассмотрели некоторые примеры его практического применения. Полный исходный код доступен на GitHub .