Skip to content
ExponaBLOG
ELASTICSEARCH & SEARCH ENGINEERING · DAY 064 / 100

Elasticsearch vs PostgreSQL full-text search in practice

When do you need a search engine?

Expona Admin7 min read

When do you need a search engine?

Most teams reach for Elasticsearch the moment they need to search. Most teams are wrong. We've shipped search features on both PostgreSQL and Elasticsearch across e-commerce, SaaS and ride-hailing platforms, and the honest answer is that Elasticsearch vs PostgreSQL full-text search in practice depends on whether your search is a core product feature or a nice-to-have utility. Let's walk through when each makes sense, what you actually gain, and the hidden costs nobody talks about.

PostgreSQL's full-text search (FTS) is legitimately powerful. Most teams don't push it hard enough before jumping to Elasticsearch. When you need to search a product catalog, user profiles, or document collections with basic relevance, Postgres tsvector and GIN indexes do real work.

Here's how it works: you store a tsvector column that holds tokenized, stemmed versions of your searchable text. A GIN index makes lookups fast.

sql
CREATE TABLE products (
  id BIGSERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  description TEXT,
  search_vector tsvector GENERATED ALWAYS AS (
    setweight(to_tsvector('english', coalesce(name, '')), 'A') ||
    setweight(to_tsvector('english', coalesce(description, '')), 'B')
  ) STORED
);

CREATE INDEX idx_products_search ON products USING GIN(search_vector);

SELECT id, name, ts_rank(search_vector, query) as rank
FROM products, plainto_tsquery('english', 'running shoes') as query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;

This query finds products matching "running shoes", ranks them by relevance, and returns top 20. The GIN index makes it sub-millisecond on millions of rows. You get:

  • Automatic stemming (run, running, runs all match)
  • Phrase queries and boolean operators
  • Relevance ranking via ts_rank
  • No additional infrastructure
  • Single source of truth (search lives in your main database)

The trade-off is shallow. You can't easily do typo tolerance. Faceted search (count by category, price range, etc.) requires separate queries. Boosting individual fields works but feels manual. Sorting by relevance while filtering by structured fields gets verbose. But for a team with 2-3 million products and users searching a few hundred times per second, Postgres FTS absolutely works.

Elasticsearch — Better relevance, facets, typos and scale

Elasticsearch enters when search becomes a product differentiator. Your e-commerce site lives or dies by search quality. Your job board needs fuzzy matching so "Photsop" still finds Photoshop jobs. Your dashboard needs to aggregate metrics across 100 million log entries in 200ms.

Elasticsearch excels because it's built for search first. It offers:

  • Typo tolerance. Fuzzy queries and phonetic analyzers handle misspellings without manual rules.
  • Facets and aggregations. Count and filter by category, price bucket, location, tags all in one query response.
  • Relevance tuning at scale. Boost exact matches, synonyms, custom analyzers, query-time field weighting—all declarative, no code.
  • Full-text analyzers. Language-specific stemming, stop words, character filtering, synonyms configured once and reused.
  • Horizontal scale. Shard your index across nodes. Elasticsearch handles rebalancing and failover.

Here's a realistic search query on an e-commerce index:

json
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "running shoe",
            "fields": ["name^2", "description", "tags"],
            "fuzziness": "AUTO"
          }
        }
      ],
      "filter": [
        { "term": { "status": "active" } },
        { "range": { "price": { "gte": 50, "lte": 200 } } }
      ]
    }
  },
  "aggs": {
    "brands": { "terms": { "field": "brand.keyword", "size": 10 } },
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 50 }, { "from": 50, "to": 100 }, { "from": 100 }
        ]
      }
    }
  },
  "size": 20
}

One request. You get ranked results, filtered by active status and price range, with facet counts for brands and price buckets. The fuzzy matching catches typos. Field boosting (name^2) prioritizes exact matches in product names. This query runs on billions of documents in milliseconds.

Try building that in Postgres. You'll write separate queries for search, filters, and each facet. You'll manage multiple indexes. Fuzzy matching requires extension code or application logic. Your query planner has to work harder. It's doable but painful.

Start simple — Use Postgres until search becomes a product feature

The principle we follow: don't add systems until you're forced to. Postgres FTS is part of your existing database. It requires no deployment, no capacity planning, no new monitoring dashboards. If your search latencies are under 100ms and your users aren't complaining about typos or relevance, Postgres is the right tool.

