fix(search)!: reject a date range bound the codec cannot read, instead of dropping it - #751
Merged
Merged
Conversation
…d of dropping it A date bound isoToUnixSeconds could not parse was dropped, and a criterion left with no usable bound compiled to “no constraint” – true for every document. A filtered search silently answered with everything, and under an or clause it took its valid siblings with it. - validateQuery reports an unparseable-bound issue per rejected bound, so assertValidQuery rejects the query at the edge, for every surface and policy - QueryIssue carries the offending literal in a new value field, naming which bound to fix rather than only which field - the Typesense compiler reads a null bound as a bound not set: a GraphQL variable left unfilled reached the engine as datePosted:[null..…], which Typesense rejects outright The compiler is deliberately left as it was otherwise. It cannot recover from an unreadable bound either way: a criterion dropped from a clause narrows the query, while a clause dropped from the conjunction widens it, and filter_by has no term meaning “matches nothing”. Its JSDoc now says so. BREAKING CHANGE: a query whose date range carries a bound the storage codec cannot read (‘yesterday’, or a year past the ±271,821 window Date covers) now throws from assertValidQuery instead of matching every document. QueryIssue gains the unparseable-bound reason and an optional value.
ddeboer
force-pushed
the
worktree-issue-750-unparseable-date-bound
branch
from
August 20, 2026 14:16
276df47 to
bf50487
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
daterange boundisoToUnixSecondscannot read was dropped, and a criterion left with nousable bound compiled to
VACUOUS– “states no constraint”, true for every document. So afiltered search silently answered with everything, and under an
orclause the vacuousreading short-circuited the whole clause, deleting the valid criteria beside it. Nothing in the
response distinguished any of that from a deliberate match-all.
Rejected at the edge, not compiled
validateQuerygains anunparseable-boundissue, reported per rejected bound – the first ruleabout a criterion’s value rather than its field, because no surface’s type system catches it
(
StringisString).assertValidQuerytherefore throws for every caller: GraphQL, deploymentqueryDefaults, in-process callers alike.QueryIssuecarries a new optionalvaluenaming the literal that was rejected, so a caller istold which bound to fix and not just which field.
assertValidQueryrenders it:where: “datePosted” (unparseable-bound: “yesterday”).Not deep-time-specific:
'yesterday'behaved identically, and has since before the codec expandedout-of-form years (#741, #747).
Why the compiler is left alone
The issue also proposed compiling an unreadable bound to
UNUSABLErather thanVACUOUS. I triedthat and measured it, and it does not hold up:
UNUSABLEreads as false within a clause (itdrops out of the
||, siblings stand) but the moment it empties a clause,compileFilterByomitsthat clause from the
&&– and an omitted conjunct is true. Sostatus is valid AND date is garbagecompiled tostatus:[valid], dropping the date constraint and returning more thanwas asked for. It fixed the OR-sibling case and regressed the commoner AND cases.
The underlying reason is that
filter_byhas no term meaning “matches nothing”, so neitherreading can be right in both positions. The compiler keeps its existing behaviour and its JSDoc
now states this limit plainly, pointing at validation as the fix.
That leaves a real but separate defect: any clause left with no terms silently widens the
query, including one whose only criterion names a misspelled field.
isUnsatisfiablecovers onlythe empty-
idshape. Filed separately – it is not a date bug.Also fixed
A
nullrange bound (a GraphQL variable left unfilled, which the surface passes through) compiledto
datePosted:[null..…], which Typesense rejects – an unset filter failing the whole search. Itnow reads as a bound not set.
Fix #750