The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →To filter by a number or a date in Whoosh, index the value in a typed field (NUMERIC or DATETIME) and query it with the matching range object (NumericRange or DateRange). Both range objects include their endpoints by default. The guidance below comes from the official Whoosh 2.7.4 documentation. That version is the one the documentation describes, and it has not been checked against current Python releases or current Whoosh maintenance status, so confirm the argument names and behaviour against the version you have installed.
Choose the field type that matches the value
Whoosh stores each field with a type, and the type decides how values are sorted and searched. For numbers and dates, the two types you need are:
As an Amazon Associate I earn from qualifying purchases.
NUMERICfor integers and floating-point values. The field converts each value into sortable bytes, which is what makes range searches possible.DATETIMEfor Pythondatetime.datetimeobjects. The writing documentation shows datetime fields accepting datetime objects, not date strings.
A minimal schema looks like this:
from datetime import datetime
from whoosh.fields import Schema, TEXT, NUMERIC, DATETIME
from whoosh.filedb.filestore import RamStorage
schema = Schema(
title=TEXT(stored=True),
price=NUMERIC(stored=True),
published=DATETIME(stored=True),
)
ix = RamStorage().create_index(schema)
writer = ix.writer()
writer.add_document(title="Bridge guide", price=24.5,
published=datetime(2007, 3, 14, 9, 30))
writer.commit()
If a value is stored as a string in a TEXT or ID field, it is compared as text. Numeric ordering such as 9 before 10 will not hold there, so do not use text fields for numeric or chronological filters.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Numeric ranges with NumericRange
The direct numeric API is whoosh.query.NumericRange(fieldname, start, end, startexcl=False, endexcl=False, boost=1.0, constantscore=True). Its endpoints must be numbers, not strings. By default, values equal to both boundaries match. Set startexcl=True or endexcl=True to drop the matching endpoint.
#1 Best Overall
from whoosh.query import NumericRange
# prices from 10 up to and including 50
q = NumericRange("price", 10, 50)
# prices strictly above 10 and up to 50
q = NumericRange("price", 10, 50, startexcl=True)
# prices from 10 up to but not including 50
q = NumericRange("price", 10, 50, endexcl=True)
The NUMERIC constructor also takes bits, signed, decimal_places and shift_step. The documentation says a lower shift step stores more index data in exchange for faster searches, and that a shift step of zero disables tiered indexing. Tiered indexing stores high-resolution terms at the range edges and lower-resolution terms in the middle, so large ranges can be matched with fewer terms. The documentation describes this as a speed benefit in general terms; it does not publish a benchmark for any workload, so measure your own index before tuning it. Constant-score matching, which is on by default through constantscore=True, is described as useful for typical filter use.
Date ranges with DateRange
DateRange is a thin subclass of NumericRange. It converts datetime endpoints into numbers and otherwise behaves the same way, including the inclusive default and the exclusion flags.
from datetime import datetime
from whoosh.query import DateRange
# published in 2005 through 2010, both boundaries included
start = datetime(2005, 1, 1)
end = datetime(2010, 6, 2, 23, 59, 59)
q = DateRange("published", start, end)
with ix.searcher() as searcher:
hits = searcher.search(q)
for hit in hits:
print(hit["title"], hit["published"])
Keep the two range types separate. Pass numbers to NumericRange and datetime objects to DateRange. A datetime endpoint passed to a numeric field, or a number passed to a datetime field, is the most common cause of ranges that return nothing.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteRank #2
Time zones: index UTC, not local time
The date indexer ignores the tzinfo attribute of a datetime. Attaching a timezone to an object does not make the stored value timezone-aware. The official “About time zones and basetime” section gives the rule directly: “The best way to deal with time zones is to always index datetimes in native UTC form.” Attribute that sentence to the Whoosh documentation rather than to an individual author.
In practice, convert before you index and convert your query endpoints the same way, so both sides use the same clock:
from datetime import datetime, timezone
def to_utc_naive(dt):
"""Convert an aware local datetime to naive UTC for storage."""
return dt.astimezone(timezone.utc).replace(tzinfo=None)
local = datetime(2024, 5, 1, 9, 0, tzinfo=timezone.utc).astimezone()
stored = to_utc_naive(local)
The helper above returns a naive datetime holding UTC wall-clock time. This avoids any dependence on the indexer’s handling of tzinfo. Use the same helper for the start and end values of every DateRange query.
Open-ended date ranges
The Whoosh 2.7.4 date guide states that DATETIME fields do not currently support open-ended ranges. Its suggested workaround is to use an endpoint far in the past or future. Choose that bound from the valid data domain of your application, so it sits safely outside any real value:
from datetime import datetime
from whoosh.query import DateRange
# "published on or after 2015-01-01", with a far-future upper bound
q = DateRange("published", datetime(2015, 1, 1), datetime(9999, 12, 31))
The guide presents this as a workaround for that version. Later releases may handle open ends differently, which is one more reason to check the installed version.
Range syntax in the query language
The default query parser has its own range syntax. Square brackets include the endpoints, and curly braces exclude them. You can mix delimiters so that one endpoint is inclusive and the other exclusive:
[apple TO bear]matches terms from apple to bear, including both.{apple TO bear}excludes both endpoints.[apple TO bear}includes the start and excludes the end.
The guide gives date-shaped examples such as date:[20050101 TO 20090715] and date:[20050101 to 20100602]. These work because the dates are stored in a lexically sorted form, such as YYYYMMDD, where text order matches chronological order. This is term-range behaviour, not the typed DateRange object. Do not assume a parser range on a NUMERIC or DATETIME field behaves identically to the typed API. If you expose parser syntax to users on typed fields, test the exact strings you accept against your index before relying on them.
Parser-side single-date forms are also documented. date:2005 and date:20050624 are examples of this lexical style.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Comparison operators with GtLtPlugin
The optional GtLtPlugin lets users write comparisons such as field:>apple or date:>='31 march 2001', which the parser translates into ranges. It is a plugin, not a default. Add it explicitly to the parser you use, for example with parser.add_plugin(GtLtPlugin()), and check the output of a few sample queries.
Best Value
Human-readable dates with DateParserPlugin
If users type dates in natural language, the DateParserPlugin can convert that text into a date query. Three limits matter before you adopt it:
- The Whoosh 2.7.4 documentation labels it experimental.
- It supports English dates.
- Relative expressions, such as “last week”, depend on a base datetime. Pass a fixed base when you need repeatable results, or the same input will match different ranges on different days.
The free=True setting allows unquoted date text after a field prefix. That makes input more forgiving, and it also makes the parser more likely to accept text you did not intend as a date. Enable it only after testing with realistic input.
Choosing an approach
| Choice | Use it when | Main considerations |
|---|---|---|
NumericRange |
The field is NUMERIC and your code builds the query |
Endpoints must be numbers. Both ends are inclusive unless startexcl or endexcl is set (Whoosh 2.7.4 query API). |
DateRange |
The field is DATETIME and your code builds the query |
Pass datetime objects. Store and query in UTC. Open-ended ranges need a far-past or far-future bound (Whoosh 2.7.4 date guide). |
| Query parser range syntax | Users type query strings and your schema supports the form | Brackets are inclusive and braces exclusive. Ranges compare lexical term order, so verify numeric and date behaviour on your own index. |
GtLtPlugin |
Users write comparisons such as >= |
Optional plugin that must be added to the parser. Translates comparisons into range queries. |
DateParserPlugin |
Users enter natural-language dates | Experimental in the 2.7.4 documentation, English only, and relative terms depend on a base datetime. |
Common pitfalls
- Strings where numbers belong. A numeric filter on a text field sorts lexically, so
10falls between1and2. - Local time in the index. Mixed time zones make date boundaries shift for some records. Normalize to UTC on both write and query.
- Assuming exclusive ends by default. A range ending at 50 includes 50 unless you set
endexcl=Trueor use a closing brace in query syntax. - Trusting an open end. A
DATETIMErange with no real upper or lower limit needs an explicit sentinel bound, not an empty value. - Using old version examples unchanged. Confirm argument names and defaults in the documentation for your installed release before copying code.
Verify before you ship
- Run
pip show whooshand confirm the installed version and the Python interpreter you are using. - Index three test records: one at each boundary and one just outside each boundary. Query each range and confirm which records match.
- Repeat the boundary test with a UTC value and a local-time value to confirm your normalization.
The official Whoosh documentation is the reference for every behaviour described above. Check it against your installed version whenever you upgrade.
Quick Recap
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




