Elasticsearch - это база данных документов, тип базы данных No-SQL, в которой данные хранятся в виде документов. Под "документами" в Elasticsearch подразумеваются документы JSON, а поскольку данные хранятся в формате JSON, эта база данных позволяет легко сохранять иерархию и структуру сложных данных. Дополнительным преимуществом является то, что хранение данных в формате JSON позволяет пользователям содержать свойства с различными типами данных в одном документе.
В отличие от баз данных SQL, Elasticsearch не выполняет операции объединения, которые могут замедлить время выполнения запроса, а стремится к получению результатов в режиме реального времени с миллисекундным временем отклика на запрос.
Elasticsearch использует множество методов для определения отношений между документами, включая типы объектов, вложенные документы, отношения "родитель-ребенок" и денормализацию.
Иерархические объекты (объекты, содержащиеся в других объектах) создаются с помощью JSON. Elasticsearch демонстрирует уникальный тип данных, называемый объектным типом, который используется для представления иерархии объектов.
Использование типа поля объекта
Тип поля object позволяет пользователям использовать объект (с собственными полями и значениями) в качестве значения поля в документе.
Например, поле адреса события может быть объектом с собственными полями для региона, города, улицы и так далее. Если одно и то же событие происходит в нескольких городах, вы можете запрограммировать несколько адресов.
Тип объекта - это самый простой способ представления группы интересов и связанных с ней событий. Это позволяет пользователям задавать значения полей в виде одного объекта JSON или в виде нескольких объектов JSON.
Как использовать тип поля object
Документы JSON имеют иерархическую структуру и могут содержать внутренние объекты, которые сами могут содержать другие внутренние объекты, как в примере ниже:
1 2 3 4 5 6 7 8 9 10 11 | PUT books/_doc/1 { "title": "Machine Learning", "author": { "age": 30, "name": { "first": "Elie", "last": "John" } } } |
В этом примере индекс books содержит документы, каждый документ представляет собой книгу, а документы представлены в виде JSON-объектов, которые содержат те же свойства, что и книги. К ним относятся название и автор, где автор - это внутренний объект, содержащийся во внешнем объекте документа и имеющий два свойства, возраст и имя. Название - это внутренний объект, содержащийся в объекте author, который имеет два свойства, first и last, представляющие имя и фамилию автора книги.
Elasticsearch понимает поля и значения каждого объекта без знания его структуры.
Верхний документ внутренне индексируется как простой плоский список пар ключ-значение, подобный этому:
1 2 3 4 5 6 | { "title": "Machine Learning", "author.age": 30, "author.name.first": "Elie", "author.name.last": "John" } |
Явное отображение документа будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | PUT books { "mappings": { "properties": { "title": { "type": "text" }, "author": { "properties": { "age": { "type": "float" }, "name": { "properties": { "first": { "type": "text" }, "last": { "type": "text" } } } } } } } } |
Как показано выше, поле author представляет собой внутреннее объектное поле, а поле author.name - внутреннее объектное поле внутри этого поля. Поскольку объект является значением по умолчанию, нет необходимости явно присваивать объекту тип поля. См. ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | PUT books { "mappings": { "properties": { "title": { "type": "text" }, "author": { "type": "object", "properties": { "age": { "type": "float" }, "name": { "type": "object", "properties": { "first": { "type": "text" }, "last": { "type": "text" } } } } } } } } |
Пока корневой объект и внутренний объект имеют отношения "один к одному", сопоставление внутренних объектов, о котором говорилось выше, будет работать. Когда у книги только один "автор", это не проблема, однако если у книги, в данном случае "Machine Learning", два автора, а мы добавим еще одну книгу, "Artificial Intelligence", с одним автором, как показано ниже, это может вызвать проблемы:
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "title": "Machine Learning", "author": [ { "first_name": "John", "last_name": "Stefan" }, { "first_name": "Sandy", "last_name": "Naily" } ] } |
1 2 3 4 5 6 7 8 9 | { "title": "Artificial Intelligence", "author": [ { "first_name": "John", "last_name": "Naily" } ] } |
Когда пользователи ищут книгу, написанную "Джоном Нейли", используя этот запрос:
1 | "query: author.first_name=John AND author.last_name=Naily" |
они думают, что получат документ о книге "Искусственный интеллект". Однако когда пользователи действительно выполнят этот запрос, им будут возвращены оба документа. Поскольку Elasticsearch внутренне сплющивает внутренние объекты в один объект, запись о книге "Машинное обучение" будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 | { "title": "Machine Learning", "author.first_name": [ "John", "Sandy" ], "author.last_name": [ "Stefan", "Naily" ] } |
Это объясняет, почему он возвращается в качестве результата. Поскольку Elasticsearch построен на плоском фундаменте, документы внутри представлены уплощенными полями.
Поле объекта принимает следующие параметры:
- Dynamic: этот параметр определяет, нужно ли динамически добавлять дополнительные свойства к существующему объекту. Этот параметр может быть установлен в значения runtime, strict, true (значение по умолчанию) и false.
- Enabled: этот параметр определяет, должно ли JSON-значение поля объекта индексироваться и разбираться (по умолчанию true) или полностью игнорироваться (false).
- Subobjects: этот параметр определяет способность объекта содержать подобъекты (по умолчанию true) или нет (false). Если нет, то вложенные поля с точками в именах будут обрабатываться как листья, кроме того, имена полей будут расширены до соответствующей структуры объекта.
- Properties: этот параметр представляет собой поля внутри объекта, которые могут быть любого типа данных, даже объектного. Новые свойства могут быть добавлены к существующему объекту.
Тип поля объекта - преимущества и недостатки
Преимущества
- Они просты в использовании. В большинстве случаев пользователям не нужно делать ничего дополнительного для индексирования объектов, поскольку Elasticsearch автоматически обнаруживает их по умолчанию.
- На объектах пользователи могут выполнять запросы и агрегации так же, как и на плоских документах. Это происходит потому, что на уровне Lucene они являются плоскими документами.
- Никаких объединений не происходит.
Недостатки
- Отсутствие границ между объектами. Если пользователям требуется такая возможность, им следует рассмотреть альтернативные подходы, такие как вложенный, родительский и денормализованный, и в конечном итоге объединить их с объектами, если этого требует сценарий использования.
- При обновлении любого объекта весь документ будет переиндексирован.
Заключение
- Использование типа поля объекта - это просто, быстро и эффективно. Однако он применим только в тех случаях, когда поддерживаются отношения "один к одному".
- Elasticsearch не требует, чтобы тип данных поля был явно определен как объект в отображении. Когда он сталкивается с полями с иерархическими данными, он динамически устанавливает тип данных поля как объект.
- Придав всему полю путь с точечной нотацией, например author.name, пользователи могут выполнять поиск в поле объекта.
- Из-за того, что тип объекта индексируется, объекты отлично работают, когда пользователям нужно запросить только одно поле объекта за раз (обычно это отношения один-к-одному), но когда пользователям нужно запросить несколько полей (как это обычно бывает в отношениях один-ко-многим), они могут получить неожиданные результаты.
- Использование типа поля объекта ограничивает пользователей, сглаживая внутренние объекты вместо того, чтобы хранить их в виде отдельных документов. При этом теряется связь между объектами, индексируемыми из массива, что является недостатком. Однако для решения этой проблемы можно использовать тип вложенного поля.