Привет, меня зовут Евгений Думчев, я разработчик в DD Planet. Сегодня хочу поделиться опытом применения одной из редко используемых, но полезных функций Elasticsearch, которую мы успешно применили в одном из проектов.
Elasticsearch - это высокопроизводительная, масштабируемая, распределенная поисковая и аналитическая система, предназначенная для быстрого поиска, анализа и обработки больших объемов данных.
Основной и наиболее распространенной задачей применения Elasticsearch на проектах является внедрение полнотекстового поиска (поиск по текстовым полям с учетом морфологии, синонимов и релевантности) и точного поиска с применением фильтраций и агрегаций данных. В данном случае разработчик целенаправленно подключает Elasticsearch в приложение для того, чтобы воспользоваться средствами индексации и гибкого поиска данных. Для этого требуется создать индекс с маппингом типов данных и реализовать утилиту-индексатор, которая будет на основе данных из постоянного хранилища данных заполнять индекс документами (индексировать) в соответствии со структурой маппинга. Также в основном API приложении потребуется реализовать запрос к Elasticsearch (существуют библиотеки для большинства языков программирования) для поиска и получения данных из целевого индекса. Архитектурная схема выглядит так:

И еще один, не менее популярный вариант использования Elasticsearch на проектах - в качестве инструмента для централизованного хранения и анализа логов приложений, баз данных и других инфраструктурных элементов. В связке с Logstash (для сбора и обработки логов) и Kibana (для визуализации) он образует стек ELK, который позволяет эффективно искать и анализировать большие объемы данных. Elasticsearch обеспечивает быстрый поиск по логам, агрегацию данных и настройку алертинга для обнаружения аномалий. Это делает его популярным решением для мониторинга инфраструктуры, диагностики проблем и аудита безопасности. Но данное решение не отражает весь масштаб и спектр возможностей, предоставляемых Elasticsearch, поскольку пользователь взаимодействует с поисковым движком только через визуальный интерфейс и функции, доступные в Kibana. Пример интерфейса Kibana:

