Do You Even Need Elasticsearch? Postgres FTS, Meilisearch and Typesense
The honest tour by operational weight: Postgres tsvector with trigrams, the search-box specialists with superb typo tolerance, and the real signals that justify the heavyweight cluster.
Confession from someone who just wrote three posts about Elasticsearch: most applications that run Elasticsearch shouldn't. Not because it's bad — because it's a distributed system you now operate, and "we needed product search for 40,000 SKUs" never justified operating a distributed system. The search landscape has excellent smaller answers now. Here's the honest tour, ordered by how little machinery each one adds.
Level 0: the database you already run
Both big relational engines do genuine full-text search, and "no new infrastructure" is a feature that beats most other features.
Postgres FTS is the strong one: tsvector/tsquery with real language stemming, ranking (ts_rank), highlighting, and — the modern trick — pair it with pg_trgm trigram indexes for the typo-tolerant/partial matching FTS lacks:
ALTER TABLE products ADD COLUMN search tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('simple', coalesce(title, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(description, '')), 'B')
) STORED;
CREATE INDEX products_search_idx ON products USING GIN (search);
SELECT id, title, ts_rank(search, query) AS rank
FROM products, websearch_to_tsquery('simple', 'kahve makinesi') query
WHERE search @@ query
ORDER BY rank DESC LIMIT 20;
Everything stays transactional — no sync pipeline, no drift, no reconciliation cron, and your filters are just WHERE clauses. Honest limits: relevance tuning is coarse next to BM25 done well, Turkish stemming support is thinner than English (test with your real data; simple + trigrams is often the pragmatic Turkish answer), and complex faceting gets verbose. MySQL FULLTEXT exists and is serviceable for basic needs, several rungs below Postgres. My rule: under ~a million searchable rows and "good search" rather than "search is the product" — start here, seriously.
Level 1: the search-box specialists — Meilisearch & Typesense
Two single-binary engines built around one insight: most apps need a fantastic search box, not an analytics platform. Both give you typo tolerance that works out of the box (this is the headline — Elasticsearch fuzziness is a knob; here it's the default and it's good), sub-50ms as-you-type latency, faceting, and a setup experience measured in minutes. Both have first-class Laravel Scout drivers, making them drop-in: SCOUT_DRIVER=meilisearch and your Scout write path just works.
Differences at the margins: Meilisearch has the most polished DX and a ranking-rules model that's easy to reason about; Typesense is arguably faster at scale, offers a clustered HA story, and its schema-first strictness suits teams who like contracts. Both fall short of Elasticsearch on: heavy aggregations, log/analytics workloads, complex per-field language analysis, and datasets past the low hundreds of GB. For "products/articles/docs search with facets and typos handled", either is not a compromise — it's a better fit than Elasticsearch, with one process to run instead of a cluster to husband.
Level 2: Elasticsearch/OpenSearch — when it's genuinely the answer
The heavyweight earns its keep on real signals: log/observability pipelines at volume (this is where ILM, tiers and the whole operational apparatus pay off), aggregation-heavy analytics over big datasets, search as the product with deep per-language analysis and relevance engineering, data volumes that need sharding rather than a bigger box, and vector+keyword hybrid retrieval at serious scale. If two or more of those describe you, welcome — the three posts preceding this one are yours. If none do, the cluster is a lifestyle you're subsidizing.
The decision table I actually use with clients
"Search should just not be embarrassing"
→ Postgres FTS (+ pg_trgm). Ship this week. Revisit if users complain.
"Search box is a core UX surface" (e-commerce, docs, marketplace)
→ Meilisearch or Typesense. Scout driver, one binary, superb defaults.
"Logs/metrics/analytics" or "search IS the product" or ">100s of GB"
→ Elasticsearch/OpenSearch, with the ops budget acknowledged out loud.
"AI/RAG semantic retrieval"
→ different question entirely: pgvector for modest scale, dedicated
vector stores beyond — a future post of its own.
Two closing principles. Migration between levels is cheap if you keep the seam — a ProductSearch interface in front of whatever engine (the same seam logic as ports at real boundaries) means outgrowing Postgres FTS is a driver swap, not a rewrite. And choose by the workload you have, not the one that flatters you — the résumé wants Elasticsearch; the product, more often than anyone admits, wants a GIN index and a Friday evening off.
Genuinely unsure which level you're at? That's a one-hour conversation with your query logs, and it usually ends cheaper than it started.