Ответ
Для полнотекстового поиска (Full-Text Search, FTS) в PostgreSQL используются два основных типа данных и специальный оператор сопоставления.
Типы данных
-
tsvector(Text Search Vector) Это тип данных, который хранит документ в оптимизированном для поиска виде: набор уникальных лексем (слов), которые были нормализованы (приведены к основной форме через стемминг) и из которых удалены стоп-слова (предлоги, союзы и т.д.). -
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 — это ж, блядь, просто песня, а не технология! Ну реально, как будто тебе дали волшебную палочку, чтобы искать не просто слова, а смыслы, ёпта.
Так, поехали по косточкам, чтобы не было, как у того Герасима, который нихуя не мог сказать, кроме «Му-му».
Вот смотри, вся магия крутится вокруг двух главных хуёвин, прости господи, типов данных:
-
tsvector— это типа такой заумный мешок. Берёшь ты свой текст, сука, «Голанг — быстрый и компилируемый язык», а он его разбирает на молекулы: выкидывает все эти «и», «в», «на» (стоп-слова, блядь), оставляет только суть, да ещё и слова к корням приводит. Получается такая оптимизированная для поиска каша из лексем. Представь, что ты взял статью и пропустил через мясорубку, оставив только самое вкусное мясо. -
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-индекс — и ты король поиска. А без индекса будешь как тот немой Герасим — хочешь что-то сказать, а получается только «му-му», пока база не сдохнет от нагрузки.