Полнотекстовый поиск: PostgreSQL, Elasticsearch, Meilisearch

Когда каталог разрастается, стандартный поиск перестаёт справляться: пользователи не находят нужные товары, а время ответа растёт. Мы внедряем полнотекстовый поиск на базе PostgreSQL, Elasticsearch или Meilisearch, подбирая оптимальное решение под ваши задачи. Наша команда реализует проект под ключ — от аудита до настройки и последующей поддержки, обеспечивая быстрый и релевантный поиск, который масштабируется вместе с бизнесом.

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

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

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

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

Представьте: интернет-магазин на 50 000 товаров, где пользователи не находят нужную позицию по запросу «беспроводные наушники» — выдаются только точные совпадения в названии. Мы решаем эту проблему с помощью полнотекстового поиска (FTS). Для каталога электроники из 120 000 SKU внедрили Elasticsearch с русским стеммером и фасетами. Среднее время поиска упало с 3 с до 0.1 с, конверсия выросла на 30%. За 5 лет реализовали 15+ проектов FTS, сократив время поиска в 10 раз и повысив конверсию на 25%. Экономия бюджета на инфраструктуру достигает 30% за счёт снижения нагрузки на БД. Ниже разберём, как выбрать и внедрить полнотекстовый поиск: от PostgreSQL FTS до внешних движков.

Какие проблемы решает полнотекстовый поиск?

На больших объёмах данных LIKE-запросы вызывают полное сканирование таблицы. При 100 000 товаров время отклика превышает 5 секунд, что приводит к потере до 40% пользователей. FTS использует инвертированные индексы, позволяя находить записи за миллисекунды. Кроме того, FTS ранжирует результаты по релевантности: заголовки получают больший вес, чем описания, а свежие записи — выше позицию. Это повышает конверсию поиска на 25% и снижает нагрузку на БД.

Какую архитектуру выбрать: встроенный FTS или внешний движок?

Выбор зависит от объёмов данных и требований к функциям. Для типового каталога до 100 000 записей достаточно PostgreSQL FTS. Для сложных сценариев (fuzzy search, фасеты, синонимы) нужен Elasticsearch или Meilisearch.

PostgreSQL FTS: встроенный вариант

Подготовка схемы:

ALTER TABLE products ADD COLUMN search_vector TSVECTOR GENERATED ALWAYS AS (
    to_tsvector('russian', coalesce(title, '') || ' ' || coalesce(description, '') || ' ' || coalesce(brand, ''))
) STORED;
CREATE INDEX idx_products_fts ON products USING GIN (search_vector);

GENERATED ALWAYS AS ... STORED — колонка обновляется автоматически при INSERT/UPDATE, не нужен триггер.

Поиск с ранжированием и сниппетами:

-- Простой запрос
SELECT id, title, ts_rank(search_vector, query) AS rank,
       ts_headline('russian', description, query, 'MaxWords=30, MinWords=15, StartSel=<b>, StopSel=</b>' ) AS excerpt
FROM products,
     plainto_tsquery('russian', 'беспроводные наушники') AS query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;

-- websearch_to_tsquery: поддерживает "фразы", -исключения, OR
SELECT id, title
FROM products
WHERE search_vector @@ websearch_to_tsquery('russian', '"беспроводные наушники" -проводные')
ORDER BY ts_rank(search_vector, websearch_to_tsquery('russian', '"беспроводные наушники" -проводные')) DESC;

ts_headline генерирует сниппет с подсвеченными совпадениями. Подробнее — в документации PostgreSQL.

Elasticsearch: когда нужен внешний движок

PostgreSQL FTS ограничен: нет fuzzy search, нет синонимов из коробки, нет агрегаций по фасетам. Если нужно всё это — Elasticsearch или OpenSearch.

Схема индекса с русским анализатором:

PUT /products
{
  "settings": {
    "analysis": {
      "analyzer": {
        "russian_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "russian_stop", "russian_stemmer"]
        }
      },
      "filter": {
        "russian_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "russian_stemmer": {
          "type": "stemmer",
          "language": "russian"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "russian_analyzer",
        "boost": 3
      },
      "brand": {
        "type": "text",
        "analyzer": "russian_analyzer",
        "boost": 2
      },
      "description": {
        "type": "text",
        "analyzer": "russian_analyzer"
      },
      "category_id": {
        "type": "keyword"
      },
      "price": {
        "type": "double"
      },
      "status": {
        "type": "keyword"
      },
      "created_at": {
        "type": "date"
      }
    }
  }
}

