Full-Text Search
Real search — stemming, ranking, stop words — built into PostgreSQL, no separate search engine required for many use cases.
Intermediate
LIKE '%word%' can't use a standard index (the leading wildcard defeats it), doesn't understand word stems (searching "running" won't match "run"), has no concept of relevance ranking, and treats "the" and "cat" as equally significant. PostgreSQL's full-text search addresses all four.SELECT to_tsvector('english', 'The quick brown foxes are running');
-- 'brown':3 'fox':4 'quick':2 'run':6
-- notice: stop words ('the', 'are') removed, words stemmed ('foxes' → 'fox', 'running' → 'run')
SELECT to_tsvector('english', 'The quick brown foxes are running')
@@ to_tsquery('english', 'fox & run');
-- true — 'fox' and 'run' (stemmed) are both present
tsvector is a preprocessed, searchable representation of a document (stemmed, stop-words removed, with position info for ranking). tsquery is a preprocessed search query. @@ tests whether a vector matches a query.SELECT title, ts_rank(to_tsvector('english', body), query) AS rank
FROM articles, to_tsquery('english', 'postgresql & performance') query
WHERE to_tsvector('english', body) @@ query
ORDER BY rank DESC
LIMIT 10;
ts_rank scores how well a document matches, based on term frequency and (with ts_rank_cd) how close together the matching terms appear — use it to sort results by relevance rather than an arbitrary order.ALTER TABLE articles ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (to_tsvector('english', title || ' ' || body)) STORED;
CREATE INDEX idx_articles_search ON articles USING GIN (search_vector);
SELECT title FROM articles
WHERE search_vector @@ to_tsquery('english', 'postgresql & indexing')
ORDER BY ts_rank(search_vector, to_tsquery('english', 'postgresql & indexing')) DESC;
to_tsvector on every query works for small tables but re-does the same parsing work every time. A GENERATED ... STORED column keeps a precomputed tsvector in sync automatically on every write, and a GIN index on that column makes the @@ lookup itself fast.