PostgreSQL ships native full-text search with tsvector, tsquery, and GIN indexes. For many products it replaces a separate Elasticsearch cluster when data volume, ranking needs, and operational headcount favor one datastore.
-- Minimal searchable documents tableCREATE TABLE articles ( id bigserial PRIMARY KEY, title text NOT NULL, body text NOT NULL, published timestamptz DEFAULT now());-- Generated tsvector column (PostgreSQL 12+)ALTER TABLE articles ADD COLUMN search_vector tsvector GENERATED ALWAYS AS ( setweight(to_tsvector('english', coalesce(title, '')), 'A') || setweight(to_tsvector('english', coalesce(body, '')), 'B') ) STORED;CREATE INDEX articles_search_idx ON articles USING gin (search_vector);-- SearchSELECT id, title, ts_rank(search_vector, query) AS rankFROM articles, plainto_tsquery('english', 'postgresql replication') queryWHERE search_vector @@ queryORDER BY rank DESCLIMIT 20;
When to reach for this: Full-text over relational data, moderate corpus size (millions of rows), and team wants transactional consistency without dual-write to a search engine.
SELECT cfgname FROM pg_ts_config;-- common: english, simple, frenchSELECT to_tsvector('simple', 'Running runs RUN');-- simple: no stemming, good for product SKUs and codes
Use simple for identifiers; use english (or locale-specific) for prose.
Is Postgres FTS good enough for SaaS search?Often yes up to millions of documents with GIN and proper weights. Benchmark p95 before adopting Elasticsearch.plainto_tsquery vs websearch_to_tsquery?`plainto_tsquery` ANDs terms. `websearch_to_tsquery` supports quotes and minus terms like web search boxes.Do I need Elasticsearch for autocomplete?Not always. `pg_trgm` on prefix queries or materialized prefix tables work for moderate traffic.How big can GIN indexes get?Roughly comparable to corpus token count. Monitor with `pg_relation_size`.Multi-language content?Use `simple` config plus language column, or per-language `tsvector` columns.Highlighting in API?`ts_headline` in SQL or return positions via `tsvector` parsing in application.Replication lag impact?FTS reads local indexes on replica; no special lag beyond normal streaming replication.Managed Postgres FTS limits?Same engine; check extension allowlist for `unaccent`. No Elasticsearch-style managed sibling on RDS.Security?FTS inherits RLS policies on base table. No separate search ACL layer.Upgrade notes PG 18?Core FTS stable; watch `websearch_to_tsquery` behavior in release notes for parser tweaks.