SearchView
SearchView integrates a DataSource with Whoosh indexing. It discovers
schema, validates the source, builds the index, and supports incremental
refresh and schema evolution.
Basic Usage
from whoosh_modern.views import SearchView
from whoosh_modern.data_sources.sql import SQLSource
import sqlite3
conn = sqlite3.connect("data/articles.db")
source = SQLSource(
connection=conn,
query="SELECT * FROM articles",
incremental_field="updated_at",
id_field="id",
)
view = SearchView(
name="articles",
source=source,
)
# Build index (discovers schema, validates, populates)
ix = view.build("indexdir")
Full Reindex vs Incremental Refresh
# Full reindex (clears and rebuilds the entire index)
count = view.reindex()
# Incremental refresh (only changed documents since last sync)
count = view.refresh()
Validation
# Run validation before building
results = view.validate()
for result in results:
print(f"Level {result.level}: {'PASS' if result.passed else 'FAIL'}")
for error in result.errors:
print(f" ERROR: {error}")
for warning in result.warnings:
print(f" WARNING: {warning}")
Validation levels:
- Structural — DataSource availability, schema detection
- Search — Indexable fields, term vectors, searchable analyzers
- Performance — Performance warnings (e.g., TEXT fields on large datasets)
- Runtime — Sample iteration, type conformance
Field Overrides
Customize field types after schema discovery:
from whoosh.fields import TEXT, NUMERIC, DATETIME
view = SearchView(
name="custom",
source=source,
fields={
"title": TEXT(stored=True, phrase=False),
"price": NUMERIC(int, sortable=True),
"published": DATETIME(sortable=True),
},
)
Facets
Configure facet settings:
view = SearchView(
name="faceted",
source=source,
facets={
"category": {"type": "terms", "limit": 50},
"price": {"type": "range", "buckets": ["0-100", "100-500", "500+"]},
},
)
Middleware
Attach middleware to the search pipeline:
from whoosh_modern.views import SearchView
from whoosh_modern.middleware import LoggingMiddleware, RetryMiddleware
view = SearchView(
name="with_middleware",
source=source,
middleware=[
RetryMiddleware(attempts=3, backoff="exponential"),
LoggingMiddleware(),
],
)
Strict Mode
view = SearchView(
name="strict",
source=source,
strict=True, # Raise ValidationError on any validation failure
)
Schema Evolution
Add new fields to an existing index without a full reindex:
view = SearchView(name="articles", source=source)
view.build("indexdir")
# Add a new field
view.evolve_schema({
"new_field": TEXT(stored=True),
})
Schema Version Checking
view = SearchView(name="articles", source=source, schema_version="2.1")
view.build("indexdir")
# Check if the stored schema version matches
if not view.check_schema_version():
print("Schema version mismatch, consider reindexing")