COMPARE · COMPARISON: DATA FILES

The best local full-text search for JSONL and CSV files in 2026, with a published benchmark

AUTHOR · XERJ documentation team · REVIEWED BY · XERJ engineering team · PUBLISHED · 2026-10-19 · UPDATED · 2026-10-19
SHORT ANSWER · UPDATED 2026-10-19

What is the best local full-text search for JSONL and CSV files in 2026? For one file, SQLite FTS5: in our benchmark it indexed 50,000 JSONL records in 0.29 seconds and answered in under 0.1 ms. ripgrep needs no index, DuckDB adds SQL, and XERJ was slowest to index and ran out of memory at 300,000.

Key number: On 50,000 JSONL records (9.7 MB), SQLite FTS5 built its index in 0.29 s; XERJ v1.0.0-rc.97 took 70.57 s and peaked at 9,578,548 kB resident. (as of 2026-10-19 · source: benchmarks/best-of-jsonl-search-2026/README.md)

Last checked: 2026-10-19. Every third-party fact on this page was re-read from the tool's own docs or repository on that date.

TL;DR — For one JSONL or CSV file, an embedded index wins: SQLite FTS5 indexed 50,000 records in 0.29 s and answered in hundredths of a millisecond. ripgrep needs no index at all. XERJ returned the same counts but took 70.57 s to index, peaked at about 9.1 GiB, and was killed for running out of memory at 300,000 records.

PASTE THIS TO YOUR AI AGENT — IT DOES THE REST Act as a coding agent. Read https://xerj.org/llms.txt, then pick a local full-text search tool for the JSONL and CSV files in this folder from the table on this page: ripgrep for one-off scans, SQLite FTS5 for a small embedded index, DuckDB if you also need SQL analytics, XERJ for many questions across mixed files through MCP. Re-run the benchmark in benchmarks/best-of-jsonl-search-2026 if you need numbers for your own machine.

Pick one in ten seconds

Summary table

ToolLicenceInstallIndexRanksMeasured on 50,000 records (2026-10-19)Stars · latest release (2026-10-19)
ripgrepMIT or Unlicensebrew install ripgrepnonenono index; 2.90–3.38 ms69,257 · 15.2.0 (2026-07-15)
SQLite FTS5Public domainsqlite3 with FTS5FTS5 virtual tableBM250.29 s, 13.8 MB; 0.02–0.08 msnot on GitHub · 3.54.1 (2026-10-16); tested 3.46.1
DuckDB FTSMITpip install duckdb, INSTALL ftsFTS extension, rebuilt on changeBM250.61 s, 3.7 MB; 7.25–9.19 ms42,278 · v1.5.6 (2026-09-28)
tantivy-cliMITcargo install tantivy-cli --lockedtantivy (Lucene-style)BM25not run405 · 0.25.0 (2025-08-20)
qsvMITbrew install qsvrow-offset indexnonot run3,811 · 26.0.0 (2026-10-19)
MeilisearchMIT; Enterprise BUSL-1.1curl -L https://install.meilisearch.com | shserveryesnot run59,612 · v1.55.0 (2026-10-12)
XERJApache-2.0curl -fsSL https://xerj.org/get | shBM25 nodeBM2570.57 s, 112.3 MB; 0.31–0.35 ms over HTTP10,569 · v1.0.0-rc.97 (2026-10-19)

The benchmark

We ran four tools on one shared Linux host (8 CPUs, 16 GB RAM) on 2026-10-19. The corpus is deterministic synthetic log records written by gen_corpus.py, with planted terms only in the msg field. Each tool counted a rare term, a common term and a phrase. Latency is the median of 20 runs after 3 warm-ups.

ToolIndex timeIndex sizeRare / common / phraseHits
ripgrep 14.1.1nonenone3.27 / 3.38 / 2.90 ms51 / 2,485 / 253
SQLite FTS5 3.46.10.29 s13.8 MB0.02 / 0.08 / 0.05 ms51 / 2,485 / 253
DuckDB FTS 1.5.60.61 s3.7 MB7.25 / 9.19 / 8.13 ms51 / 2,485 / 253
xerj v1.0.0-rc.9770.57 s112.3 MB0.35 / 0.31 / 0.31 ms (HTTP)51 / 2,485 / 253

