ParadeDB vs Postgres FTS

ParadeDB is Postgres, so this comparison is really about the search machinery. Postgres ships tsvector and GIN, and that is exactly what every managed Postgres (RDS, Cloud SQL, Azure, Supabase, Neon) bundles as search. ParadeDB replaces that machinery with a dedicated search engine living in the same database.

Benchmark

Latency and throughput

ParadeDB (pg_search 0.25.6 on Postgres 18) ran against stock Postgres 18 full-text search in its best case: stored generated tsvector columns, a GIN index on each, and btree indexes for the filters.

Both engines ran on identical hardware, four pinned CPUs and 8 GB of memory each, over the full 28.7-million-row Hacker News dataset, queried through pgbouncer in transaction pooling mode.

Every workload below ran for 30 seconds against a rotating pool of 40 query terms, after an identical 30-second warmup, and any query that took longer than 30 seconds was cancelled.

01Top K search
Field
Top K
Terms
Clients
ParadeDB
SELECT id, title, by, score
FROM hn_items
WHERE title ||| 'rust'
ORDER BY pdb.score(id) DESC
LIMIT 10
Postgres FTS
SELECT id, title, by, score
FROM hn_items
WHERE title_tsv @@
websearch_to_tsquery('english', 'rust')
ORDER BY ts_rank_cd(title_tsv,
websearch_to_tsquery('english', 'rust')) DESC
LIMIT 10
Latency · lower is better
ParadeDBPostgres FTS
p50
64×
2.5 ms
160 ms
p95
200×
3.0 ms
601 ms
Throughput
412 QPS5 QPS
02Filtered search
Filter
Clients
ParadeDB
SELECT id, title, by, score
FROM hn_items
WHERE text ||| 'rust'
AND score > 10
ORDER BY pdb.score(id) DESC
LIMIT 10
Postgres FTS
SELECT id, title, by, score
FROM hn_items
WHERE text_tsv @@
websearch_to_tsquery('english', 'rust')
AND score > 10
ORDER BY ts_rank_cd(text_tsv,
websearch_to_tsquery('english', 'rust')) DESC
LIMIT 10
Latency · lower is better
ParadeDBPostgres FTS
p50
18.0 ms
161 ms
p95
11×
28.7 ms
327 ms
Throughput
58.4 QPS5.9 QPS
03Count over search
Field
Clients
ParadeDB
SELECT count(*) FROM hn_items
WHERE title ||| 'rust'
Postgres FTS
SELECT count(*) FROM hn_items
WHERE title_tsv @@
websearch_to_tsquery('english', 'rust')
Latency · lower is better
ParadeDBPostgres FTS
p50
3.9×
17.2 ms
67.9 ms
p95
8.9×
31.1 ms
278 ms
Throughput
56.3 QPS9.4 QPS
04Faceting
Facet
Clients
ParadeDB
SELECT id,
pdb.agg('{"terms": {"field": "type"}}') OVER ()
FROM hn_items
WHERE text ||| 'rust'
ORDER BY pdb.score(id) DESC
LIMIT 10
Postgres FTS
WITH hits AS (
SELECT id, type,
ts_rank_cd(text_tsv, q) r
FROM hn_items,
websearch_to_tsquery(
'english', 'rust') q
WHERE text_tsv @@ q
)
SELECT
-- pass 1: the facet buckets
(SELECT jsonb_object_agg(k, c)
FROM (SELECT type k,
count(*) c FROM hits
GROUP BY 1) t),
-- pass 2: the top 10 hits
(SELECT jsonb_agg(h)
FROM (SELECT * FROM hits
ORDER BY r DESC LIMIT 10) h)
Latency · lower is better
ParadeDBPostgres FTS
p50
20×
44.6 ms
884 ms
p95
39×
79.5 ms
3.1 s
Throughput
21.9 QPS1 QPS
05Highlighting
Field
Clients
ParadeDB
SELECT id, title, pdb.snippet(title)
FROM hn_items
WHERE title ||| 'rust'
ORDER BY pdb.score(id) DESC
LIMIT 10
Postgres FTS
SELECT id, title,
ts_headline('english', title,
websearch_to_tsquery('english', 'rust'))
FROM hn_items
WHERE title_tsv @@ websearch_to_tsquery('english', 'rust')
ORDER BY ts_rank_cd(title_tsv,
websearch_to_tsquery('english', 'rust')) DESC
LIMIT 10
Latency · lower is better
ParadeDBPostgres FTS
p50
33×
2.8 ms
92.0 ms
p95
115×
3.2 ms
369 ms
Throughput
366.8 QPS7.3 QPS
06Vector search
Coming soon
ParadeDB
SELECT id, title
FROM hn_items
ORDER BY embedding <=> $query_vec
LIMIT 10

