COMPARE · COMPARISON: DESKTOP SEARCH

How do I grep PDFs and Word docs from the command line?

AUTHOR · XERJ documentation team · REVIEWED BY · XERJ engineering team · PUBLISHED · 2026-08-22 · UPDATED · 2026-08-23

TL;DR — ripgrep-all is the better choice for one question, today. It converts PDFs and Office files as it goes, walks into archives, and wants no daemon and no index. XERJ indexes the same folder once for an agent to query many times. No head-to-head benchmark was run.

PASTE THIS TO YOUR AI AGENT — IT DOES THE REST Act as a coding agent with a folder of PDFs and Word documents. Read https://xerj.org/llms.txt, decide between a one-shot rga search and an index, then start a local XERJ node, run xerj autoindex on the folder and answer with a match_phrase query that cites the ax_path of every hit.
RUN THIS XERJ COMMAND xerj autoindex ./documents --url http://127.0.0.1:9200 --prefix docs --progress plain Index the folder once, so later questions do not parse the PDFs again.
RUN THIS XERJ COMMAND xerj autoindex map --url http://127.0.0.1:9200 Print the data map, so the next query names a real index and a real field.
RUN THIS XERJ COMMAND curl -s -XPOST http://127.0.0.1:9200/docs-*/_search -H 'content-type: application/json' -d '{"query":{"match_phrase":{"body":"invoice number"}},"size":10,"_source":["ax_path","ax_format"]}' Ask the phrase question over HTTP and read the file path from every hit.
RUN THIS XERJ COMMAND xerj mcp --url http://127.0.0.1:9200 Serve the same node to an agent as a stdio MCP server.

No benchmark was run, and this page says so

We did not freeze a shared corpus. We did not install ripgrep-all next to XERJ. We measured no hit counts, no recall and no latency.

There is no win count on this page. No run stands behind one.

Every ripgrep-all statement below comes from its own README and changelog. Every XERJ statement is a documented capability of the binary.

If you want numbers, take your own folder. Run both tools on it and count the files each one names. That is the only comparison that describes your documents.

What ripgrep-all is

ripgrep-all, called rga, wraps ripgrep. ripgrep matches a regular expression over lines of text. rga puts a converter in front of it. The same regular expression then reaches a PDF or a Word file.

The converters are called adapters. Each adapter claims a set of file extensions, and rga runs it before the search.

adapterwhat it readshow
popplerPDFpdftotext from poppler-utils
pandocepub, odt, docx, fb2, ipynb, htmlpandoc to plain text
ffmpegmkv, mp4, avi, mp3, ogg, flac, webmmetadata, chapters and subtitles
zipzip, jarreads the archive as a stream
tartarreads the archive as a stream
decompressgz, bz2, xz, zstunpacks, then runs another adapter
sqlitedb, sqlite, sqlite3prints the tables as plain text
mailmbox, mbx, emlopt-in, --rga-adapters=+mail

Two flags matter for a mixed folder. --rga-accurate picks the adapter from the file contents rather than the file extension. --rga-adapters=+mail turns on the mail adapter, which is off by default.

rga is not stateless. rga caches the extracted text of each file under the user cache directory. A second search over the same PDFs does not convert them again. --rga-no-cache turns the cache off.

What XERJ is

XERJ is a single Rust binary that runs one search node. xerj autoindex <folder> reads the folder and detects each file family by content. It infers a dataset per file shape and writes the documents.

The command takes no configuration file and no mapping. It also needs no helper program on the host, because the binary parses the formats in process.

The node answers the Elasticsearch REST API. xerj autoindex map prints a data map of what the folder became. An agent reads that map, then names a real index and a real field.

xerj mcp is a stdio MCP server in the same binary. It serves 10 tools against a node you already started. Agent memory lives in the engine under /_memory/{namespace}.

XERJ is single-node. There is no replication and no failover. The default embedder is lexical feature hashing, not a neural model, and neural retrieval is opt-in through --embed-mode neural.

Capabilities, side by side

Every row is a documented capability of each tool. No row is a measured result.

capabilityripgrep-allXERJ
regular expressions over raw textyes, ripgrep does the matchingno, the query language is analyzed terms
a substring inside a wordyesno, an analyzed index matches whole terms
start cost before the first answernone, no index and no daemonone xerj autoindex run
repeat questions on the same folderconverts once, then reads its cachereads the index
archives, and archives inside archivesyes, zip, tar and compressed filesno archive handler
a single gzipped fileyes, the decompress adapteryes, gzip is transparent on every parsed family
email filesopt-in mail adapterno email handler
OCR for image-only PDFsnot shipped, custom adapter onlynone
helper programs on the hostpandoc, poppler-utils, ffmpegnone
ranking of resultsnone, matches in file orderBM25 over the extracted text
filter by file family or pathshell globs and ripgrep flagskeyword fields, ax_format and ax_path
HTTP query APInoneyes, Elasticsearch REST API
MCP server for an agentnoneyes, xerj mcp, 10 tools
catalog of refused filesmessages on standard errorautoindex-catalog, one reason per file
agent memory in the enginenoneyes, /_memory/{namespace}

