Какие основные типы данных и операторы используются для реализации полнотекстового поиска в PostgreSQL?

Ответ

Для полнотекстового поиска (Full-Text Search, FTS) в PostgreSQL используются два основных типа данных и специальный оператор сопоставления.

Типы данных

  1. tsvector (Text Search Vector) Это тип данных, который хранит документ в оптимизированном для поиска виде: набор уникальных лексем (слов), которые были нормализованы (приведены к основной форме через стемминг) и из которых удалены стоп-слова (предлоги, союзы и т.д.).

  2. tsquery (Text Search Query) Это тип данных для хранения поискового запроса. Он также состоит из нормализованных лексем и может включать логические операторы для их комбинирования: & (И), | (ИЛИ), ! (НЕ).

Основной оператор

  • @@ — оператор сопоставления. Он возвращает true, если tsvector (документ) соответствует tsquery (запросу).

Пример рабочего процесса

-- 1. Создаем таблицу с колонкой типа tsvector
CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title TEXT,
    body TEXT,
    search_vector TSVECTOR
);

-- 2. Заполняем таблицу, преобразуя текст в tsvector с помощью to_tsvector()
INSERT INTO articles (title, body)
VALUES ('A Quick Guide to Go', 'Golang is a fast and compiled language.');

-- Рекомендуется обновлять tsvector через триггер или при записи данных
UPDATE articles
SET search_vector = to_tsvector('russian', title || ' ' || body)
WHERE id = 1;

-- 3. Выполняем поиск с помощью to_tsquery() и оператора @@
SELECT title, body
FROM articles
WHERE search_vector @@ to_tsquery('russian', 'быстрый & язык');

-- 4. Для ускорения поиска создаем GIN-индекс
CREATE INDEX idx_articles_search ON articles USING GIN(search_vector);

Использование GIN (Generalized Inverted Index) индекса критически важно для производительности полнотекстового поиска на больших объемах данных.

Ответ 18+ 🔞

А, слушай, смотри, вот этот твой полнотекстовый поиск в PostgreSQL — это ж, блядь, просто песня, а не технология! Ну реально, как будто тебе дали волшебную палочку, чтобы искать не просто слова, а смыслы, ёпта.

Так, поехали по косточкам, чтобы не было, как у того Герасима, который нихуя не мог сказать, кроме «Му-му».

Вот смотри, вся магия крутится вокруг двух главных хуёвин, прости господи, типов данных:

  1. tsvector — это типа такой заумный мешок. Берёшь ты свой текст, сука, «Голанг — быстрый и компилируемый язык», а он его разбирает на молекулы: выкидывает все эти «и», «в», «на» (стоп-слова, блядь), оставляет только суть, да ещё и слова к корням приводит. Получается такая оптимизированная для поиска каша из лексем. Представь, что ты взял статью и пропустил через мясорубку, оставив только самое вкусное мясо.

  2. tsquery — а это уже твой запрос, но тоже приведённый к тому же самому, блядь, умному виду. Ты пишешь «быстрый И язык», а он делает из этого 'быстр' & 'язык'. И можно там операторы воткнуть: & (И), | (ИЛИ), ! (НЕТ). Мощная хуйня!

А чтобы это всё состыковать, есть оператор @@. Он просто отвечает на вопрос: «А этот умный мешок (tsvector) соответствует этому умному запросу (tsquery)?» Да или нет. Всё, пиздец, просто.

Ну и как это, сука, в жизни выглядит? Да элементарно!

-- 1. Делаем таблицу. Главное — не забыть колонку под наш волшебный tsvector.
CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title TEXT,
    body TEXT,
    search_vector TSVECTOR -- Вот он, красавец, будущий перемолотый текст!
);

-- 2. Пихаем туда данные. Но просто так пихнуть — не катит. Надо текст перемолоть!
INSERT INTO articles (title, body)
VALUES ('Быстрое руководство по Go', 'Golang — это быстрый и компилируемый язык.');

-- Вот тут-то и происходит магия! to_tsvector('russian', ...) — это и есть наша мясорубка.
-- 'russian' — говорим, мол, текст на русском, обрабатывай соответственно, ёпта.
UPDATE articles
SET search_vector = to_tsvector('russian', title || ' ' || body)
WHERE id = 1;

-- 3. А теперь, сука, ищем! Берём запрос, пропускаем через to_tsquery и тычем оператором @@ в нашу колонку.
SELECT title, body
FROM articles
WHERE search_vector @@ to_tsquery('russian', 'быстрый & язык');
-- Найдёт! Потому что у нас в мешке есть и «быстр» и «язык». Красота!

-- 4. И ВНИМАНИЕ, БЛЯДЬ, САМОЕ ВАЖНОЕ! Если у тебя данных больше, чем три строки,
-- искать без индекса — это самоубийство, чистой воды.
-- Поэтому ВЕШАЕМ GIN-ИНДЕКС! Это как создать карту сокровищ для твоего перемолотого мешка.
-- Без него на больших объёмах будет пиздец, а не поиск.
CREATE INDEX idx_articles_search ON articles USING GIN(search_vector);

Вот и вся наука, блядь. Сделал tsvector, сделал tsquery, соединил через @@, навесил GIN-индекс — и ты король поиска. А без индекса будешь как тот немой Герасим — хочешь что-то сказать, а получается только «му-му», пока база не сдохнет от нагрузки.