Skip to main content

Autocomplete

An optional edge-ngram style autocomplete layer for Whoosh-NG.

Install​

pip install whoosh-ng[autocomplete]

Minimal index​

from whoosh.fields import Schema, TEXT, KEYWORD

schema = Schema(
title=TEXT(stored=True),
tags=KEYWORD(stored=True, commas=True),
)

with ix.writer() as writer:
writer.add_document(title="Python Quickstart", tags="python,quickstart")
writer.commit()

Query autocomplete​

from whoosh_modern.autocomplete import create_autocomplete

# Create provider (inverted = prefix matching by default)
provider = create_autocomplete("inverted")

# Index terms (typically done at index time)
provider.add(["python", "quickstart", "programming", "pyramid"])

# Search for suggestions
hits = provider.search("py", limit=5)
for hit in hits:
print(hit.text, hit.score)
# Output: python 0.9, pyramid 0.8

Modern Autocomplete Providers (Whoosh-NG 2.0)​

Whoosh-NG 2.0 introduces multiple autocomplete provider strategies: InvertedIndexAutocomplete, NGramProvider, and FuzzySuggestProvider. For full details on creating, registering, and switching providers, see the Autocomplete Providers Guide.

Autocomplete Provider Integration in the Pipeline​

Autocomplete providers operate in two modes: standalone (in-memory index of phrases) and registry-based (discovered via AutocompleteRegistry). The AutocompletePlugin registers the default "inverted" provider at startup.

Architecture​

┌─────────────────────────────────────────────────────────────────┐
│ Registration (startup) │
│ │
│ AutocompletePlugin.register(PluginManager) │
│ └── AutocompleteRegistry.register("inverted", provider) │
│ │
│ The "inverted" provider is now available globally │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ Mode 1: Standalone (in-memory index) │
│ │
│ provider = create_autocomplete("inverted") │
│ provider.add(["python", "java", "javascript"]) │
│ hits = provider.search("py", limit=5) │
│ └── [AutocompleteHit(text="python", score=0.9), ...] │
│ │
│ No Whoosh index required. Pure in-memory lookup. │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ Mode 2: Registry-based (tied to a Whoosh index) │
│ │
│ provider = AutocompleteRegistry.get("inverted") │
│ │
│ # Populate from index terms │
│ with ix.searcher() as s: │
│ for term in s.reader().all_terms(): │
│ provider.add_term(term, s.doc_freq(term)) │
│ │
│ # Query suggestions │
│ hits = provider.suggest("py", maxdist=1, limit=5) │
│ └── [AutocompleteHit(text="python", score=...), ...] │
└─────────────────────────────────────────────────────────────────┘

Full workflow: indexing + autocomplete​

from whoosh import index, fields
from whoosh_modern.autocomplete import create_autocomplete
from whoosh_modern.autocomplete.plugin import AutocompletePlugin
from whoosh.plugins.manager import PluginManager

# 1. Register autocomplete plugin at startup
manager = PluginManager()
AutocompletePlugin().register(manager)

# 2. Create index
schema = fields.Schema(
title=fields.TEXT(stored=True),
content=fields.TEXT,
)
ix = index.create_in("indexdir", schema)

# 3. Index documents
with ix.writer() as writer:
writer.add_document(title="Python programming", content="Learn Python")
writer.add_document(title="Java development", content="Learn Java")
writer.add_document(title="JavaScript basics", content="Learn JS")
writer.commit()

# 4. Build autocomplete provider from index terms
provider = create_autocomplete("inverted")

with ix.searcher() as searcher:
reader = searcher.reader()
for term in reader.all_terms():
# Add term with its document frequency as score weight
provider.add([term.decode("utf-8")])

# 5. Query autocomplete
hits = provider.search("py", limit=5)
for hit in hits:
print(f"{hit.text} (score: {hit.score:.3f})")
# Output: python (score: 0.429)

Provider strategies​

ProviderStrategyBest for
InvertedIndexAutocompletePrefix matching with scoringSimple autocomplete, small vocabularies
NGramProviderCharacter n-gram indexingSubstring matching, typo tolerance
FuzzySuggestProviderApproximate matching via rapidfuzzTypo-tolerant suggestions
# Prefix matching (default)
provider = create_autocomplete("inverted")
provider.add(["python", "pyramid", "pyodbc"])
hits = provider.search("py")
# → python, pyramid, pyodbc

# N-gram matching
provider = create_autocomplete("ngram", n=3)
provider.add(["python", "java", "javascript"])
hits = provider.search("pyt")
# → python (matched by "pyt" n-gram)

# Fuzzy matching
provider = create_autocomplete("fuzzy", max_distance=2, score_cutoff=60.0)
provider.add(["python", "pyramid", "pyodbc"])
hits = provider.search("pythn") # typo: missing 'o'
# → python (fuzzy match)

Integration with search middleware​

Autocomplete providers can be combined with search middleware for real-time suggestions:

from whoosh.middleware.chain import MiddlewareChain
from whoosh.middleware.wrappers import MiddlewareSearcher

provider = create_autocomplete("inverted")
provider.add(["python", "java", "javascript"])

# In a search handler
def suggest(query: str, limit: int = 5):
hits = provider.search(query, limit=limit)
return {"suggestions": [hit.text for hit in hits]}