Skip to content

fix(query-planner): out-of-schema-range $gt/$lt silently drops boundary documents#8601

Open
sravan27 wants to merge 1 commit into
pubkey:masterfrom
sravan27:fix/query-planner-out-of-range-bounds
Open

fix(query-planner): out-of-schema-range $gt/$lt silently drops boundary documents#8601
sravan27 wants to merge 1 commit into
pubkey:masterfrom
sravan27:fix/query-planner-out-of-range-bounds

Conversation

@sravan27
Copy link
Copy Markdown

Summary

A $gt/$lt range bound that lies entirely outside the indexed field's schema minimum/maximum silently drops the boundary document. The out-of-range bound is clamped to the boundary when the index string is built (getNumberIndexString), but the bound stays exclusive — so the boundary document is excluded. Storages that resolve the query purely from the index (selectorSatisfiedByIndex: true — memory, localstorage, foundationdb, denokv) return wrong results with no error and no matcher fallback.

Reproduce

Field score with { minimum: 0, maximum: 100 }, docs score ∈ {0, 1, 50, 99, 100}:

{ score: { $gt: -10 } }  → [1, 50, 99, 100]   // drops score=0  (expected all 5)
{ score: { $lt: 110 } }  → [0, 1, 50, 99]     // drops score=100 (expected all 5)

$gte: -10 / $lte: 110 are unaffected, because the inclusive bound includes the clamped boundary — which is why this slipped through (the existing boundary test only checks bounds at the limit, not beyond it).

Fix

In the query planner, a lower bound below the field minimum becomes INDEX_MIN (inclusive) and an upper bound above the field maximum becomes INDEX_MAX (inclusive). Exact-boundary exclusives ($gt at the minimum, $lt at the maximum) and all in-range bounds are unchanged.

Tests

Adds a rx-storage-query-correctness case covering both bug cases plus regression guards ($gt at minimum still excludes it, $lt at maximum still excludes it, in-range bounds unchanged). Validated end-to-end against the memory storage before/after the fix.

A `$gt`/`$lt` bound that lies outside the indexed field's schema
minimum/maximum was clamped to the boundary when building the index string but
kept exclusive, so the boundary document was silently dropped from index-based
results: e.g. `$gt: -10` on a field with `minimum: 0` excluded the doc with
value 0, and `$lt: 110` with `maximum: 100` excluded 100. Storages that resolve
the query purely from the index (memory, localstorage, foundationdb, denokv)
returned wrong results with no error.

Treat a lower bound below the field minimum as INDEX_MIN (inclusive) and an
upper bound above the field maximum as INDEX_MAX (inclusive). Exact-boundary
exclusives ($gt at minimum, $lt at maximum) and all in-range bounds are
unchanged. Validated end-to-end against the memory storage; adds a
query-correctness regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant