Advanced Full-Text Search in PostgreSQL: Beyond the Basics
PostgreSQL has powerful built-in support for full-text search. While many developers know how to use basic to_tsvector
and to_tsquery
functions, there’s a lot more depth to uncover. In this guide, we’ll explore more advanced features like ranking, prefix matching, weighting, and combining full-text with structured filters.
1. Basic Setup and Indexing
Start by making sure your text content is properly indexed. Here's how to create a GIN index for full-text search:
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
body TEXT,
tsv TSVECTOR
);
UPDATE articles SET tsv = to_tsvector('english', coalesce(title, '') || ' ' || coalesce(body, ''));
CREATE INDEX tsv_idx ON articles USING GIN(tsv);
Use a trigger to keep the tsvector updated automatically:
CREATE FUNCTION articles_tsv_trigger() RETURNS trigger AS $$
BEGIN
NEW.tsv := to_tsvector('english', coalesce(NEW.title, '') || ' ' || coalesce(NEW.body, ''));
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER tsv_update BEFORE INSERT OR UPDATE ON articles
FOR EACH ROW EXECUTE FUNCTION articles_tsv_trigger();
2. Basic Full-Text Query
SELECT * FROM articles
WHERE tsv @@ to_tsquery('english', 'postgres & search');
This matches rows containing both "postgres" and "search".
3. Phrase and Prefix Searching
To search for phrases or prefixes, use:
SELECT * FROM articles
WHERE tsv @@ phraseto_tsquery('english', 'full text search');
For prefix searching:
SELECT * FROM articles
WHERE tsv @@ to_tsquery('english', 'search:*');
4. Ranking Results
Use ts_rank
or ts_rank_cd
to order by relevance:
SELECT *, ts_rank(tsv, to_tsquery('english', 'postgres')) AS rank
FROM articles
WHERE tsv @@ to_tsquery('english', 'postgres')
ORDER BY rank DESC;
5. Weighting Different Fields
You can assign different weights to fields to prioritize title over body, for example:
UPDATE articles
SET tsv = setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'B');
6. Combining With Structured Filters
Full-text search can be combined with other SQL conditions:
SELECT * FROM articles
WHERE tsv @@ to_tsquery('english', 'index')
AND published = true
AND category = 'PostgreSQL';
7. Highlighting Matches
Use ts_headline
to highlight matched terms:
SELECT title, ts_headline('english', body, to_tsquery('english', 'search'))
FROM articles
WHERE tsv @@ to_tsquery('english', 'search');
8. JSON and Full-Text
You can even index and search JSON fields:
CREATE INDEX idx_json_search ON documents
USING GIN (to_tsvector('english', data::text));
Conclusion
PostgreSQL's full-text search is an underutilized powerhouse. When used correctly, it allows you to build scalable, sophisticated search features directly inside your database—no third-party engine needed. Whether you're powering a blog or a large-scale document system, mastering these techniques will take your search functionality to the next level.
If this post helped you, consider supporting me: buymeacoffee.com/hexshift