Перейти к основному содержимому

Агрегации MongoDB с использованием Java

· 6 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим структуру агрегации MongoDB с помощью Java - драйвера MongoDB .

Сначала мы рассмотрим, что агрегирование означает концептуально, а затем настроим набор данных. Наконец, мы увидим различные методы агрегирования в действии с помощью Aggregates builder .

2. Что такое агрегаты?

Агрегации используются в MongoDB для анализа данных и получения из них значимой информации .

Обычно они выполняются на разных этапах, и этапы образуют конвейер, так что выходные данные одного этапа передаются в качестве входных данных для следующего этапа.

Наиболее часто используемые этапы можно резюмировать следующим образом:

   | стадия    | Эквивалент SQL    | Описание   | 
| **проект** | ВЫБРАТЬ | выбирает только обязательные поля, также может использоваться для вычисления и добавления производных полей в коллекцию |
| **соответствие** | КУДА | фильтрует коллекцию по заданным критериям |
| **группа** | ГРУППА ПО | собирает входные данные вместе в соответствии с указанными критериями (например, количество, сумма), чтобы вернуть документ для каждой отдельной группы |
| **Сортировать** | СОРТИРОВАТЬ ПО | сортирует результаты в порядке возрастания или убывания заданного поля |
| **считать** | СЧИТАТЬ | подсчитывает документы, содержащиеся в коллекции |
| **ограничение** | ПРЕДЕЛ | ограничивает результат указанным количеством документов вместо возврата всей коллекции |
| **вне** | ВЫБЕРИТЕ В NEW_TABLE | записывает результат в именованную коллекцию; этот этап приемлем только как последний в конвейере |

Эквивалент SQL для каждого этапа агрегации приведен выше, чтобы дать нам представление о том, что означает указанная операция в мире SQL.

Вскоре мы рассмотрим примеры кода Java для всех этих этапов. Но перед этим нам нужна база данных.

3. Настройка базы данных

3.1. Набор данных

Первым и главным требованием для изучения всего, что связано с базами данных, является сам набор данных!

Для целей этого руководства мы будем использовать общедоступную конечную точку Restful API , которая предоставляет исчерпывающую информацию обо всех странах мира. Этот API дает нам множество точек данных по стране в удобном формате JSON . Вот некоторые из полей, которые мы будем использовать в нашем анализе:

  • name – название страны; например, Соединенные Штаты Америки
  • alpha3Code — шорткод для названия страны; например, IND (для Индии) ``
  • регион – регион, к которому относится страна; например, Европа
  • area – географическая область страны
  • languages – официальные языки страны в формате массива; например, английский
  • borders – массив alpha3Code соседних стран

Теперь давайте посмотрим, как преобразовать эти данные в коллекцию в базе данных MongoDB .

3.2. Импорт в MongoDB

Во- первых, нам нужно нажать на конечную точку API, чтобы получить все страны и сохранить ответ локально в файле JSON . Следующий шаг — импортировать его в MongoDB с помощью команды mongoimport :

mongoimport.exe --db <db_name> --collection <collection_name> --file <path_to_file> --jsonArray

Успешный импорт должен дать нам коллекцию из 250 документов.

4. Примеры агрегации в Java

Теперь, когда у нас есть основа, давайте перейдем к получению некоторых важных сведений из данных, которые у нас есть для всех стран . Для этой цели мы будем использовать несколько тестов JUnit.

Но прежде чем мы это сделаем, нам нужно установить соединение с базой данных:

@BeforeClass
public static void setUpDB() throws IOException {
mongoClient = MongoClients.create();
database = mongoClient.getDatabase(DATABASE);
collection = database.getCollection(COLLECTION);
}

Во всех следующих примерах мы будем использовать вспомогательный класс Aggregates , предоставляемый Java-драйвером MongoDB .

Для лучшей читабельности наших сниппетов мы можем добавить статический импорт:

import static com.mongodb.client.model.Aggregates.*;

4.1. совпадай и считай

Для начала, давайте начнем с чего-то простого. Ранее мы отмечали, что набор данных содержит информацию о языках.

Теперь предположим, что мы хотим проверить количество стран в мире, где английский язык является официальным языком :

@Test
public void givenCountryCollection_whenEnglishSpeakingCountriesCounted_thenNinetyOne() {
Document englishSpeakingCountries = collection.aggregate(Arrays.asList(
match(Filters.eq("languages.name", "English")),
count())).first();

assertEquals(91, englishSpeakingCountries.get("count"));
}

Здесь мы используем два этапа в нашем конвейере агрегации: match и count .

