One of the Postgres features our customers ask us for the most is full-text search. Today, we are excited to announce TIN: a fast, full-featured, reliable full-text search extension for Postgres. TIN stands for “Text INdex,” and that is what it does.
TIN is available immediately as a GA release for all Postgres and Neki databases. Check it out:
CREATE INDEX an_index_name ON table_name USING tin(text_column_name);
SELECT * FROM table_name
WHERE text_column_name ==> 'some words';
We built TIN because we believe a good text index should support:
- Boolean expressions, phrase queries, and span queries
- Fuzzy, wildcard, and regular-expression matching for terms
- Case and accent folding
COUNT(*)queries and BM25-scored top-k queries
A good text index in Postgres must support all of those things while also handling joins, complicated WHERE clauses across full-text and other column types, continuous updates, replication, backups, and correct transaction visibility.
Although there are at least three existing text-search indexes for Postgres already, none of them met all of those requirements. TIN does. TIN is also really, mind-blowingly fast.
TIN performance and benchmarking
We ran benchmarks to assess performance for all the above use cases and more. Benchmark results on an 85 GB Stack Exchange corpus (150 million documents):
Index build time and size
| Total time | Index size | Required RAM | |
|---|---|---|---|
| TIN | 8m10s | 50.7 GB | 32 GB |
| ParadeDB | 19m20s | 52.1 GB | 64 GB |
| pg_textsearch | 26m49s | 41.5 GB | 128 GB |
| Postgres GIN | 2h09m04s | 28.0 GB | 64 GB |
Mixed queries, top-10 ranked
TIN handles 25× as many queries per second as ParadeDB does, with p99 latencies 26× lower. GIN can’t complete this benchmark, because it runs out of memory performing the disjunction searches.
Conjunction and phrase queries, top-10 ranked
TIN handles 10× as many queries as ParadeDB and 541× as many as GIN, with p99 latencies 6× and 1,356× lower, respectively.
Disjunction queries with concurrent writes
TIN handles 36× as many queries as pg_textsearch and 57× as many queries as ParadeDB, with p99 latencies 24× and 36× lower, respectively. Over the course of a ten-minute run, TIN completes 270,279 updates, while ParadeDB completes 185,584, and pg_textsearch completes only 735.
When the index fits in memory
On Wikipedia (8.0 GB corpus), TIN achieved 10,260 QPS on disjunction COUNT queries vs ParadeDB’s 291 and GIN’s 1.4.
Why TIN is fast
TIN directly uses Postgres’ ctid value as a document identifier rather than sequential document IDs. This avoids remapping overhead, enables two-level bitmap encoding for 48-bit identifiers, supports vectorized intersection/union on AVX2/AVX-512, and integrates with Postgres MVCC and visibility maps.
TIN returns MVCC-correct results, supports index-only scans when pages are all-visible, and maintains per-segment liveness bitmaps updated by VACUUM.
Segment merging reuses bitmaps without renumbering document IDs, reducing write amplification compared to traditional text indexes.
Summary
TIN is at least 8× faster in every benchmark scenario tested, driven by using ctid as the native format for each posting in the index.