All four returned identical counts. ripgrep's time includes a process spawn per query; SQLite and DuckDB are in-process calls; XERJ's includes an HTTP round trip over loopback. DuckDB has no phrase operator, so its phrase row requires all three terms; on this corpus the count is the same.

At 300,000 records (58.3 MB), ripgrep took 8.79–12.25 ms, SQLite FTS5 indexed in 1.84 s and DuckDB in 2.24 s, and all three returned 338 / 15,061 / 1,484. XERJ did not finish: the kernel killed the node at 9,972,316 kB of anonymous memory about 13 minutes in.

Scripts, raw JSON results, logs and the kernel OOM record are in benchmarks/best-of-jsonl-search-2026. Treat the absolute times as one machine's.

SQLite FTS5 and DuckDB: embedded and fast

SQLite FTS5 is a virtual table module, so the index lives in one database file next to your data. In our run it was the fastest to build and to query, at both sizes.

DuckDB reads JSONL directly with read_json_auto and builds a BM25 index with PRAGMA create_fts_index. Its index was the smallest. Its docs say the index does not update when the table changes, so rebuild it after new rows arrive.

tantivy-cli, qsv and Meilisearch: not benchmarked here

tantivy-cli indexes one JSON document per line into a tantivy index and serves queries from the command line; its README calls it suitable for smaller projects. qsv's search is a regex over CSV fields, and its index is for random row access, not full text. Meilisearch is a server that accepts NDJSON and CSV documents and adds typo tolerance.

We did not run these three, so the table shows "not run" rather than a guess.

XERJ: same answers, heavy indexing

XERJ returned the same counts as the other three, and once indexed it answered in about a third of a millisecond over HTTP. But indexing one 9.7 MB file took 70.57 s and peaked at 9,578,548 kB resident, and the 300,000-record run ran out of memory. On this workload that is a defect, and we publish it as measured.

XERJ still fits when an agent searches a folder of mixed files through one MCP server; the CLI walkthrough shows that path on a smaller folder.

How this page was checked

The benchmark ran on 2026-10-19 with the versions named in its table. Licences, install lines and capabilities of the tools not benchmarked were read from each project's README or docs the same day, and stars and latest releases came from the GitHub REST API. SQLite is not on GitHub; its latest release is from sqlite.org.

FAQ

What is the fastest way to search one JSONL file?

For a single search, ripgrep: no index, about 3 ms on 50,000 records in our run. For many searches over the same file, SQLite FTS5: a 0.29 s index build, then rare-term counts in 0.02 ms.

How was the benchmark run?

On 2026-10-19, on one shared Linux host with 8 CPUs and 16 GB of RAM, over deterministic synthetic log records with a rare term, a common term and a phrase. Latency is the median of 20 runs after 3 warm-ups. The scripts, corpus generator and raw results are in benchmarks/best-of-jsonl-search-2026 in the XERJ repository.

Why did XERJ lose this benchmark?

Indexing one 9.7 MB file took 70.57 s and peaked at about 9.1 GiB resident, and the 300,000-record run was killed by the kernel for running out of memory. That is a XERJ problem on this workload, published as measured. XERJ's query time once indexed was 0.31 to 0.35 ms over HTTP.

Can qsv do full-text search on CSV?

qsv's search command runs a regex over selected fields and prints matching rows. Its index command builds a row-offset index for fast random access, not a full-text index, so there is no ranking.

Do these tools rank results?

SQLite FTS5, DuckDB FTS, tantivy, Meilisearch and XERJ rank with BM25 or a similar score. ripgrep and qsv search print every matching line or row with no ranking.

When is XERJ still the right pick?

When an agent asks many questions across a folder of mixed files, such as JSONL next to CSV, logs and code, through one MCP server and one Elasticsearch-compatible API. For a single large JSONL file on a small machine, pick SQLite FTS5 or DuckDB.

Evidence

Related