Dense and sparse vector search, plus hybrid BM25 and vector ranking, all served from the same index. We're adding these workloads to the benchmark next.

Download the result dataparadedb/benchmarker

Full results table · all workloads at every concurrency
One row per workload, concurrency, and metric. Latency is in milliseconds; throughput is completed queries per second. All figures come from the downloadable data.
WorkloadFieldDetailClientsMetricParadeDBPostgres FTS
Top Ktitle1 term · top 101p50 latency2.5 ms160 ms
Top Ktitle1 term · top 101p99 latency3.1 ms1.1 s
Top Ktitle1 term · top 101throughput (QPS)4125
Top Ktitle1 term · top 104p50 latency3.1 ms112 ms
Top Ktitle1 term · top 104p99 latency3.9 ms627 ms
Top Ktitle1 term · top 104throughput (QPS)135424
Top Ktitle1 term · top 108p50 latency4.9 ms148 ms
Top Ktitle1 term · top 108p99 latency7.6 ms978 ms
Top Ktitle1 term · top 108throughput (QPS)165636
Top Ktext1 term · top 101p50 latency2.8 ms712 ms
Top Ktext1 term · top 101p99 latency3.6 ms2.9 s
Top Ktext1 term · top 101throughput (QPS)3481
Top Ktext1 term · top 104p50 latency3.3 ms3.3 s
Top Ktext1 term · top 104p99 latency4.6 ms12.6 s
Top Ktext1 term · top 104throughput (QPS)11801
Top Ktext1 term · top 108p50 latency5.8 ms6.8 s
Top Ktext1 term · top 108p99 latency9.2 ms20.2 s
Top Ktext1 term · top 108throughput (QPS)14011
Top Ktext10 terms · top 101p50 latency30.9 ms>30 s
Top Ktext10 terms · top 101p99 latency79.3 ms>30 s
Top Ktext10 terms · top 101throughput (QPS)280
Top Ktext10 terms · top 104p50 latency32.1 ms>30 s
Top Ktext10 terms · top 104p99 latency79.0 ms>30 s
Top Ktext10 terms · top 104throughput (QPS)1130
Top Ktext10 terms · top 108p50 latency64.3 ms>30 s
Top Ktext10 terms · top 108p99 latency174 ms>30 s
Top Ktext10 terms · top 108throughput (QPS)1130
Filteredtextscore > 101p50 latency18.0 ms161 ms
Filteredtextscore > 101p99 latency33.5 ms351 ms
Filteredtextscore > 101throughput (QPS)586
Filteredtextscore > 104p50 latency17.4 ms173 ms
Filteredtextscore > 104p99 latency33.7 ms377 ms
Filteredtextscore > 104throughput (QPS)23522
Filteredtextscore > 108p50 latency30.3 ms354 ms
Filteredtextscore > 108p99 latency79.3 ms805 ms
Filteredtextscore > 108throughput (QPS)23922
Filteredtexttype = 'story'1p50 latency9.0 ms686 ms
Filteredtexttype = 'story'1p99 latency27.7 ms923 ms
Filteredtexttype = 'story'1throughput (QPS)982
Filteredtexttype = 'story'4p50 latency9.3 ms735 ms
Filteredtexttype = 'story'4p99 latency28.7 ms1.1 s
Filteredtexttype = 'story'4throughput (QPS)3776
Filteredtexttype = 'story'8p50 latency18.4 ms1.6 s
Filteredtexttype = 'story'8p99 latency57.1 ms2.4 s
Filteredtexttype = 'story'8throughput (QPS)3945
Counttitle1p50 latency17.2 ms67.9 ms
Counttitle1p99 latency37.7 ms529 ms
Counttitle1throughput (QPS)569
Counttitle4p50 latency19.5 ms84.6 ms
Counttitle4p99 latency41.5 ms563 ms
Counttitle4throughput (QPS)20432
Counttitle8p50 latency32.3 ms99.8 ms
Counttitle8p99 latency84.4 ms649 ms
Counttitle8throughput (QPS)21856
Counttext1p50 latency37.6 ms378 ms
Counttext1p99 latency75.2 ms2.1 s
Counttext1throughput (QPS)272
Counttext4p50 latency38.8 ms1.2 s
Counttext4p99 latency75.7 ms4.9 s
Counttext4throughput (QPS)1033
Counttext8p50 latency72.1 ms1.6 s
Counttext8p99 latency161 ms5.9 s
Counttext8throughput (QPS)1064
Facetingtextterms1p50 latency44.6 ms884 ms
Facetingtextterms1p99 latency93.4 ms3.2 s
Facetingtextterms1throughput (QPS)221
Facetingtextterms4p50 latency44.9 ms2.1 s
Facetingtextterms4p99 latency93.4 ms8.3 s
Facetingtextterms4throughput (QPS)892
Facetingtextterms8p50 latency85.2 ms4.2 s
Facetingtextterms8p99 latency204 ms27.9 s
Facetingtextterms8throughput (QPS)892
Facetingtexthistogram1p50 latency51.3 ms975 ms
Facetingtexthistogram1p99 latency106 ms3.2 s
Facetingtexthistogram1throughput (QPS)191
Facetingtexthistogram4p50 latency56.3 ms1.5 s
Facetingtexthistogram4p99 latency110 ms13.7 s
Facetingtexthistogram4throughput (QPS)732
Facetingtexthistogram8p50 latency93.5 ms4.0 s
Facetingtexthistogram8p99 latency235 ms16.0 s
Facetingtexthistogram8throughput (QPS)802
Highlightingtitle1p50 latency2.8 ms92.0 ms
Highlightingtitle1p99 latency3.5 ms500 ms
Highlightingtitle1throughput (QPS)3677
Highlightingtitle4p50 latency3.2 ms106 ms
Highlightingtitle4p99 latency4.1 ms636 ms
Highlightingtitle4throughput (QPS)126126
Highlightingtitle8p50 latency5.4 ms171 ms
Highlightingtitle8p99 latency8.3 ms964 ms
Highlightingtitle8throughput (QPS)152334
Highlightingtext1p50 latency3.2 ms729 ms
Highlightingtext1p99 latency3.9 ms2.9 s
Highlightingtext1throughput (QPS)3071
Highlightingtext4p50 latency3.8 ms2.2 s
Highlightingtext4p99 latency4.9 ms8.1 s
Highlightingtext4throughput (QPS)10402
Highlightingtext8p50 latency6.8 ms5.0 s
Highlightingtext8p99 latency10.2 ms16.8 s
Highlightingtext8throughput (QPS)12201
Comparison

