полнотекстовый поиск
Полнотекстовый поиск — технология поиска по содержимому текстовых документов с учётом морфологии языка, синонимов, стоп-слов и ранжирования результатов по релевантности. В отличие от простого поиска по подстроке (LIKE '%слово%'), полнотекстовый поиск понимает, что «бежать», «бегу» и «бежали» — формы одного слова, умеет находить документы с несколькими ключевыми словами в разных частях текста и упорядочивает результаты по близости к запросу.
Как работает инвертированный индекс
Ядро полнотекстового поиска — инвертированный индекс (inverted index). При индексировании каждый документ разбивается на токены (слова), которые нормализуются (лемматизация или стемминг): «бегает», «бежал» → «бег». Получается словарь: ключ — нормализованный токен, значение — список документов, в которых он встречается, с позициями и частотами.
При поиске запрос проходит те же преобразования, и система ищет документы, содержащие нормализованные термины запроса. Ранжирование вычисляется по алгоритмам TF-IDF или BM25: документ, где слово встречается часто (TF) и само слово редко (IDF), ранжируется выше.
Полнотекстовый поиск в PostgreSQL
PostgreSQL содержит встроенный полнотекстовый поиск без дополнительных расширений:
- tsvector — тип для хранения нормализованного представления документа: to_tsvector('russian', 'Быстрая лиса прыгнула') → лексемы с позициями.
- tsquery — тип для поисковых запросов: to_tsquery('russian', 'быстрый & лиса').
- Оператор @@ проверяет соответствие: tsvector @@ tsquery.
- Функция ts_rank() вычисляет релевантность.
- Функция ts_headline() формирует сниппет с подсветкой найденных слов.
Для ускорения поиска создаётся GIN-индекс: CREATE INDEX ON documents USING GIN(to_tsvector('russian', content));. Или хранится готовый вектор в отдельном столбце с триггером обновления.
Конфигурации языка и словари
PostgreSQL поддерживает языковые конфигурации, определяющие стоп-слова (предлоги, союзы, не несущие смысловой нагрузки) и стемминг. Для русского языка используется конфигурация 'russian'. Словари подключаются в порядке очереди: простой (without stemming), ispell (словарь форм), snowball (алгоритмический стеммер). Кастомные словари позволяют добавлять синонимы и специализированную терминологию.
Elasticsearch и OpenSearch
Elasticsearch — специализированная поисковая система на основе Apache Lucene. Изначально проектировалась для полнотекстового поиска, а не для транзакционных данных. Возможности, выходящие за рамки PostgreSQL:
- Фасетный поиск с агрегациями (counts, ranges, terms).
- Fuzzy search — поиск с учётом опечаток (расстояние Левенштейна).
- Горизонтальное масштабирование через шардирование из коробки.
- Phrase search с учётом порядка слов и расстояния между ними.
- Rich analyzer ecosystem: языковые анализаторы, tokenizer-ы, char_filter-ы.
- Percolator — «обратный поиск»: запрос соответствует документу, а не наоборот.
Алгоритм ранжирования BM25
BM25 (Best Match 25) — современный стандарт ранжирования, пришедший на смену TF-IDF. Учитывает длину документа (нормализация): частое слово в коротком документе ценится выше, чем в длинном. Параметр k1 контролирует насыщение TF (по умолчанию 1.2), b — нормализацию длины (0.75). Elasticsearch использует BM25 по умолчанию с версии 5.0. PostgreSQL реализует собственный алгоритм через ts_rank, близкий к TF-IDF.
Морфологический анализ для русского языка
Русский язык с его развитой морфологией требует более сложного лингвистического анализа, чем английский. Стемминг (алгоритм Snowball/Porter для русского) отсекает окончания приближённо. Лемматизация — точнее: приводит к словарной форме. Для PostgreSQL существует расширение pg_stemmer и интеграция с Mystem (Яндекс). Elasticsearch поддерживает морфологию через плагин analysis-snowball и community-плагины для русского.
Гибридный поиск: fulltext + семантика
Полнотекстовый поиск точен для ключевых слов, но не понимает смысла. Семантический поиск (pgvector, Elasticsearch dense_vector) понимает смысл, но плохо работает с точными совпадениями. Современные системы комбинируют оба подхода через RRF (Reciprocal Rank Fusion) — объединение ранжированных списков. Такой гибридный поиск обеспечивает лучшее качество для большинства пользовательских запросов.
Частые вопросы
Чем полнотекстовый поиск отличается от LIKE '%слово%'?
LIKE '%слово%' — простое посимвольное совпадение, не использует индекс (полный скан таблицы), не понимает морфологии и не ранжирует результаты. Полнотекстовый поиск строит инвертированный индекс, нормализует слова (бежал = бег), применяет стоп-слова, вычисляет релевантность и работает на порядки быстрее на больших данных.
Когда выбрать Elasticsearch вместо PostgreSQL для поиска?
Elasticsearch оптимален для: поискового ядра с фасетами, агрегациями и нечётким поиском; поиска по большим объёмам данных (сотни миллионов документов) с требованиями высокой доступности; задач, где поиск — основная функция, а не одна из многих. PostgreSQL достаточен для большинства приложений с умеренными объёмами и когда поиск — вспомогательная функция.
Как реализовать поиск с учётом опечаток?
PostgreSQL: расширение pg_trgm создаёт триграммный индекс, позволяющий искать похожие строки по расстоянию Левенштейна (similarity function). Elasticsearch: параметр fuzziness в match-запросе задаёт допустимое число ошибок (AUTO, 0, 1, 2). Для русских слов fuzziness=1 исправляет одну ошибку в слове длиной 5+ символов.
Другие термины в теме «Базы данных»
Не хватает деталей?
Напишите, что уточнить по теме «полнотекстовый поиск» — это помогает улучшать материал и подсказывает, какие термины добавить дальше. Email необязателен: укажите, если хотите ответ только для вас (мы не шлём рассылки).