When to choose ripgrep-all instead

Choose ripgrep-all for one question, today. There is no index to build and no node to start. The answer costs one command.

Choose ripgrep-all when the pattern is a real regular expression. An analyzed index matches whole terms. A run of characters inside a word has nothing to match. ripgrep reads bytes and ignores where a word starts.

Choose ripgrep-all when the documents sit inside archives. It walks into zip, tar and compressed files, and into an archive inside an archive. XERJ has no archive handler, so a zip or a tar never reaches a XERJ index.

A single gzipped file is the one exception on the XERJ side. xerj autoindex detects gzip by content and decompresses it during indexing, on every parsed family. A .jsonl.gz log therefore lands beside the plain file next to it.

Choose ripgrep-all when the folder holds mail files. The opt-in mail adapter reads mbox, mbx and eml. XERJ has no email handler.

Choose ripgrep-all when you must add a converter of your own. Custom subprocess adapters are configuration, so an OCR program can sit in front of the search. XERJ has no OCR and no adapter interface.

Choose ripgrep-all when the files change under you. It converts the file it is looking at, and XERJ answers from the last xerj autoindex run.

When to choose grep or ripgrep instead

Choose grep or ripgrep instead when the folder is plain text. Both are line matchers with no conversion step, and neither wants an adapter or a node.

For code trees the same argument continues on the ripgrep comparison for code agents, which publishes the tasks where ripgrep names files an index does not.

Choose Recoll instead when a person wants a GUI, email indexing or OCR. The Recoll comparison covers that trade.

Choose Elasticsearch instead when the documents must live on more than one host. XERJ has no replication and no failover. XERJ speaks the same REST API on one node.

When XERJ is the better fit

Choose XERJ when the same folder answers many questions. The parse happens once, and every later query reads the index rather than the PDFs.

Choose XERJ when the caller is a program. An agent that speaks HTTP or MCP needs no wrapper and no output parser. xerj mcp serves 10 tools.

Choose XERJ when the order of the answers matters. rga returns every match in file order. BM25 puts the best documents first.

Choose XERJ when the answer must carry provenance. Every document carries ax_path, ax_file, ax_format and four more keyword fields, so an agent can cite the source file.

Choose XERJ when a refused file must be explainable. Files that XERJ does not parse land in autoindex-catalog with a reason string, rather than passing in silence.

What XERJ does not have

XERJ has no regular expression engine over raw bytes. The index matches analyzed terms and phrases.

XERJ has no archive handler, no email handler and no OCR. It has no adapter interface, so a format that the binary does not parse stays out of the index.

XERJ runs on one node. There is no failover. Plan for restore from a copy.

How to read this page

This is a capability comparison drawn from each tool's own documentation. It is not a benchmark, and it is not a claim about your files.

The honest summary is short. rga fits a low question count and odd formats. XERJ fits a program that asks the folder many questions.

The folder search walkthrough shows the XERJ side in full.

FAQ

Is ripgrep-all enough or do I need a real index?

ripgrep-all is enough while the folder is small and the questions are few. An index pays for itself when the same folder answers many questions, because the PDFs are parsed once instead of once per search.

What's the difference between rga and a local search engine?

rga converts each file and matches a regular expression over the text. A search engine parses the folder once into typed documents, then ranks and filters them, and answers over an API.

How do I search SQLite and PDFs without building an index?

Run rga with its sqlite and poppler adapters. Install poppler-utils and pandoc first, because rga calls those programs for the conversion.

Did you run a head-to-head benchmark between XERJ and ripgrep-all?

No. No shared corpus was frozen and no hit counts or timings were measured, so this page publishes documented capabilities and no win counts.

Which tool searches inside zip and tar archives?

ripgrep-all. It descends into zip, tar and compressed files up to a default depth. XERJ has no archive handler and never opens a zip or a tar. A single gzipped file is the exception: autoindex decompresses .gz on every parsed family.

Can ripgrep-all read image-only PDFs?

Not with the shipped adapters. Its config file accepts custom subprocess adapters, so an OCR program can be added by hand. XERJ has no OCR at all.

Evidence

Related