How they differ

Stock FTS is real search: parsing, stemming, indexes. The gap opens at ranking quality, and at how each side executes Top K, filters, and counts once the corpus stops being small.

 ParadeDBPostgres FTS
Index structureOne index: a segmented inverted index (Tantivy), columnar storage, and a SPANN-style IVF vector indexGIN posting trees over tsvector, plus a pending list; vectors need a separate pgvector index
Relevance scoringBM25: corpus-wide IDF, term saturation, length normalizationts_rank / ts_rank_cd: local frequency only, no corpus statistics
Top K executionScore-ordered iterator with block-max WAND: skips matches that can't reach the top k, so work stays sublinear in match countNo score order, so bitmap every match, ts_rank each from the heap, sort, then LIMIT: cost tracks match count
Semantic searchDense and sparse vectors indexed and scored alongside BM25, on the same live rowsFTS is lexical only; pgvector is a separate extension and index to add, sync, and tune
Filtered searchNumbers, dates, and literals are stored columnar and queried in the same index scanGIN and btree via BitmapAnd, then rank whatever survives
Hybrid rankingVector and BM25 scored in a single index pass, fused with RRF (native RRF coming soon)pgvector and GIN as two separate index scans, then RRF-fused
count(*) over a matchAnswered from the indexNo index-only scans on GIN: every counted row touches the heap
TokenizationPer-field tokenizers: ICU, ngram, CJK, stemmers, customOne parser; dictionaries for stemming, stopwords, synonyms
Query surfaceMatch, phrase, fuzzy, boost, regex, and range operators in SQLtsquery: & | ! <-> plus four weight classes (A–D)
Highlightingpdb.snippet() from indexed positionsts_headline() re-parses each document at query time
Document limitsNo practical capstsvector: 1MB per row, positions clamped at 16,383
Write pathMVCC; segments merge in the background (LSM-style), with an optional in-memory mutable segment to absorb high write ratesMVCC; GIN fastupdate pending list, flushed on the unlucky write
Where it runsParadeDB Cloud, BYOC, or self-hosted extensionEverywhere: bundled in RDS, Cloud SQL, Azure, Supabase, Neon