Более сложные и специализированные возможности Elasticsearch, требующие дополнительной настройки и глубокого понимания системы, применяются реже, но могут оказаться очень полезными. Рассмотрим одну из таких малоиспользуемых возможностей - векторный поиск.
Концепция векторного поиска (vector search) основана на представлении объектов в виде многомерных числовых векторов и определении их близости по метрике расстояния (например, косинусное расстояние, евклидово расстояние или расстояние Джеффри). Близость векторов отражает семантическую или контекстуальную схожесть объектов. Текстовая информация может быть преобразована в вектор с помощью моделей машинного обучения, таких как Word2Vec, BERT или других методов embedding.
Создание и конфигурирование индекса
В Elasticsearch начиная с версии 7.10 появилась поддержка поля dense_vector
- специального типа данных, позволяющего хранить и индексировать векторы фиксированной размерности. При создании этого типа данных можно указать следующие параметры:
dims
– количество измерений (размерность) вектора, обязательный.similarity
– алгоритм сравнения векторов. Доступны следующие варианты:l2_norm
– евклидово расстояние,dot_product
– скалярное произведение,cosine
– косинусное расстояние.index
– логическое значение (true/false), указывает, нужно ли индексировать вектор для поиска (по умолчанию false).index_options
– дополнительные параметры для индексации.type
– тип индексации, например, hnsw (Hierarchical Navigable Small World).m
– параметр HNSW, определяющий количество связей между узлами (обычно 16-100).ef_construction
– параметр HNSW, управляющий точностью построения индекса (обычно 100-200).
Пример создания индекса:
PUT semantic_index
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"vector": {
"type": "dense_vector",
"dims": 512, // Количество измерений вектора
"similarity": "cosine", // Тип метрики: косинусное расстояние
"index": true, // Индексировать ли вектор для поиска через script_score
"index_options": { // Параметры для индексации (если "index": true)
"type": "hnsw",
"m": 16, // Количество связей между узлами HNSW (обычно 16-100)
"ef_construction": 100 //Точность построения индекса (обычно 100-200)
}
},
"title": {
"type": "text"
}
}
}
}
Автоматически вычислить значения dense_vector
встроенными средствами Elasticsearch нельзя, потому что в нем пока нет встроенных механизмов генерации эмбеддингов из текстовых или других данных без предварительно обученной модели машинного обучения. Даже встроенные модели, такие как ELSER, нужно сначала загрузить и активировать. Чтобы получить и проиндексировать семантические вектора, можно воспользоваться следующими способами:
получить вектора через внешний сервис и явно передать значения вектора при добавлении документа;
подключить предобученную ML-модель для генерации векторов через Inference Pipeline.
Индексация векторов через внешний сервис
Внешний сервис подразумевает решение, которое позволяет предварительно обработать текстовые данные и получить вектора. В качестве такого решения может выступать собственная NLP-модель или сервисы для семантического анализа, например: OpenAI, Hugging Face.
Как пример, для получения вектора воспользуемся библиотекой transformers от Hugging Face и фреймворком torch. Установим зависимости:
pip install sentence-transformers
И воспользуемся специализированной моделью distiluse-base-multilingual-cased-v2
для поиска смысловой схожести между текстами. Эта многоязычная нейросетевая модель построена на базе DistilBERT и обучена на Natural Language Inference (NLI) и парафразных задачах, то есть способна определять насколько два предложения означают одно и то же. Модель позволяет сформировать 512-мерный вектор.
Скрипт на Python для получения вектора:
from sentence_transformers import SentenceTransformer
# Загружаем модель
model = SentenceTransformer('distiluse-base-multilingual-cased-v2')
# Входной текст
sentence = "Привет, как настроение?"
# Получаем векторное представление
vector = model.encode(sentence)
# Выводим вектор
print("Размерность вектора:", vector.shape)
print("Векторное представление:", vector)
В результате получаем 512-мерный вектор, значение которого можно проиндексировать в Elasticsearch, указав в поле с типом dense_vector
:
PUT semantic_index/_doc/1
{
"title": "Привет, как настроение?",
"vector": [-1.79605780e-03, -4.74534556e-03, ..., -1.98550411e-02]
}
PUT semantic_index/_doc/2
{
"title": "Как дела?",
"vector": [1.28867049e-02, 4.09924565e-03, ..., -2.74000857e-02]
}
PUT semantic_index/_doc/3
{
"title": "Который час?",
"vector": [0.01102088, 0.00279999, ..., -0.0050466]
}
PUT semantic_index/_doc/4
{
"title": "Здравствуй, как жизнь?",
"vector": [0.00611453, -0.0028536, ..., -0.02347422]
}
PUT semantic_index/_doc/5
{
"title": "Сколько времени?",
"vector": [1.17157530e-02, 2.19503101e-02, ..., -7.98434392e-03]
}
В результате в индексе сохранено 5 документов и они доступны для поиска.
Индексация векторов с использованием Ingest Pipeline с Inference
Альтернативный вариант получения и индексации векторов - это воспользоваться Ingest Pipeline с Inference. Данный инструмент позволяет интегрировать машинное обучение в обработку данных при индексации. Ограничением является то, что Elasticsearch ML доступен только в платных тарифах. В бесплатной версии не получится использовать Ingest Pipeline Inference или upload моделей.
Для начала нужно установить модель в Elasticsearch. Для этого можно воспользоваться инструментом Eland.
pip install eland # Установка Eland
eland_import_hub_model \
--url http://localhost:9200 \ # Адрес Elasticsearch
--hub-model-id sentence-transformers/distiluse-base-multilingual-cased-v2 \
--task text_embedding \
--model-id distiluse-base-multilingual-cased-v2 \
--es-username elastic_username \
--es-password elastic_password
И проверить статус установки в Elasticsearch:
GET _ml/trained_models/distiluse-base-multilingual-cased-v2/_stats
А также можно вызвать запрос получения семантического вектора:
POST _ml/trained_models/distiluse-base-multilingual-cased-v2/_infer
{
"docs": [
{
"title": "Привет, как настроение?"
}
]
}
Когда модель установлена в Elasticsearch необходимо создать Ingest Pipeline с Inference:
PUT _ingest/pipeline/semantic-pipeline
{
"processors": [
{
"inference": {
"model_id": "distiluse-base-multilingual-cased-v2",
"input_output_map": {
"title": "title"
},
"target_field": "vector",
"field_map": {
"title": "title"
}
}
}
]
}
Также можно скорректировать настройки индекса и указать default_pipeline
:
PUT semantic_index
{
"settings": {
"default_pipeline": "semantic-pipeline"
},
"mappings": {
"properties": {
"vector": {
"type": "dense_vector",
"dims": 512, // Количество измерений вектора
"similarity": "cosine", // Тип метрики: косинусное расстояние
"index": true, // Индексировать ли вектор для поиска через script_score
"index_options": { // Параметры для индексации (если "index": true)
"type": "hnsw",
"m": 16, // Количество связей между узлами HNSW (обычно 16-100)
"ef_construction": 100 //Точность построения индекса (обычно 100-200)
}
},
"title": {
"type": "text"
}
}
}
}
Теперь при добавлении документа Elasticsearch автоматически создаст dense_vector
, используя дефолтный или указанный pipeline с NLP-моделью.
PUT semantic_index/_doc/1
{
"title": "Привет, как настроение?"
}
PUT semantic_index/_doc/2?pipeline=semantic-pipeline
{
"title": "Как дела?"
}
PUT semantic_index/_doc/3
{
"title": "Который час?"
}
PUT semantic_index/_doc/4?pipeline=semantic-pipeline
{
"title": "Здравствуй, как жизнь?"
}
PUT semantic_index/_doc/5
{
"title": "Сколько времени?"
}
В результате pipeline автоматически создаст и заполнит поле vector
, в индексе будет сохранено 5 документов и они будут доступны для поиска.
Применение запросов векторного поиска
Для векторного поиска семантически схожих объектов используется метод ближайших соседей (kNN). При выполнении knn
поиска, указывается вектор запроса (query_vector
) и количество ближайших соседей (k
), которые нужно найти. Elasticsearch вычисляет расстояние между вектором запроса и векторами документов в индексе, используя метрику расстояния (например, евклидово расстояние или косинусное сходство). Документы ранжируются по их близости к вектору запроса. Чем меньше расстояние, т.е. больше сходство, тем выше документ окажется в результатах поиска. Elasticsearch возвращает k ближайших документов, отсортированных по степени их близости к запросу.
Сформируем вектора для фразы “Привет, как настроение?” и выполним запрос поиска похожих документов запросом knn
:
POST semantic_index/_search
{
"_source": {
"exclude": [
"vector"
]
},
"knn": {
"field": "vector",
"query_vector": [
-0.001796057797037065,
-0.004745345562696457,
-0.029693424701690674,
… ,
-0.019855041056871414
],
"k": 5,
"num_candidates": 100
}
}
В результате получаем:
{
"took": 7,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": 0.9356358,
"hits": [
{
"_index": "semantic_index",
"_id": "3",
"_score": 0.9356358,
"_source": {
"title": "Здравствуй, как жизнь?"
}
},
{
"_index": "semantic_index",
"_id": "1",
"_score": 0.9054657,
"_source": {
"title": "Как дела?"
}
},
{
"_index": "semantic_index",
"_id": "5",
"_score": 0.7244736,
"_source": {
"title": "Привет, не забудь помыть посуду"
}
},
{
"_index": "semantic_index",
"_id": "2",
"_score": 0.64549077,
"_source": {
"title": "Который час?"
}
},
{
"_index": "semantic_index",
"_id": "4",
"_score": 0.63735604,
"_source": {
"title": "Поставь лайк"
}
}
]
}
}
Из результата выполнения запроса видно, что документы отранжировались по _score
с учетом их смысловой близости. Чем значение _score
ближе к 1, тем выше семантическая схожесть. В данном случае фраза “Привет, как настроение?” близка с фразами “Здравствуй, как жизнь?” и “Как дела?”, то есть смысловая нагрузка действительно учтена.
Нужно понимать, что качество векторного поиска зависит от поставленной задачи и используемой ML-модели. В некоторых случаях для достижения лучшего результата правильнее будет создать и использовать собственную ML-модель.
Потенциал векторного поиска
С появлением поддержки векторов (dense vectors
) в Elasticsearch стало возможным выполнять семантический поиск, учитывающий смысловую близость терминов.
Использование kNN в Elasticsearch позволяет:
Искать семантически похожие документы. Представить текст в виде векторов можно за счет моделей BERT, SBERT или OpenAI embeddings.
Находить похожие изображения. Векторные представления изображений можно получать с помощью ResNet, ViT или CLIP.
Создавать системы рекомендаций. Для этого нужно векторизовать предпочтения пользователя и искать товары, близкие к предпочтениям.
Однако из-за сложности настройки и необходимости интеграции с моделями машинного обучения, эта функция пока не получила широкого распространения.
В заключении хочется сказать, что Elasticsearch мощный инструмент, который предлагает широкий спектр функций и возможностей, но некоторые из них остаются недооцененными или редко используемыми из-за их сложности, специфичности или недостаточной осведомленности разработчиков. Повышение знаний об этих возможностях и обучение их эффективному использованию может значительно расширить потенциал приложений, основанных на Elasticsearch.