Full-Text Search: PostgreSQL tsvector, Elasticsearch, and Meilisearch Compared

Three Approaches, One Problem

Full-text search seems simple until you try to implement it. Users expect Google-quality results from a search box that queries your application's data. The gap between WHERE title LIKE '%search term%' and a proper full-text search engine is enormous — relevance ranking, stemming, typo tolerance, faceted filtering, and sub-50ms response times.

There are three main approaches: use what your database already has (PostgreSQL's built-in tsvector), deploy a dedicated search engine (Elasticsearch), or use a newer lightweight alternative (Meilisearch). Each hits a different sweet spot.

PostgreSQL tsvector: Good Enough for Most

PostgreSQL's full-text search is surprisingly capable. It handles stemming, ranking, phrase matching, and boolean queries. And since it's built into your existing database, there's no additional infrastructure to manage.

JavaScript Runtime Comparison
Runtime Cold Start HTTP Throughput TypeScript
Node.js 22~50 ms~85k req/sVia tsx/ts-node
Deno 2~30 ms~120k req/sBuilt-in
Bun 1.x~10 ms~260k req/sBuilt-in
Cloudflare Workers0 ms (pre-warm)N/A (edge)Via Wrangler
-- Add a tsvector column and GIN index
ALTER TABLE articles ADD COLUMN search_vector tsvector;

UPDATE articles SET search_vector =
 setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
 setweight(to_tsvector('english', coalesce(excerpt, '')), 'B') ||
 setweight(to_tsvector('english', coalesce(body, '')), 'C');

CREATE INDEX idx_articles_search ON articles USING gin(search_vector);

-- Keep it updated with a trigger
CREATE FUNCTION update_search_vector() RETURNS trigger AS $$
BEGIN
 NEW.search_vector :=
 setweight(to_tsvector('english', coalesce(NEW.title, '')), 'A') ||
 setweight(to_tsvector('english', coalesce(NEW.excerpt, '')), 'B') ||
 setweight(to_tsvector('english', coalesce(NEW.body, '')), 'C');
 RETURN NEW;
END $$ LANGUAGE plpgsql;

CREATE TRIGGER trig_search_vector
 BEFORE INSERT OR UPDATE ON articles
 FOR EACH ROW EXECUTE FUNCTION update_search_vector();

The weight assignments (A, B, C, D) let you rank title matches higher than body matches. Searching:

SELECT title, ts_rank(search_vector, query) AS rank
FROM articles, plainto_tsquery('english', 'postgresql performance') AS query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;

Strengths: No extra infrastructure, ACID consistency with your data, joins with other tables in the same query, decent performance up to a few million documents.

This connects to the ideas in Debugging Production Issues: Logging, Tracing, and Reproduct.

Limitations: No typo tolerance, limited language support compared to Elasticsearch, no built-in autocomplete/suggestions, ranking algorithm is basic.

For an internal tool, a blog, or a SaaS app with under a million searchable documents, PostgreSQL full-text search is the right starting point. You can always migrate to a dedicated engine later.

Elasticsearch: The Enterprise Choice

Elasticsearch is built on Apache Lucene and is the industry standard for full-text search at scale. It handles billions of documents, supports complex aggregations, and has a rich query DSL.

The tradeoff: it's a separate distributed system that needs its own cluster, monitoring, and expertise. Elasticsearch clusters require careful capacity planning, and misconfigurations can lead to slow queries or cluster instability.

For a related perspective, see Microservices vs Monolith in 2026: When to Choose What.

Strengths: Extremely powerful query language, typo tolerance (fuzzy matching), faceted search, aggregations, scales horizontally to billions of docs, extensive language analyzers.

Weaknesses: Operational complexity (JVM tuning, shard management, rolling upgrades), eventual consistency (there's a refresh interval before new documents are searchable, default 1 second), expensive memory requirements (plan for 50% of your index size as heap).

Use Elasticsearch when: you're indexing more than 10 million documents, you need complex aggregations for analytics, you're building faceted search for an e-commerce catalog, or you need features like percolation queries, geospatial search, or ML-based relevance tuning.

Meilisearch: The Developer-Friendly Alternative

Meilisearch positions itself as the "search Elasticsearch could've been if it were designed for developers." It's a single binary written in Rust, requires near-zero configuration, and provides typo-tolerant search out of the box.

See also: State Management in React 2026: Zustand, Jotai, Redux Toolki.

# Start Meilisearch
./meilisearch --master-key="your-secret-key"

# Index documents
curl -X POST 'http://localhost:7700/indexes/articles/documents' -H 'Content-Type: application/json' -H 'Authorization: Bearer your-secret-key' --data-binary @articles.json

# Search (with typo tolerance, instant)
curl 'http://localhost:7700/indexes/articles/search' -H 'Authorization: Bearer your-secret-key' --data '{"q": "postgrsql perfomance", "limit": 10}'
 # Note the typos — Meilisearch handles them

Strengths: Typo tolerance by default, instant search (typically under 50ms), dead simple setup, excellent documentation, built-in filtering and faceting, prefix search works great for autocomplete.

Weaknesses: Single-node only (no clustering), the entire index must fit in RAM, limited query expressiveness compared to Elasticsearch, no aggregation pipeline.

Meilisearch is ideal for: user-facing search boxes, product catalogs under 10 million documents, documentation search, and any case where developer experience and time-to-deploy matter more than raw scalability.

Quick Comparison

For a dataset of 500,000 documents with typical web content:

  • PostgreSQL tsvector — search latency ~10-50ms, no typo tolerance, zero additional infrastructure
  • Meilisearch — search latency ~5-20ms, typo tolerant, needs one extra process
  • Elasticsearch — search latency ~5-30ms, typo tolerant, needs a cluster (minimum 3 nodes for production)

For most web applications, start with PostgreSQL. If you need typo tolerance or instant-search UX, try Meilisearch. Reach for Elasticsearch when you need its specific capabilities at scale.

References & Standards: WebAssembly specification · Node.js documentation