Elasticsearch: настройка поиска для веб-приложений

Представьте: ваш интернет-магазин вырос до 500 000 товаров, и поиск по названию через ILIKE % в PostgreSQL тормозит на каждой букве. Клиенты уходят, конверсия падает — каждая секунда задержки снижает её на 7%. Мы с такой ситуацией сталкивались не раз. В одном проекте поиск по 1 млн товаров занимал 8

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Elasticsearch: настройка поиска для веб-приложений
Сложный
~2-3 дня

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    983
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1245
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    983
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    998

Представьте: ваш интернет-магазин вырос до 500 000 товаров, и поиск по названию через ILIKE % в PostgreSQL тормозит на каждой букве. Клиенты уходят, конверсия падает — каждая секунда задержки снижает её на 7%. Мы с такой ситуацией сталкивались не раз. В одном проекте поиск по 1 млн товаров занимал 8 секунд, после миграции на Elasticsearch — 80 мс. Конверсия выросла на 15%. За время работы мы настроили Elasticsearch для 30+ проектов — от каталогов до маркетплейсов. Наш опыт показывает: правильная настройка индексов и анализаторов сокращает время поиска в 10 раз.

Почему Elasticsearch, а не полнотекстовый поиск в PostgreSQL?

PostgreSQL умеет полнотекстовый поиск через tsvector, но он не справляется с высокими нагрузками, сложной морфологией и фасетами. Elasticsearch превосходит PostgreSQL в 10-20 раз по скорости на реальных нагрузках. Сравним:

Критерий PostgreSQL (ILIKE/tsvector) Elasticsearch
Скорость поиска по 1 млн записей 200-500 мс 10-50 мс
Морфология русского языка Базовая через словари Стемминг, синонимы, кастомные анализаторы
Фасетная фильтрация Ограничена Мощные агрегации
Автодополнение Костыли с триграммами Edge n-gram, Suggester
Геопоиск Через PostGIS, медленно Нативно, быстро

Кроме того, распределенный поиск Elasticsearch позволяет обрабатывать миллиарды документов.

Как мы настраиваем Elasticsearch под вашу задачу

Мы не ставим Elasticsearch «из коробки». Всегда анализируем структуру данных и типичные запросы. Вот реальный пример: для каталога электроники с 200 000 товаров мы создали индекс с двумя анализаторами: русским (стемминг + стоп-слова) и автодополнением (edge n-gram). Настройка заняла 4 дня. В другом проекте потребовался кастомный char_filter для очистки названий от спецсимволов.

Установка Elasticsearch 8.x

# Добавить репозиторий wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" > /etc/apt/sources.list.d/elastic-8.x.list apt update && apt install -y elasticsearch # Сохранить пароль superuser из вывода установки systemctl enable elasticsearch && systemctl start elasticsearch 

Минимальный конфиг для single-node dev:

# /etc/elasticsearch/elasticsearch.yml cluster.name: myapp-search node.name: node-1 path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch network.host: 127.0.0.1 discovery.type: single-node xpack.security.enabled: true xpack.security.http.ssl.enabled: false # для dev; в prod — включить 

Heap выделяем так: не более 50% RAM, не более 32 ГБ (из-за compressed OOP). Для старта 4 ГБ достаточно.

Маппинг индекса

Маппинг — схема индекса. Неверный маппинг не исправить без переиндексации. Подробнее читайте в маппинг Elasticsearch:

PUT /products { "settings": { "number_of_shards": 2, "number_of_replicas": 1, "analysis": { "analyzer": { "russian_analyzer": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "russian_stop", "russian_stemmer"] }, "autocomplete_analyzer": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "edge_ngram_filter"] }, "autocomplete_search": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase"] } }, "filter": { "russian_stop": { "type": "stop", "stopwords": "_russian_" }, "russian_stemmer": { "type": "stemmer", "language": "russian" }, "edge_ngram_filter": { "type": "edge_ngram", "min_gram": 2, "max_gram": 20 } } } }, "mappings": { "properties": { "id": { "type": "keyword" }, "name": { "type": "text", "analyzer": "russian_analyzer", "fields": { "autocomplete": { "type": "text", "analyzer": "autocomplete_analyzer", "search_analyzer": "autocomplete_search" }, "keyword": { "type": "keyword" } } }, "description": { "type": "text", "analyzer": "russian_analyzer" }, "category": { "type": "keyword" }, "brand": { "type": "keyword" }, "price": { "type": "scaled_float", "scaling_factor": 100 }, "in_stock": { "type": "boolean" }, "attributes": { "type": "object", "dynamic": true }, "location": { "type": "geo_point" }, "created_at": { "type": "date" } } } } 

Поисковый запрос с фасетами

POST /products/_search { "query": { "bool": { "must": [ { "multi_match": { "query": "беспроводные наушники", "fields": ["name^3", "description", "name.autocomplete^2"], "type": "best_fields", "fuzziness": "AUTO" } } ], "filter": [ { "term": { "in_stock": true } }, { "range": { "price": { "gte": 1000, "lte": 10000 } } }, { "terms": { "category": ["audio", "headphones"] } } ] } }, "aggs": { "categories": { "terms": { "field": "category", "size": 20 } }, "brands": { "terms": { "field": "brand", "size": 30 } }, "price_ranges": { "range": { "field": "price", "ranges": [ { "to": 1000 }, { "from": 1000, "to": 5000 }, { "from": 5000, "to": 15000 }, { "from": 15000 } ] } } }, "highlight": { "fields": { "name": {}, "description": { "fragment_size": 150 } } }, "from": 0, "size": 24, "sort": [{ "_score": "desc" }, { "created_at": "desc" }] } 

