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