Real scenario: a SaaS company with a user directory. 50,000 users, 100 searches per day. Postgres tsvector with a GIN index handles it perfectly. The engineering team owns one database, one monitoring stack, one backup strategy. Search latency is 2-5ms. Users type slowly enough that typo tolerance doesn't matter.

Then the product team asks: "Can we show facets? Users want to filter by department and location while searching." That's still Postgres. Add a few WHERE clauses, count(*) GROUP BY. Latency creeps to 10-20ms but it's fine.

Then: "Typo tolerance. Our users are mobile. They fat-finger searches." Now you're building fuzzy matching in Postgres—substring matching, Levenshtein distance, trigrams. Queries get complex. Indexes multiply. You're at 50-100ms per search and adding application-side caching logic.

That's when we evaluate Elasticsearch. Not before. The jump is only worth it if:

  • Search latency requirements are sub-50ms at high volume
  • Relevance tuning is now part of product roadmap
  • You're indexing millions of documents and growth is steep
  • Typo tolerance and advanced tokenization are table stakes
  • Faceted navigation is expected UX

If none of those apply, you're just adding operational debt.

Operational cost — Elasticsearch is another system to run

This is the part sales engineers gloss over. Elasticsearch requires:

  • Capacity planning. How much disk? How many nodes? How many replicas? Get it wrong and you're either out of disk or wasting money.
  • Indexing pipeline. Data flows from your application into Elasticsearch. You need to handle failures, retries, backpressure, and eventual consistency.
  • Version upgrades. Major versions introduce breaking changes to query syntax and configuration. You'll plan upgrades quarterly and test them thoroughly.
  • Monitoring and alerting. Disk usage, heap memory, GC pauses, query latency, indexing lag, shard imbalance. New operational surface area.
  • Cost. On AWS, a three-node cluster with replication costs $3,000-5,000 per month. That's before data transfer, snapshots, and cluster management tooling.
  • Search quality maintenance. Typo tolerance, synonyms, field boosts, analyzer tuning—these need ongoing review and adjustment as your domain evolves.

We've seen teams spin up Elasticsearch for 10,000 searches per day, then let it run idle for months between configuration tweaks. The infra team manages it because it exists, not because it's worth managing. That's sunk cost thinking.

Build monitoring before you build the cluster. Know what you're optimizing for. If it's "users complained one time about search", Postgres probably fixes it. If it's "our search metrics went up 15% after we improved relevance and now it's a competitive advantage", Elasticsearch justifies itself.

Common mistakes

Mistake 1: Elasticsearch for scale you don't have. A team with 500K documents and 50 searches per day thinks they need Elasticsearch because a blog post said so. Postgres does this effortlessly. Don't inherit complexity you don't need.

Mistake 2: Not tuning Postgres before abandoning it. Skipping GIN indexes, not using tsvector, running full table scans. Postgres FTS is fast when configured right. Measure before you migrate.

Mistake 3: Elasticsearch as a crutch for bad data. Misspelled product names, inconsistent categorization, duplicate records. Elasticsearch will surface these problems more obviously than Postgres. Fix data quality first, then choose your search engine.

Mistake 4: Forgetting the index lag problem. With Elasticsearch, your index is slightly stale. A product gets added, but it might not be searchable for seconds or minutes. If your use case requires searches to immediately find new items, that gap is a real problem. Postgres reads your live data.

Mistake 5: Running Elasticsearch without a team. If nobody's responsible for the cluster, updates lag, configuration drifts, and it becomes a black box that nobody's willing to touch. Ensure search is owned by a team that will maintain it.

Key takeaways

  • PostgreSQL full-text search with tsvector and GIN indexes is a complete solution for millions of documents and hundreds of queries per second. Use it as your default.
  • Move to Elasticsearch when relevance tuning, typo tolerance, faceted navigation, or latency at massive scale become product requirements—not before.
  • Elasticsearch is another system. Budget for infrastructure management, monitoring, upgrades, and data pipeline complexity.
  • Start simple. Measure your current search performance. If Postgres can deliver your requirements, it wins on operational simplicity and cost.
  • Typo tolerance and fuzzy matching are valuable but Elasticsearch isn't the only way to get them. Evaluate Postgres extensions and application-layer solutions first.
  • Faceted search is easier in Elasticsearch but doable in Postgres with careful query design and caching. Your SLA determines what's worth the effort.
  • Index lag is real. If your product demands immediate searchability, Postgres's consistency model is a feature, not a limitation.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles