Объектные поля в сравнении с вложенными полями. Вложенные типы полей в Elasticsearch

При определении маппингов Elasticsearch конфигурирует поля, содержащие в себе массив объектов, как тип "объект". Во многих случаях это нормально, но иногда маппинги необходимо корректировать. Ниже мы рассмотрим различные сценарии и то, как выбрать правильный мапинг для каждого случая.

Поля объектов

Одним из преимуществ использования структур на основе документов является то, что их свойства могут быть сгруппированы в иерархической форме. Такие свойства мы называем объектами.

Объекты можно встраивать внутрь объектов и проникать в них настолько глубоко, насколько это необходимо.

При этом не имеет значения, насколько глубока связь "объект в объекте", поскольку Elasticsearch внутренне сгладит ее (см. пояснения ниже).

В качестве значений свойств можно создавать массивы объектов.

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

Тип вложенного поля

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

Проблема использования объектных полей

Чтобы продемонстрировать использование объектных полей в сравнении с вложенными типами полей, сначала проиндексируем несколько документов. Примеры можно выполнить в консоли Kibana.

Elasticsearch будет динамически генерировать эти маппинги:

Остановимся на полях "авторы" и "теги". Оба поля заданы как поля типа "объект". Это означает, что Elasticsearch сгладит их свойства. Документ 1 будет выглядеть следующим образом:

Как видно, поле "tags" выглядит как обычный строковый массив, а вот поле "authors" выглядит иначе - оно было разбито на множество полей-массивов.

Проблема заключается в том, что Elasticsearch не хранит свойства каждого объекта "authors" отдельно от свойств всех остальных объектов "authors".

Чтобы проиллюстрировать проблему с таким маппингом, рассмотрим два следующих запроса.

Запрос 1: Поиск книг с авторами из Чили или авторами в возрасте 30 лет и моложе.

Спойлер: Обе книги удовлетворяют этим условиям.

Чтобы найти книги, удовлетворяющие этим критериям, мы должны выполнить следующий запрос:

Возвращаются обе книги, что правильно, поскольку Густаво Ллермали родом из Чили, а Джону Доу меньше 30 лет.

Запрос 2: Книги, написанные авторами, которым 30 лет или меньше и которые являются выходцами из Чили.

Спойлер: Ни одна книга не соответствует критериям.

Этот запрос также вернет оба документа, что неверно. Мы знаем, что единственному автору из Чили 32 года, и поэтому он не соответствует всем необходимым критериям, но Elasticsearch не сохранил эту связь между авторами и возрастом.

Как решить эту проблему

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

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

Нам придется изменить тип маппинга. Для изменения существующих маппингов необходимо переиндексировать данные.

Сначала создадим пустой индекс, чтобы функция динамических маппингов Elasticsearch не генерировала маппинг для нашего поля authors:

*Все остальные маппинги будут сгенерированы Elasticsearch на основе проиндексированных документов.

Теперь воспользуйтесь API reindex для перемещения документов из старого индекса в новый:

Выполните эту процедуру, чтобы убедиться в правильности передачи документов:

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

Если мы попробуем снова ответить на эти вопросы с помощью вложенных запросов, то это будет выглядеть следующим образом:

Запрос 1: Ищем книги с авторами из Чили или авторами в возрасте 30 лет и моложе.

Обе книги продолжают появляться в результатах, что очень хорошо.

Запрос 2: Книги, написанные авторами в возрасте до 30 лет и родом из Чили.

Ни одна книга не возвращается, что является ожидаемым результатом.

Почему это важно

Использование типа вложенного поля для каждого поля массива объекта "на всякий случай" звучит заманчиво, но его следует использовать исключительно по мере необходимости. Под капотом Lucene создает новый документ для каждого объекта в массиве, и это может привести к снижению производительности или даже к взрыву маппинга.

Чтобы избежать низкой производительности, количество вложенных полей в одном индексе ограничено 50, а количество вложенных объектов в одном документе - 10000.

Оба параметра могут быть изменены, но делать это не рекомендуется:

index.mapping.nested_fields.limit

index.mapping.nested_objects.limit

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

Заключение

  • Поля, основанные на объектах или массивах объектов, по умолчанию создаются с типом object.
  • Объектный тип поля не поддерживает запрос связанных свойств внутри отдельных объектов.
  • Не используйте вложенный тип, если на один внешний объект будет приходиться только один внутренний объект.
  • В противном случае используйте поля вложенного типа, если необходимо запросить два или более полей в пределах одного внутреннего объекта, в противном случае используйте объектный тип.
  • Слишком большое количество вложенных объектов может привести к снижению производительности или взрыву маппинга.
  • Для маппинга всех ключевых полей внутреннего объекта в одно поле используйте тип поля flattened.
Понравилась статья? Поделиться с друзьями:
Добавить комментарий