Spring Data MongoDB: прогнозы и агрегации

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 миллионов в порядке убывания численности населения

Здесь у нас будет три конвейера:

  1. $group этап суммирования совокупности всех почтовых индексов

  2. $match этап для фильтрации штатов с населением более 10 миллионов

  3. $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. Получите наименьший штат по среднему населению города

Для этой задачи нам понадобятся четыре этапа:

  1. $group для суммирования общей численности населения каждого города

  2. $group для расчета средней численности населения каждого штата

  3. $sort, чтобы упорядочить штаты по среднему населению города в возрастающем порядке

  4. $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. Получите состояние с максимальным и минимальным почтовыми индексами

Для этого примера нам понадобятся три этапа:

  1. $group для подсчета количества почтовых индексов для каждого штата

  2. $sort, чтобы упорядочить состояния по количеству почтовых индексов

  3. $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.