Skip to content

Binary-search an integer metadata column's in (...) list - #10

Merged
vlasky merged 2 commits into
vlasky:mainfrom
MayCXC:metadata-in-bsearch-vlasky
Oct 28, 2026
Merged

vlasky merged 2 commits into
vlasky:mainfrom
MayCXC:metadata-in-bsearch-vlasky

Conversation

@MayCXC

@MayCXC MayCXC commented Oct 7, 2026

Copy link
Copy Markdown

Builds on #9: the first commit here is that PR's comparator fix, which this one depends on.

A KNN query with n in (...) on an integer metadata column compares every row of every chunk with every value in the list, so its cost grows with rows times list length. This sorts the list once when vec0Filter_knn reads it, and each row binary-searches it, the way rowid in (...) already works.

100,000 rows of float[8] with an integer metadata column, k = 10, n in (select value from json_each(?)), median of three:

list size main this PR
100 3.9 ms 1.2 ms
10,000 321.8 ms 6.5 ms
50,000 1117.1 ms 23.1 ms

On a real index of 116,625 768-dimension cosine vectors, a k = 50 KNN restricted to 112,112 ids through an integer metadata column went from 2.7 s to 178 ms.

test_vtab_in_integer_list checks the rows a 16-value list keeps, with values 2³² apart. It passes on main, which compares each value directly, and fails on a build that pairs this binary search with the comparator main has, which is why this sits on #9. The full suite passes (145 passed, 4 skipped, 259 snapshots).

The same change is open upstream as asg017#328.

MayCXC added 2 commits October 7, 2026 09:55
_cmp returned the i64 difference of its arguments as an int, and it orders
the `rowid in (...)` list of a KNN query before each row binary-searches it.
Two rowids 2**32 apart narrow to 0 and compare equal, so a row that is not in
the list matches: `rowid in (1, 2)` also returned rowid 2**32 + 1. A
difference wider than an int also changes sign, so the qsort leaves the list
out of order and the search misses rows that are in it: of 150 rowids drawn
from +/-2**62, 29 to 54 were missing per run. A difference wider than i64
overflows. _cmp now compares its arguments.

test_vec0_knn_rowid_in_far_apart covers both: an extra row 2**32 away, and
five rowids 3e9 apart with alternating signs, one of which the old
comparator loses. It fails on the old _cmp and passes on this one.
A KNN query's `n in (...)` on an integer metadata column compared every
row of every chunk against every value in the list, so its cost grew with
rows times list length. The list is now sorted once when vec0Filter_knn
reads it, and each row binary-searches it, as `rowid in (...)` already
does.

test_vtab_in_integer_list checks the rows a 16-value list keeps, with
values 2**32 apart; it fails on a build that pairs this search with the
comparator _cmp had before the previous commit.
mceachen added a commit to photostructure/sqlite-vec that referenced this pull request Oct 8, 2026
…eries

A KNN query compared each row's integer metadata value with the entries of
an `in (...)` list one by one until one matched. Ported from the unmerged
vlasky#10 (asg017#328), with a branchless search in
place of its bsearch(), which was slower than the scan on short lists.
@vlasky
vlasky merged commit 5cb513a into vlasky:main Oct 28, 2026
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.

2 participants