We launched our marketplace with Elasticsearch as the search engine. It was the obvious choice: fast, scalable, and packed with features like fuzzy matching and custom scoring. For a year, it served 50,000 product queries a day without complaint. Then the bill arrived.

Elasticsearch required a dedicated three‑node cluster with 64 GB of RAM each—just to handle the index size and query load. That was fine at first, but as we expanded to new regions, we needed separate clusters for each. Our infrastructure costs ballooned. Worse, the operations team spent 20% of their time managing shards, rolling restarts, and monitoring the JVM heap.

We started asking: do we really need Elasticsearch? Our data was relational, our catalog was only half a million products, and our users mostly searched by product name and description. Maybe PostgreSQL could handle it.

The benchmark that made us question everything

We ran a two‑week spike to compare Elasticsearch against PostgreSQL's built‑in full‑text search. We used the same dataset, the same queries, and measured latency, CPU usage, and developer effort.

Search query latency (p95, over 10,000 runs)

+---------------------------+------------------+-------------------+ | Query type                | Elasticsearch    | PostgreSQL (GIN)  | +---------------------------+------------------+-------------------+ | Single‑term product name  | 45 ms            | 12 ms             | | Multi‑term description    | 120 ms           | 85 ms             | | Fuzzy search (typo)       | 230 ms           | 340 ms            | | Sorting by relevance      | 180 ms           | 150 ms            | | Full scan (no index)      | 2,400 ms         | 4,200 ms          | +---------------------------+------------------+-------------------+

PostgreSQL was faster for most common queries, except fuzzy searches. But fuzzy searches were only 5% of our traffic, and we could work around them with a simple trigram index plus a fallback to Elasticsearch for that edge case.

More importantly, PostgreSQL's query planner used the same index for both search and filtering (category, price range), eliminating the need for separate filters in the application layer.

We saved $4,000/month in infrastructure costs and halved our operational alert fatigue—just by using what was already in our stack.

— Ravi Menon, from the infrastructure review

The migration took three weeks. Here's the approach we used:

  1. Add a generated column for the search vector

    We created a tsvector column that automatically updated when product data changed, using PostgreSQL's GENERATED ALWAYS feature.

    ALTER TABLE products  ADD COLUMN search_vector tsvector  GENERATED ALWAYS AS ( setweight(to_tsvector('english', coalesce(name, '')), 'A') || setweight(to_tsvector('english', coalesce(description, '')), 'B') ) STORED; CREATE INDEX idx_product_search ON products USING GIN (search_vector);

  2. Rewrite the search query using plainto_tsquery

    We replaced the Elasticsearch client with a simple SQL query that uses plainto_tsquery and ranks results by ts_rank.

  3. Add trigram index for typo‑tolerant search

    For the 5% of queries that needed fuzzy matching, we installed the pg_trgm extension and used % similarity operator.

  4. Cache popular search results

    We added a Redis cache for the top 1,000 search terms, which further reduced latency to under 5 ms for 80% of requests.

What we gave up (and what we gained)

We lost Elasticsearch's advanced analytics (aggregations, faceted search) and its ability to handle massive scale. But we gained:

  • Simplicity – one fewer distributed system to operate.
  • Consistency – search results were always in sync with the database, no indexing lag.
  • Lower latency – because we eliminated the network hop to Elasticsearch.

Worth noting: this decision is only viable if your dataset fits comfortably in PostgreSQL (under a few million rows) and your search needs are predominantly full‑text, not analytical. For large‑scale, multi‑language, or log‑based search, Elasticsearch is still the right tool.

The takeaway

Six months after the migration, we haven't looked back. Our infrastructure bill is down 60%, the team is happier, and the users haven't noticed any difference—except that searches feel snappier. We still keep an Elasticsearch cluster for our analytics pipeline, but for product search, PostgreSQL full‑text search is more than enough.

The lesson: don't assume you need a dedicated search engine just because it's the standard. Sometimes the database you already have can do the job, and it does it with less complexity and lower cost.