Spring Data MongoDB: прогнозы и агрегации
1. обзор
Spring Data MongoDB предоставляет простые высокоуровневые абстракции для собственного языка запросов MongoDB. В этой статьеwe will explore the support for Projections and Aggregation framework.
Если вы новичок в этой теме, прочтите нашу вводную статьюIntroduction to 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 в базе данных), всегда выбирается, если явно не исключено.
Исключенные поля -null в экземпляре класса модели, когда записи выбираются с помощью проекции. В случае, когда поля имеют тип примитива или их класс-оболочку, тогда значения исключенных полей являются значениями по умолчанию для типов примитивов.
Например,String будетnull,int /Integer будет0 иboolean /Boolean будетfalseс.
Таким образом, в приведенном выше примере полеname будетJohn,id будетnull, аage будет0.
2.2. Прогнозы с использованиемMongoRepository
При использовании MongoRepositories аннотацияfields из@Query может быть определена в формате JSON:
@Query(value="{}", fields="{name : 1, _id : 0}")
List findNameAndExcludeId();
Результат будет таким же, как при использовании MongoTemplate. value=”\{}” означает отсутствие фильтров, и, следовательно, будут извлечены все документы.
3. Aggregationс
Агрегация в MongoDB была построена для обработки данных и возврата вычисленных результатов. Данные обрабатываются поэтапно, и выходные данные одного этапа предоставляются в качестве входных данных для следующего этапа. Эта способность применять преобразования и выполнять вычисления для данных поэтапно делает агрегирование очень мощным инструментом для аналитики.
Spring Data MongoDB предоставляет абстракцию для собственных запросов агрегирования с использованием трех классовAggregation, которые обертывают запрос агрегирования,AggregationOperation, который обертывает отдельные этапы конвейера, иAggregationResults, который является контейнером результата, полученного с помощью агрегация.
Для выполнения и агрегации сначала создайте конвейеры агрегации, используя методы статического построителя в классеAggregation, затем создайте экземплярAggregation, используя методnewAggregation() в классе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);
Обратите внимание, что какMatchOperation, так иProjectionOperation реализуютAggregationOperation. Есть аналогичные реализации для других конвейеров агрегации. OutType - это модель данных для ожидаемого результата.
Теперь мы рассмотрим несколько примеров и их пояснения, чтобы охватить основные агрегационные конвейеры и операторов.
Набор данных, который мы будем использовать в этой статье, содержит подробную информацию обо всех почтовых индексах в США, которые можно загрузить сMongoDB repository.
Давайте посмотрим на образец документа после его импорта в коллекцию под названиемzips в базе данныхtest.
{
"_id" : "01001",
"city" : "AGAWAM",
"loc" : [
-72.622739,
42.070206
],
"pop" : 15338,
"state" : "MA"
}
Для простоты и краткости кода в следующих фрагментах кода мы будем предполагать, что все методыstatic класса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 из вывода вstate в модели:
GroupOperation groupByStateAndSumPop = group("state")
.sum("pop").as("statePop");
MatchOperation filterStates = match(new Criteria("statePop").gt(10000000));
SortOperation sortByPopDesc = sort(new Sort(Direction.DESC, "statePop"));
Aggregation aggregation = newAggregation(
groupByStateAndSumPop, filterStates, sortByPopDesc);
AggregationResults result = mongoTemplate.aggregate(
aggregation, "zips", StatePopulation.class);
КлассAggregationResults реализуетIterable, и, следовательно, мы можем перебирать его и распечатывать результаты.
Если модель выходных данных неизвестна, можно использовать стандартный класс MongoDBDocument.
3.2. Получите наименьший штат по среднему населению города
Для этой задачи нам понадобятся четыре этапа:
-
$group для суммирования общей численности населения каждого города
-
$group для расчета средней численности населения каждого штата
-
$sort, чтобы упорядочить штаты по среднему населению города в возрастающем порядке
-
$limit, чтобы получить первый штат с самым низким средним населением города
Хотя это необязательно, мы будем использовать дополнительный этап$project, чтобы переформатировать документ в соответствии с моделью данных outStatePopulation.
GroupOperation sumTotalCityPop = group("state", "city")
.sum("pop").as("cityPop");
GroupOperation averageStatePop = group("_id.state")
.avg("cityPop").as("avgCityPop");
SortOperation sortByAvgPopAsc = sort(new Sort(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 на последнем этапе. Таким образом, мы можем вызватьgetUniqueMappedResult(), чтобы получить требуемый экземплярStatePopulation.
Следует также отметить, что вместо того, чтобы полагаться на аннотацию@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();
Здесь мы не использовали какую-либо модель, но использовалиDocument, уже предоставленные с драйвером MongoDB.
4. Заключение
В этой статье мы узнали, как извлечь определенные поля документа в MongoDB, используя проекции в Spring Data MongoDB.
Мы также узнали о поддержке инфраструктуры агрегации MongoDB в Spring Data. Мы рассмотрели основные этапы агрегации - группирование, проект, сортировка, ограничение и сопоставление и рассмотрели некоторые примеры его практического применения. Полный исходный кодavailable over on GitHub.