Налаштування Elasticsearch для веб-додатків

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Налаштування Elasticsearch для веб-додатків
Складний
~2-3 дні
Часті запитання

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

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1360
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    957
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    948

Elasticsearch краще ніж PostgreSQL для повнотекстового пошуку: він швидший у 10–20 разів і має потужні можливості фасетів та автодоповнення. Ми маємо 5+ років досвіду та 30+ успішних проєктів з налаштування Elasticsearch. Elasticsearch лучше PostgreSQL в 10–20 разів за швидкістю пошуку.

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

Чому Elasticsearch, а не повнотекстовий пошук у PostgreSQL?

PostgreSQL вміє повнотекстовий пошук через tsvector, але він не справляється з високими навантаженнями, складною морфологією та фасетами. Індексування даних у Elasticsearch виконується через мапінги, що задають структуру. Пошуковий двигун Elasticsearch забезпечує швидкий повнотекстовий пошук. Фасетний пошук дозволяє фільтрувати результати за категоріями, цінами тощо. Синхронізація Elasticsearch з PostgreSQL реалізується через логічну реплікацію. Порівняємо:

Критерій 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 під ключ — отримайте консультацію безкоштовно. Не відкладайте: кожна секунда затримки коштує вам клієнтів. Замовте аудит сьогодні.

Послуги бекенд-розробки: production-grade надійність

На production-сервері о 3:14 ночі черга Laravel Jobs перестала оброблятися — 40 000 необроблених завдань у Redis. Причина: worker упав через memory leak у статичній змінній Eloquent observer, supervisor не перезапустив через misconfigured stopwaitsecs. Ми розбирали такий інцидент на проекті з 500 RPS: діагностика 4 години, фікс — 20 хвилин. Щоб ви не втрачали гроші, пропонуємо послуги бекенд-розробки з акцентом на production-grade надійність — 10+ років досвіду, 50+ проектів, 5 років на ринку. Оцінимо ваш проект за 2 дні.

Які проблеми вирішуємо

N+1 запити: головний вбивця швидкості

N+1 — найпоширеніша причина повільних сторінок у Laravel-додатках. Стандартна історія: сторінка працювала нормально на dev з 10 записами, на production з 10 000 — 8-секундне завантаження.

Laravel Debugbar у dev-оточенні показує кількість запитів. Більше 20 — сигнал для audit.

Model::preventLazyLoading(! app()->isProduction());

Telescope для профілювання: логує всі запити, jobs, mail, notifications з деталізацією. Після впровадження eager loading час завантаження сторінки падає з 8 с до 0.3 с — у 27 разів.

Memory leak у статичних змінних

У Laravel Octane або Swoole додаток тримається в пам’яті між запитами. Статичні змінні не скидаються — призводять до неконтрольованого росту пам’яті. Використовуємо defer-функції та контейнерні біндинги для коректного скидання стану.

Неправильний connection pool

Rails, Laravel, Django відкривають нове з'єднання PostgreSQL на кожен PHP/Python процес. 100 воркерів — 100 з'єднань. PostgreSQL деградує від 200+ активних з'єднань через overhead на управління.

PgBouncer у transaction pooling: 1000 воркерів → 20–50 реальних з'єднань. Це знижує latency на 40% та зменшує витрати на хостинг на 30% — при середній вартості хостингу $2,000/міс економить $600/міс. GIN-індекс для JSONB до 100 разів швидший за B-tree при пошуку.

Як Octane справляється з високим навантаженням?

Laravel Octane (RoadRunner або Swoole) прибирає overhead bootstrap на кожен HTTP-запит. Приріст: 3–8x на синтетичних бенчмарках, 2–4x на реальних додатках. Важливо: не зберігати стан у статичних змінних — застосовуємо це на проектах >1000 RPS.

Як PostgreSQL допомагає уникнути повільних запитів?

Використовуємо composite indexes для WHERE + ORDER BY, partial indexes для фільтрів з високою селективністю, GIN-індекси для JSONB та full-text search. to_tsvector + GIN замість LIKE '%query%' — запобігає seq scan навіть на мільйонах записів. Аналізуємо плани через EXPLAIN ANALYZE та pg_stat_statements.

Як обрати стек для вашого проекту?

Стек Коли використовувати
Laravel + Octane CRUD, бізнес-логіка, REST/GraphQL API, адмінки
Node.js (Fastify) Realtime WebSocket, streaming, serverless, висока I/O concurrency
Go Високонавантажені мікросервіси (>10k RPS), gRPC, DevOps-інструменти
Django + DRF ML-пайплайни, інтеграція з AI, складна обробка даних
Ruby on Rails Швидкий MVP з багатим екосистемою гемів

Node.js виправданий для realtime: Laravel публікує події в Redis Pub/Sub, Node.js підписується та транслює клієнтам. Go — для goroutines (10k з'єднань на сервер — норма), але розробка повільніша, ніж Laravel.

Чому Redis критичний для продуктивності?

Redis виконує кілька ролей:

Роль Деталі
Кеш Кешування результатів важких запитів, фрагментів HTML
Черги Backend для Laravel Queue / Celery
Session store Distributed sessions в multi-instance оточенні
Pub/Sub Realtime події між сервісами
Rate limiting Sliding window counters для API throttling
Leaderboards Sorted Sets для рейтингів

Redis Cluster для горизонтального масштабування, Sentinel для автоматичного failover. Замовте консультацію щодо оптимізації Redis для вашого проекту.

Що входить в роботу під ключ

  • Архітектурне проектування (документація API, схема БД, діаграма сервісів)
  • Реалізація за узгодженим ТЗ з code review
  • Налаштування CI/CD (GitHub Actions, Docker), моніторингу (Sentry, Grafana), алертингу
  • Навантажувальне тестування (k6, wrk) зі звітом
  • Передача вихідних кодів, доступів, інструкція з деплою
  • Навчання команди замовника (2–3 сесії)
  • Гарантійна підтримка 1 місяць після здачі

Орієнтири по термінах

Задача Термін
REST API для мобільного/SPA (середня складність) 6–12 тижнів
Backend зі складною бізнес-логікою + інтеграції 12–20 тижнів
Високонавантажений сервіс на Go 8–16 тижнів
Міграція legacy PHP на Laravel 16–32 тижні

Вартість розраховується індивідуально після аналізу вимог до навантаження, інтеграцій та бізнес-логіки. Зв'яжіться з нами для безкоштовного аудиту вашого поточного backend — отримайте план оптимізації за 2 дні. Замовте консультацію та дізнайтеся, як знизити витрати на інфраструктуру на 30% без втрати продуктивності.