Как настроить автодополнение?

Автодополнение реализуется через edge n-gram анализатор, как показано в маппинге. Edge n-gram создаёт токены от 2 до 20 символов. Пользователи получают подсказки уже после ввода 2-3 символов — это повышает UX. Поле name.autocomplete индексирует начало каждого слова. В поисковом запросе используем match_phrase_prefix или multi_match по этому полю.

Ориентировочные сроки

Сроки зависят от сложности. Базовая настройка с одним индексом и интеграцией — 3-5 дней. Если нужны автодополнение, фасеты и синхронизация из PostgreSQL — ещё 3-5 дней. Кластер из 3 нод с мониторингом — 1-2 недели. Стоимость рассчитывается индивидуально. Ниже таблица ориентировочных сроков по этапам:

Этап Длительность
Аналитика и проектирование 1-2 дня
Установка и настройка индекса 1-2 дня
Интеграция с приложением 2-3 дня
Тестирование и оптимизация 1-2 дня
Деплой и мониторинг 1 день

Синхронизация данных из PostgreSQL

Для синхронизации используем логическую репликацию через Debezium + Kafka в промышленных сценариях. Для старта подходит периодический реиндекс через cron. Ниже пример на TypeScript:

// sync/product-indexer.ts import { Client } from '@elastic/elasticsearch' import { Pool } from 'pg' const es = new Client({ node: 'http://localhost:9200', auth: { username: 'elastic', password: process.env.ES_PASSWORD! } }) const pg = new Pool({ connectionString: process.env.DATABASE_URL }) export async function indexProduct(id: string) { const { rows } = await pg.query(` SELECT p.*, c.name AS category_name, json_agg(json_build_object('key', a.key, 'value', a.value)) AS attributes FROM products p LEFT JOIN categories c ON c.id = p.category_id LEFT JOIN product_attributes a ON a.product_id = p.id WHERE p.id = $1 GROUP BY p.id, c.name `, [id]) if (!rows.length) { await es.delete({ index: 'products', id }) return } const p = rows[0] await es.index({ index: 'products', id: p.id, document: { id: p.id, name: p.name, description: p.description, category: p.category_name, price: p.price, in_stock: p.stock_quantity > 0, attributes: Object.fromEntries(p.attributes?.map((a: any) => [a.key, a.value]) ?? []), created_at: p.created_at } }) } export async function reindexAll() { const { rows } = await pg.query('SELECT id FROM products WHERE deleted_at IS NULL') const chunks = chunk(rows.map(r => r.id), 100) for (const ids of chunks) { await Promise.all(ids.map(indexProduct)) console.log(`Indexed ${ids.length} products`) } } 

Пошаговый план настройки

  1. Аналитика — изучаем структуру данных, типичные запросы, требования к скорости (1-2 дня).
  2. Проектирование — разрабатываем маппинг, анализаторы, схему синхронизации (1-2 дня).
  3. Реализация — устанавливаем Elasticsearch, настраиваем индекс, пишем интеграцию (3-5 дней).
  4. Тестирование — проверяем релевантность поиска, фасеты, скорость (1-2 дня).
  5. Деплой — разворачиваем на production, настраиваем мониторинг (1 день).

Что входит в работу

После завершения работ вы получаете:

  • Настроенный кластер Elasticsearch (одна нода или кластер) с доступом
  • Индексы с кастомными анализаторами под русский язык
  • Интеграцию с вашим приложением через REST API
  • Скрипты синхронизации данных (например, из PostgreSQL)
  • Документацию по эксплуатации
  • Обучение вашей команды

Мы даём гарантию на 3 месяца: если поиск работает не так, как ожидалось, исправляем бесплатно.

Мониторинг кластера

Рекомендуем отслеживать здоровье кластера через _cluster/health и включать slowlog для поисковых запросов. Для сбора метрик используйте Elastic Metricbeat — это позволяет вовремя заметить деградацию.

Типичные ошибки при настройке
  • Неправильный маппинг — dynamic mapping приводит к неожиданным типам полей, что ломает фасеты. Всегда задавайте схему явно.
  • Слишком маленький heap — нехватка памяти вызывает частые GC паузы и падение производительности. Выделяйте не менее 50% RAM, но не более 32 ГБ.
  • Отсутствие slowlog — без него не увидеть медленные запросы. Включите slowlog в конфиге: index.search.slowlog.threshold.query.warn: 2s.
  • Игнорирование реплик — для отказоустойчивости нужно хотя бы 1 реплика. Настройте number_of_replicas: 1.

Начать работу

Если ваш поиск тормозит или не справляется с нагрузкой — свяжитесь с нами. Мы оценим вашу систему и предложим решение. Закажите настройку Elasticsearch под ключ — получите консультацию бесплатно. Не откладывайте: каждая секунда задержки стоит вам клиентов. Закажите аудит сегодня.