Во-первых, мы отфильтровываем коллекцию, чтобы соответствовать только тем документам, которые содержат английский язык в поле своих языков . Эти документы можно представить как временную или промежуточную коллекцию, которая становится входными данными для нашего следующего этапа, подсчета. Это подсчитывает количество документов на предыдущем этапе.

Еще один момент, на который следует обратить внимание в этом образце, — использование метода first . Поскольку мы знаем, что вывод последней стадии, count , будет одной записью, это гарантированный способ извлечь единственный результирующий документ.

4.2. группироватьсуммой ) и сортировать

В этом примере наша цель — найти географический регион, содержащий максимальное количество стран :

@Test
public void givenCountryCollection_whenCountedRegionWise_thenMaxInAfrica() {
Document maxCountriedRegion = collection.aggregate(Arrays.asList(
group("$region", Accumulators.sum("tally", 1)),
sort(Sorts.descending("tally")))).first();

assertTrue(maxCountriedRegion.containsValue("Africa"));
}

Как видно, здесь мы используем group и sort для достижения нашей цели .

Во-первых, мы собираем количество стран в каждом регионе, накапливая сумму их вхождений в подсчет переменных . Это дает нам промежуточный набор документов, каждый из которых содержит два поля: регион и количество стран в нем. Затем мы сортируем его по убыванию и извлекаем первый документ, чтобы получить регион с максимальным количеством стран.

4.3. сортировать, ограничивать и исключать

Теперь давайте воспользуемся sort , limit и out, чтобы выделить семь крупнейших стран по площади и записать их в новую коллекцию :

@Test
public void givenCountryCollection_whenAreaSortedDescending_thenSuccess() {
collection.aggregate(Arrays.asList(
sort(Sorts.descending("area")),
limit(7),
out("largest_seven"))).toCollection();

MongoCollection<Document> largestSeven = database.getCollection("largest_seven");

assertEquals(7, largestSeven.countDocuments());

Document usa = largestSeven.find(Filters.eq("alpha3Code", "USA")).first();

assertNotNull(usa);
}

Здесь мы сначала отсортировали данную коллекцию в порядке убывания площади. Затем мы использовали метод Aggregates#limit , чтобы ограничить результат только семью документами. Наконец, мы использовали выходную стадию для десериализации этих данных в новую коллекцию под названием « самый большой_семь» . Эту коллекцию теперь можно использовать так же, как и любую другую — например, чтобы узнать , есть ли в ней США.

4.4. проект, группа (с макс.), совпадение

В нашем последнем примере давайте попробуем что-нибудь посложнее. Скажем, нам нужно выяснить, сколько границ каждая страна разделяет с другими, и каково максимальное их количество .

Теперь в нашем наборе данных у нас есть поле границ , которое представляет собой массив, в котором перечислены alpha3Code для всех граничащих стран страны, но нет поля, непосредственно дающего нам количество. Итак, нам нужно получить количество граничащих стран, используя проект :

@Test
public void givenCountryCollection_whenNeighborsCalculated_thenMaxIsFifteenInChina() {
Bson borderingCountriesCollection = project(Projections.fields(Projections.excludeId(),
Projections.include("name"), Projections.computed("borderingCountries",
Projections.computed("$size", "$borders"))));

int maxValue = collection.aggregate(Arrays.asList(borderingCountriesCollection,
group(null, Accumulators.max("max", "$borderingCountries"))))
.first().getInteger("max");

assertEquals(15, maxValue);

Document maxNeighboredCountry = collection.aggregate(Arrays.asList(borderingCountriesCollection,
match(Filters.eq("borderingCountries", maxValue)))).first();

assertTrue(maxNeighboredCountry.containsValue("China"));
}

После этого, как мы видели ранее, мы сгруппируем спроецированную коллекцию, чтобы найти максимальное значение borderingCountries . Здесь следует отметить, что накопитель max дает нам максимальное значение в виде числа , а не весь документ , содержащий максимальное значение. Нам нужно выполнить сопоставление , чтобы отфильтровать нужный документ , если необходимо выполнить какие-либо дальнейшие операции.

5. Вывод

В этой статье мы увидели, что такое агрегации MongoDB, и как их применять в Java на примере набора данных .

Мы использовали четыре образца, чтобы проиллюстрировать различные этапы агрегации, чтобы сформировать базовое понимание концепции. Этот фреймворк предлагает множество возможностей для анализа данных, которые можно исследовать дальше .

Для дальнейшего чтения Spring Data MongoDB предоставляет альтернативный способ обработки проекций и агрегаций в Java.

Как всегда, исходный код доступен на GitHub .