Skip to content
Search

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.

4 min read Updated Sep 2, 2026
Do You Even Need Elasticsearch? Postgres FTS, Meilisearch and Typesense

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.

Postgres FTS, Meilisearch, Typesense and Elasticsearch arranged by capability and operational cost

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.

Keep reading

Related articles

Search 4 min read

Elasticsearch from the Inverted Index Up

The three machines under the API: inverted indexes (why search is fast and updates are weird), analyzers with a Turkish-aware example, permanent mappings, and BM25 relevance in one honest paragraph.