corrector.suggest), automatic
query correction that powers a "did you mean?" link
(searcher.correct_query), and fuzzy matching that
finds results even for a misspelled query (FuzzyTerm or the
word~2 syntax). Every snippet below was run against whoosh3 3.16.4
before publishing; the outputs shown are the real outputs.
Nobody types cleanly. Someone searches serch pythonn and, if your
search is exact-match only, gets zero results and bounces. The fix is typo
tolerance, and it's one of the areas where a "small" library like Whoosh
genuinely holds its own — you get suggestions, query correction, and fuzzy
matching without standing up a server or a language model.
1. Turn on spelling for a field
Whoosh can build its correction data straight from the words already in your
index — no external dictionary needed. Add spelling=True to the
text fields you want suggestions from. That tells Whoosh to keep a word graph so
it can find close matches later.
from whoosh.fields import Schema, TEXT, ID
from whoosh.index import create_in
import os
schema = Schema(
title=TEXT(stored=True, spelling=True), # spelling=True enables the corrector
path=ID(stored=True),
)
os.makedirs("idx", exist_ok=True)
ix = create_in("idx", schema)
w = ix.writer()
for i, text in enumerate([
"python programming tutorial",
"advanced python search engine",
"machine learning with python",
"search algorithms explained",
"the whoosh library documentation",
"fast indexing techniques",
]):
w.add_document(title=text, path=str(i))
w.commit()
spelling=True in the schema and re-index once so the word graph is
built. There's no need to change your query code — the corrector reads the graph
that indexing produced.
2. Word suggestions
Ask a searcher for a corrector on a field, then call
suggest(). It ranks candidates by edit distance and by how common
the word is in your index, so suggestions reflect your data — not a
generic dictionary.
with ix.searcher() as s:
corr = s.corrector("title")
print(corr.suggest("pythonn", limit=3)) # -> ['python']
print(corr.suggest("serch", limit=3)) # -> ['search']
Real output: ['python'] and ['search'].
3. "Did you mean?" — correcting a whole query
Suggesting a single word is nice, but what you usually want is to fix the
entire query and show the classic "Did you mean: …?" prompt. That's
searcher.correct_query(query, query_string). It returns a
correction object with the fixed query string and a ready-made HTML rendering
that highlights what changed.
from whoosh.qparser import QueryParser
from whoosh.highlight import HtmlFormatter
with ix.searcher() as s:
qp = QueryParser("title", ix.schema)
user_text = "serch pythonn"
q = qp.parse(user_text)
corrected = s.correct_query(q, user_text)
print(corrected.string) # -> 'search python'
# HTML you can drop into a "did you mean?" link
print(corrected.format_string(HtmlFormatter()))
# -> search python
Real output: the corrected string is search python, and
format_string wraps each fixed term in a <strong>
so you can style it in your "did you mean?" banner.
A common pattern: run the user's query first; if it returns few or no hits and
corrected.query != q, show a "Did you mean search python?"
link that re-runs with the corrected query.
4. Fuzzy matching — find results despite the typo
Correction proposes a new query; fuzzy matching just matches misspelled terms directly against indexed words within an edit distance. Use it when you'd rather return results immediately than ask the user to click a suggestion.
Programmatically with FuzzyTerm
from whoosh.query import FuzzyTerm
with ix.searcher() as s:
q = FuzzyTerm("title", "pythonn", maxdist=2) # allow up to 2 edits
results = s.search(q)
print([h["title"] for h in results])
# -> ['python programming tutorial',
# 'machine learning with python',
# 'advanced python search engine']
Real output: pythonn (a typo) still finds all three
python documents.
In query syntax with the fuzzy plugin
If you accept raw query strings from users, enable
FuzzyTermPlugin and let them (or you) append ~N to a
term to allow up to N edits. Add ~N/P to also require
the first P characters to match — a good way to keep fuzzy matching
fast and precise.
from whoosh.qparser import QueryParser, FuzzyTermPlugin
with ix.searcher() as s:
qp = QueryParser("title", ix.schema)
qp.add_plugin(FuzzyTermPlugin())
q = qp.parse("serch~2") # up to 2 edits
print(repr(q)) # FuzzyTerm('title', 'serch', ... maxdist=2, prefixlength=0)
print([h["title"] for h in s.search(q)])
# -> ['search algorithms explained', 'advanced python search engine']
# require the first character to match: fewer false positives, faster
q2 = qp.parse("serch~2/1") # maxdist=2, prefixlength=1
Real output: serch~2 matches both documents containing
search.
Performance notes
- Fuzzy queries are more expensive than exact terms — each
fuzzy term expands to every indexed word within the edit distance. Keep
maxdistat 1 or 2 and set aprefixlength(~2/1) so Whoosh can skip words that don't share the prefix. - Correction is cheap: it reads the word graph built at index
time, so calling
correct_queryper search is fine. - Only mark fields you'll correct on with
spelling=True— the word graph adds a little to index size and build time.
Get started
pip install whoosh3
Same import path (import whoosh), Python 3.9–3.14. Whoosh3 is the
actively-maintained continuation of Whoosh. If you build something with it, or
hit a rough edge, open an issue —
I read every one. Star the repo if it helps.
Related guides
- Autocomplete and prefix search
- Search a folder of PDFs & Markdown
- Whoosh for RAG: hybrid keyword + vector search
- Performance tuning: fast indexing
The suggestions, corrected query string, and fuzzy search results above came from running the exact code in this guide against whoosh3 3.16.4 before publishing. "Whoosh" is the pure-Python search library originally by Matt Chaput; this is an actively maintained continuation.