Поиск с фасетами и фильтрами:

POST /products/_search
{
  "query": {
    "bool": {
      "must": {
        "multi_match": {
          "query": "беспроводные наушники",
          "fields": ["title^3", "brand^2", "description"],
          "type": "best_fields",
          "fuzziness": "AUTO"
        }
      },
      "filter": [
        {
          "term": {
            "status": "published"
          }
        },
        {
          "range": {
            "price": {
              "gte": 1000,
              "lte": 15000
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "by_brand": {
      "terms": {
        "field": "brand.keyword",
        "size": 20
      }
    },
    "price_stats": {
      "stats": {
        "field": "price"
      }
    }
  },
  "highlight": {
    "fields": {
      "title": {
        "number_of_fragments": 0
      },
      "description": {
        "fragment_size": 150,
        "number_of_fragments": 3
      }
    }
  },
  "from": 0,
  "size": 20
}

Синхронизация с PostgreSQL через CDC (Debezium + Kafka) гарантирует попадание данных в ES даже при сбоях сервиса. Документация Elasticsearch содержит полное описание всех возможностей.

Как обеспечить синхронизацию данных между PostgreSQL и Elasticsearch?

Для синхронизации изменений используется Change Data Capture (CDC) с помощью Debezium и Kafka. Debezium отслеживает изменения в WAL PostgreSQL и публикует события в Kafka, откуда Elasticsearch через Logstash или собственный consumer получает обновления. Альтернативно — прямой API-сервис, который периодически вычитывает изменения по timestamp. CDC надёжнее: не теряет данные при сбоях и не требует дополнительных запросов к БД. Для настройки CDC необходимо включить логирование WAL (wal_level = logical) и создать публикацию для таблиц.

Почему стоит внедрять полнотекстовый поиск немедленно?

Пользователи уходят, если не находят товар за 3 секунды. Полнотекстовый поиск поднимает конверсию поиска на 25% и снижает нагрузку на БД в 10 раз. Наши сертифицированные инженеры Elastic помогают выбрать оптимальное решение под ваш стек. Получите консультацию — оценим ваш проект и подберём оптимальное решение.

Как мы это делаем: кейс интернет-магазина

Мы реализовали поиск для каталога из 120 000 товаров за 3 дня. Использовали Elasticsearch с русским стеммером и фасетами по бренду, цене и статусу. Результат: среднее время поиска упало с 3 с до 0.1 с, конверсия выросла на 30%.

Процесс работы: пошаговый план

Этап Длительность Результат
Аналитика 0,5 дня Аудит схемы БД, сценариев поиска, приоритетные поля
Проектирование 0,5 дня Выбор движка (PostgreSQL FTS / Elasticsearch / Meilisearch) и схемы индекса
Реализация 1–2 дня Конфигурации, код индексации и запросов
Тестирование 0,5 дня Проверка релевантности, производительности и покрытия
Деплой 0,5 дня Настройка синхронизации (CDC или сервис) и мониторинга

Типичные ошибки

  • Использование LIKE для поиска по большим таблицам (медленно, нет морфологии).
  • Отсутствие весов полей (заголовок и описание ранжируются одинаково).
  • Игнорирование нормализации (учёт окончаний и падежей).

Сравнение движков

Характеристика PostgreSQL FTS Elasticsearch/OpenSearch Meilisearch
Настройка Минуты Часы–дни Минуты
Fuzzy search Через расширения Встроен Встроен
Фасеты Сложно Встроен Встроен
Синхронизация Не нужна CDC или sync CDC или sync
Инфраструктура Уже есть +JVM сервер +Go сервер
Дополнительная настройка анализатора Meilisearch

В Meilisearch достаточно задать язык индекса при создании:

curl -X POST 'http://localhost:7700/indexes' \
  -H 'Content-Type: application/json' \
  -d '{ "uid": "products", "primaryKey": "id" }'

Затем настроить поля для поиска и веса через PATCH settings. Свяжитесь с нами — оценим ваш проект и подберём оптимальное решение.

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

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

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060