Also worth reading:
· Whoosh ImportError on modern Python — the fix
— cannot import name 'Iterable', No module named 'imp': why old Whoosh breaks on Python 3.10+ and the one-line fix.
· Is Whoosh still maintained? (2026 status)
— which fork is active, Python 3.14 support, and how to upgrade in one line.
· Search your files from the command line
— a hands-on tour of the whoosh CLI: grep, but ranked by relevance.
· Add full-text search to a Flask app
— a step-by-step, pure-Python tutorial (indexing, ranking, highlighting).
· Add full-text search to a FastAPI app
— a /search endpoint with Pydantic models, plus how to run Whoosh safely from async routes.
· Add full-text search to a Django app
— index your models and search them, no PostgreSQL or external service required.
· Whoosh vs SQLite FTS5
— pure Python vs a built-in C extension, with real benchmark numbers.
· Migrating from SQLite FTS5 to Whoosh
— a runnable, step-by-step guide: export your rows, mirror the schema, match FTS5 stemming.
· Whoosh analyzer recipes
— copy-paste tokenizers: accent folding, stemming, camelCase, tags, and n-gram search.
· Whoosh vs Tantivy (tantivy-py)
— pure Python vs Rust bindings: install story, speed, and portability.
· Whoosh vs Elasticsearch
— when an embedded library beats running a JVM search cluster.
· Use Whoosh as a LangChain retriever
— a copy-paste pure-Python BM25 retriever for RAG and hybrid search.
· Use Whoosh as a LlamaIndex retriever
— the first-class whoosh.llamaindex BM25 retriever for RAG and QueryFusionRetriever hybrid search.
· Full-text search over a pandas DataFrame
— index df.to_dict("records"), rank with BM25, map hits back to rows, filter numeric columns.
· Faceted search & result counts
— group by category/brand, bucket prices with RangeFacet, drill down on a clicked facet.
· Highlighted search-result snippets
— Google-style excerpts with matched words wrapped in <mark>, fragment control, non-stored fields.
· Keep a Whoosh index in sync
— incremental add / update_document / delete_by_term driven by modification time, so you never rebuild.
· Whoosh as an MCP server for AI agents
— expose search and fetch tools to an LLM agent over the Model Context Protocol, backed by a local pure-Python BM25F index.
Whoosh is a full-text search library for Python: ranked
results, a real query language, faceting, highlighting, and spell-correction —
all in pure Python, with no compiler, no server, and no native dependencies.
pip install and go.
It was widely used, then went unmaintained. It was revived once as
whoosh-reloaded, then went quiet again. I'm picking it back up,
and this is the first release under new maintenance.
Update (3.15–3.30) — a grown-up CLI, full public-API type hints, and CJK search.
The revival has kept a steady release cadence. The bundled whoosh command
matured into a proper grep-style search tool: paging (--page), machine-readable
output (--jsonl/--ndjson), -l/--files-with-matches
(with -0 for null-separated paths), --or matching, --color,
--min-score, --max-size, --follow-symlinks, and a
--dry-run index preview all landed (3.16–3.25). The whole public API now carries
PEP 484 type hints — sorting, highlighting,
classification, compound/base queries, and the reader, writer, and spelling surfaces — so your
editor autocompletes Whoosh and mypy checks your calls, with the heavy imports kept
behind TYPE_CHECKING so there is zero import-time cost (3.25–3.30). And 3.28.0 added
first-class CJK (Chinese / Japanese / Korean) text support via a new
CJKFilter/CJKAnalyzer. Along the way, real reliability fixes shipped in
the multiprocessing writer, phrase highlighting, and corrupt-index handling — every change with a
regression test. pip install -U whoosh3.
Update (3.12–3.14) — a new CLI report, two safer APIs, and a nested-search fix.
The revival has kept shipping. whoosh stats --top-terms FIELD now
lists a field's most frequent indexed terms — a reviewed community contribution
from @Nitjsefnie (3.12.0). On the
correctness side, multiprocessing writes to non-shared storage no longer silently
discard documents, and a handful of query-parser and date edge cases were fixed
(3.12.x). Two new opt-in APIs make common footguns safe: AsyncWriter.wait()
gives you a race-free way to block on the background commit thread and surface any
error instead of losing buffered documents (3.13.0), and
Phrase(..., degrade=True) lets a quoted query gracefully fall back to an
AND-of-terms on fields that store no positions, instead of raising — handy when you
feed user input straight into a parser over n-gram fields (3.14.0). Finally,
NestedParent no longer silently drops results when the index contains a
child document with no parent above it, a long-standing bug in nested search (3.14.1).
Every one of these landed with a regression test. pip install -U whoosh3.
Update (3.11.x) — Python 3.14 and two scoring/reader fixes.
Whoosh now runs on Python 3.10 through 3.14, with the latest stable
CPython in CI (3.11.0). Two correctness fixes followed, each with a regression
test: boosts on multi-term queries (Prefix, Wildcard,
FuzzyTerm, TermRange) are now actually applied to the
score instead of being silently dropped (3.11.6); and
IndexReader.field_terms() no longer raises OverflowError
or yields garbage on NUMERIC/DATETIME fields — it now
returns exactly the distinct values you indexed (3.11.7).
pip install -U whoosh3.
Update (3.10.0) — first community feature merged.
whoosh search --sort-by score|mtime lets the CLI order results by
relevance (the default) or by file modification time, newest first;
--count still reports the true total regardless of sort order.
This was the first community feature contribution to the revived project,
from @abhiramvsmg — exactly the
kind of contribution the good-first-issue backlog
exists to invite.
Update (3.9.0) — the CLI plays nicely with pipes.
whoosh search --no-highlight prints a plain, grep-friendly slice
of each match with no UPPERCASED markup — ideal when you're
piping results into another tool — and --snippet-chars N lets you
set how much context each result shows (default 200), for both text and JSON
output. The whoosh command also now prints the project home in its
--help and --version output, so the docs and issue
tracker are one glance away. pip install -U whoosh3.
Update (3.8.x) — four real reliability fixes. A revival
is only worth trusting if the bugs actually get fixed, so this run went
after correctness rather than features. whoosh search --field NAME
(a reviewed community contribution) lets you restrict a query to specific
fields (3.8.0). Then three data-safety fixes, each with a regression test:
a memory-mapped compound segment no longer leaks a file descriptor
on close — the "too many open files" trap for long-running servers (3.8.1);
AsyncWriter no longer silently swallows a failure on its
background commit thread and drop your buffered documents — it now records the
exception and releases the lock (3.8.2); and a commit() that
raises partway through no longer leaves the index write-locked and
effectively read-only until someone clears the stale lock by hand (3.8.3).
These are exactly the kinds of latent, hard-to-reproduce bugs that make a
library feel unmaintained — now closed. pip install -U whoosh3.
Update (3.5.0) — the CLI grew up. The bundled
whoosh command now speaks to your shell scripts as fluently as to
you. whoosh search --json emits a machine-readable array (path,
score, snippet, title) you can pipe straight into jq;
--count prints just the number of matches for
if-tests and pipelines; --fields trims the output to
the stored fields you care about; and --limit now rejects
nonsensical values with a clear error instead of silently misbehaving. There's
also a new static-site
search example that indexes a folder of Markdown/RST/text files and
searches them with highlighted snippets — a server-free way to add search to a
static site or ship an index next to a desktop app. Most of these landed as
reviewed community contributions, which is exactly the sign of life a revived
project wants. pip install -U whoosh3, then
whoosh search --help.
Update (3.3.1): fixed a long-standing bug where large
in-memory (RamStorage) indexes could fail to commit with
NameError: <name>.run once the posting pool spilled sorted
run files — reported across both the original tracker and the
whoosh-reloaded fork. In-memory runs are now retained correctly;
disk-backed indexes were never affected. Comes with a regression test that
forces multiple spills and verifies the committed index is searchable.
Update (3.3.0) — search a folder from your terminal.
Whoosh now installs a whoosh command, so you can index and
search a directory of notes, docs, or code without writing any Python:
whoosh index ~/notes then whoosh search "ranked query".
Think of it as a ranked grep — BM25-scored results with highlighted
snippets, incremental re-indexing (only changed files are touched), file-type
filters, and optional HTML output. It's a small, fully-tested wrapper over the
same public API, so anything the CLI does you can also script.
pip install -U whoosh3, then whoosh --help. See the
command-line
reference.
Update (3.0.3): the natural-language date parser now
accepts ISO-8601 dates (e.g. created:2023-05-17) instead of
choking on the - separators, so DateParserPlugin
works with the date format most apps already store. This release also adds a
compatibility test suite that guards the exact API surface downstream
projects rely on, so future changes can't silently break them.
pip install -U whoosh3.
Update (3.0.2): another patch release, fixing two real
correctness bugs. First: a filter=/mask= set was
silently ignored whenever a search also had a time limit, so a filtered,
time-limited search returned every matching document. Second:
sorting or faceting by a column field returned scrambled results after
documents were added through a BufferedWriter. Both come with
regression tests. pip install -U whoosh3.
Update (3.0.1): a patch release fixes a real correctness
bug from the upstream backlog — NumericRange with an open lower
bound could match every document instead of the intended range (an
underflow in the trie-range splitter that hit unsigned NUMERIC
fields). It's fixed with an exhaustive regression test. pip install -U
whoosh3.
What's new in the 3.0.0 revival
- Zero runtime dependencies. Dropped
loguruandcached-property(now stdlibfunctools.cached_property). A truly pure-Python, no-compile install that works anywhere CPython runs — including PyPy and the browser via Pyodide. - Modern packaging (PEP 621) and a CI matrix across Python 3.10–3.14.
- Two long-standing bugs fixed, each with a regression test:
MultiFilterno longer throwsStopIterationon empty token streams.RamStorageno longer secretly writes to/tmp— in-memory indexes now stay in memory, fixing those intermittent "No such file" errors.
- A live, in-browser demo (Pyodide) so you can try search without installing anything.
- A reproducible benchmark vs SQLite FTS5 in
examples/, with honest caveats — FTS5 (a C extension) is faster and smaller; Whoosh is what you reach for when you want real search inside a Python process with no extra infrastructure and full programmability. - A did-you-mean / spell-check demo — Whoosh has built-in spelling correction straight off the index, and most people never discover it.
- A search-as-you-type / autocomplete guide — three pure-Python strategies (prefix queries, term completion, and n-gram substring matching) for the single most-requested search feature, with copy-paste code.
- A RAG & hybrid-search guide — use Whoosh as the BM25 keyword-retrieval half of a hybrid RAG pipeline and fuse it with vector search via Reciprocal Rank Fusion (RRF), with runnable code and no server.
- A performance-tuning
guide — why Whoosh indexing feels slow (usually a writer per
document: ~18× slower) and how to fix it with batching,
limitmb,multisegment,optimize, and searcher reuse — with verified before/after benchmarks. - A knowledge-base guide — index a folder of PDFs, Markdown, and text files into a fast local full-text search, with incremental re-indexing by mtime, delete detection, and highlighted snippets — all in one verified ~60-line script.
- A spelling &
fuzzy-search guide — typo-tolerant search in pure Python: word
suggestions, automatic "did you mean?" query correction, and fuzzy matching
with
FuzzyTermand theword~2syntax — verified outputs. - A migration guide for existing Whoosh / whoosh-reloaded users — upgrading is a one-line dependency change; imports, on-disk index format, and the public API are unchanged.
- Honest docs and a public roadmap. I'd rather under-promise.
Try it in 30 seconds
from whoosh.fields import Schema, TEXT, ID
from whoosh.filedb.filestore import RamStorage
from whoosh.qparser import QueryParser
ix = RamStorage().create_index(Schema(path=ID(stored=True), body=TEXT))
w = ix.writer(); w.add_document(path="/a", body="the quick brown fox"); w.commit()
with ix.searcher() as s:
q = QueryParser("body", ix.schema).parse("quick fox")
print([hit["path"] for hit in s.search(q)]) # ['/a']
Prefer the terminal? Search a folder of files with the bundled
whoosh command — no Python required:
pip install whoosh3
whoosh index ~/notes # build an index (incremental on re-run)
whoosh search "ranked query" # BM25-scored hits with highlighted snippets
Install it with pip install whoosh3. Or don't install anything
and run the live browser demo first.
Why pure-Python search still matters in 2026
The obvious question: why not just use a C extension like SQLite FTS5, or a managed service? Sometimes you should — and the benchmark in the repo is honest about where FTS5 wins. But there's a large middle ground where you want real ranked search inside a Python process: no extra service to run, no native build step in your Docker image or serverless function, full programmability of analyzers and scoring, and the ability to run the exact same code on your laptop, your server, PyPy, or WebAssembly. That's the niche Whoosh has always filled, and it's why the library kept getting used long after it stopped being maintained.
Credit where it's due
Whoosh was created by Matt Chaput and later carried by the
whoosh-reloaded maintainers (Sygil-Dev). This continuation
preserves their copyright and BSD-2-Clause license. Thank you.
Issues and PRs are welcome — I aim to respond promptly and kindly.