Skip to content

WIP: Support constaints on distance column in KNN queries, for pagination and range queries - #166

Merged
asg017 merged 3 commits into
mainfrom
knn-distance-constraints
Feb 13, 2026
Merged

WIP: Support constaints on distance column in KNN queries, for pagination and range queries#166
asg017 merged 3 commits into
mainfrom
knn-distance-constraints

Conversation

@asg017

Copy link
Copy Markdown
Owner

refs #165

.load dist/vec0
create virtual table vec_items using vec0(
vector float[1]
);
insert into vec_items(rowid, vector)
select value, json_array(value) from generate_series(1, 100);
select vec_to_json(vector), distance
from vec_items
where vector match '[1]'and k =5;
/*┌─────────────────────┬──────────┐│ vec_to_json(vector) │ distance │├─────────────────────┼──────────┤│ '[1.000000]' │ 0.0 ││ '[2.000000]' │ 1.0 ││ '[3.000000]' │ 2.0 ││ '[4.000000]' │ 3.0 ││ '[5.000000]' │ 4.0 │└─────────────────────┴──────────┘*/select vec_to_json(vector), distance
from vec_items
where vector match '[1]'and k =5-- the new magicand distance >4.0;
/*┌─────────────────────┬──────────┐│ vec_to_json(vector) │ distance │├─────────────────────┼──────────┤│ '[6.000000]' │ 5.0 ││ '[7.000000]' │ 6.0 ││ '[8.000000]' │ 7.0 ││ '[9.000000]' │ 8.0 ││ '[10.000000]' │ 9.0 │└─────────────────────┴──────────┘*/

TODO

  • tests
  • docs
  • edge cases - when multiple items have same distance calculations
  • should distances be f64?

@asg017asg017 mentioned this pull request Jan 31, 2025
vlasky added a commit to vlasky/sqlite-vec that referenced this pull request Nov 28, 2025
Implements WHERE constraints on the distance column in KNN queries, enabling
cursor-based pagination and range queries. Based on upstream PR asg017#166 by Alex
Garcia with completion and enhancements.
Features:
- Supports GT, GE, LT, LE operators on distance column
- Works with all vector types (float32, int8, bit)
- Compatible with partition keys, metadata, and auxiliary columns
- Multiple constraints can be combined (e.g., distance >= 3.0 AND distance <= 6.0)
Implementation:
- Added VEC0_IDXSTR_KIND_KNN_DISTANCE_CONSTRAINT to idxStr encoding
- Distance filtering applied during KNN search before top-k selection
- Cast f64 to f32 for comparison to match internal precision
Enhancements over original PR:
- Fixed variable shadowing in inner loops (i -> j)
- Added comprehensive test coverage (15 tests)
- Fixed bit/int8 vector type handling in tests
- Documented precision handling and pagination caveats
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Alex Garcia <alex@alex.garcia>
Co-Authored-By: Claude <noreply@anthropic.com>
@asg017
asg017 merged commit 611ca63 into mainFeb 13, 2026
realgetOff pushed a commit to realgetOff/sqlite-vec that referenced this pull request Aug 4, 2026
vec0BestIndex() sets aConstraintUsage[].omit = 1 on constraints against the
`distance` column, which tells SQLite the vtab will enforce them itself and
lets SQLite drop the term from the query plan. Only the FLAT chunk scan ever
read those constraints back out of idxStr, so on rescore, DiskANN and IVF
columns the predicate was silently discarded and rows violating it were
returned with no error.
This was a regression: asg017#166 added the distance constraints, and the ANN
backends merged six weeks later (asg017#276, asg017#277, asg017#278) never wired them up.
Introduce vec0_distance_constraints_satisfied() as the single point of truth
for the predicate and route every backend through it:
- FLAT keeps its pre-filter semantics; its four nested switch cases collapse
into one loop over the chunk bitmap (net -44 lines, snapshots unchanged).
- rescore filters the rescored float distances before the top-k truncation,
so a lower-bound constraint still yields k rows. Coarse quantized distances
from phase 1 are not comparable to a user-supplied threshold, so the filter
cannot be pushed down into the quantized scan.
- DiskANN and IVF compact their final result sets.
Also fixes a latent issue on the rescore path: when the constraint filters out
every candidate, result_k becomes 0 and sqlite3_malloc(0) returns NULL, which
the existing check would have misreported as SQLITE_NOMEM.
Adds parametrized coverage over flat / rescore-bit / rescore-int8 / diskann.
17 of the new assertions fail without this change and pass with it.
Fixesasg017#308
realgetOff pushed a commit to realgetOff/sqlite-vec that referenced this pull request Aug 4, 2026
vec0BestIndex() sets aConstraintUsage[].omit = 1 on constraints against the
`distance` column, which tells SQLite the vtab will enforce them itself and
lets SQLite drop the term from the query plan. Only the FLAT chunk scan ever
read those constraints back out of idxStr, so on rescore, DiskANN and IVF
columns the predicate was silently discarded and rows violating it were
returned with no error.
This was a regression: asg017#166 added the distance constraints, and the ANN
backends merged six weeks later (asg017#276, asg017#277, asg017#278) never wired them up.
Introduce vec0_distance_constraints_satisfied() as the single point of truth
for the predicate and route every backend through it:
- FLAT keeps its pre-filter semantics; its four nested switch cases collapse
into one loop over the chunk bitmap (net -44 lines, snapshots unchanged).
- rescore filters the rescored float distances before the top-k truncation,
so a lower-bound constraint still yields k rows. Coarse quantized distances
from phase 1 are not comparable to a user-supplied threshold, so the filter
cannot be pushed down into the quantized scan.
- DiskANN and IVF compact their final result sets.
Also fixes a latent issue on the rescore path: when the constraint filters out
every candidate, result_k becomes 0 and sqlite3_malloc(0) returns NULL, which
the existing check would have misreported as SQLITE_NOMEM.
Adds parametrized coverage over flat / rescore-bit / rescore-int8 / diskann,
plus ivf behind a build-flag skipif mirroring tests/conftest.py. 17 of the new
assertions fail without this change on a default build (22 with IVF enabled)
and all pass with it.
Fixesasg017#308
Sign up for freeto 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

@asg017