Most products do not need a search cluster
Reaching for a dedicated search engine is a reasonable instinct and usually a premature one. It is another service to run, another index to keep in sync, and another failure mode, and for a corpus of thousands or even tens of thousands of documents, Postgres full text search is fast, good, and already running.
How it works
Postgres turns text into a tsvector, a normalized list of lexemes with positions, and matches it against a tsquery. Add a generated column for the searchable text and an index on it, and you have ranked search with no new infrastructure.
alter table documents
add column search tsvector
generated always as (
to_tsvector('english', title || ' ' || coalesce(body, ''))
) stored;
create index documents_search_idx on documents using gin (search);
The query ranks results by relevance and, crucially, lets you filter by your ordinary columns in the same statement:
select id, title, url,
ts_rank(search, websearch_to_tsquery('english', $1)) as rank
from documents
where search @@ websearch_to_tsquery('english', $1)
and doc_type = $2
order by rank desc
limit 10;
websearch_to_tsquery accepts the kind of input a user actually types, including quoted phrases and the word "or", so you are not parsing query syntax yourself.
Combine it with semantic search
Full text search nails exact terms, names, and codes. Semantic search over embeddings nails meaning and paraphrase. The strongest results come from running both and merging: keyword search for precision, vector search for recall. Both live in the same database (see the pgvector guide), so combining them is a query, not an integration.
When to graduate
If you reach very large corpora, demanding typo tolerance, faceting, and sub fifty millisecond latency at high query volume, a dedicated engine earns its keep. Until then, the search you already pay for is the search you should ship, and you can always add the engine later without having wasted anything.
