Skip to main content

Module vector_filter

Module vector_filter 

Source
Expand description

Filtered vector search: strategies, the byte-budget cost model, and the planner that chooses between them (§5.3, D-007).

A vector query rarely arrives naked. The caller wants the ten nearest neighbours of an embedding among concepts reachable in two hops, and the two access paths cannot be nested: the DiskANN index is opaque to SQL predicates, and the relational filter is opaque to the index. Composing them is a cost decision, and this module makes it arithmetically rather than by rule of thumb.

§What changed in this cycle, and why the third strategy is gone

§5.3 specified three strategies. TwoPhaseTempTable was to push the candidate id set into the vector query as an allow-list, staging it through a TEMP table. Both of its mechanisms are absent from libSQL 0.9.30, and both were measured rather than reasoned about:

  • CREATE TEMP TABLE on the read connection fails with SQLITE_READONLY (8). PRAGMA query_only = ON (D-019) covers the TEMP database too, and D-019 is the runtime half of the write-serialization guarantee, so it is the strategy that gives way, not the pragma.
  • There is no allow-list to push into. vector_top_k refuses a fourth argument at runtime — “too many arguments on vector_top_k() - max 3” — and vectorIndexSearch in the bundled amalgamation rejects argc != 3 before it looks at anything else.

So the variant named an access path this engine does not offer, and the cost table priced an operation that cannot be issued. It is removed rather than kept as decoration: that is the precedent D-039 set with louvain_communities returning one community per node. If a future libSQL gains a constrained index walk, the variant comes back with a body, which is a smaller change than the confusion of shipping a name with nothing behind it.

§Why the strategy can never change the answer

PostFilter retrieves a generous k′ from the index and then discards whatever fails the predicate. When the filter is tight the answer set falls off the end of k′, and the classic implementation returns four rows for a top-ten query without saying so. That is a silent wrong answer, which Doctrine II exists to prevent, so it is not merely documented here — it is detected. When a post-filtered pass comes back short and the underlying index scan was saturated, the planner cannot conclude the matches do not exist, so it escalates to the exact strategy and says so at debug.

That gives the module its acceptance gate: the two strategies must agree, on every query, for every graph. Strategy is then a performance decision and nothing else, which is the only form in which a planner is safe.

Structs§

CostEstimate
One planning decision, with the arithmetic that produced it (D-007).
CostEstimator
Byte-budget cost model estimator for vector filter strategies (§5.3, D-007).
FilteredVectorSearch
A vector search restricted to the nodes a traversal reaches (§5.3).

Enums§

CandidateCount
What the counting probe learned about the candidate set.
VectorFilterStrategy
Strategy for combining vector search and graph traversal filters (§5.3).

Constants§

DEFAULT_BYTE_BUDGET
Default ceiling on the candidate set, in bytes.
DEFAULT_PROBE_CAP
Default cap on the counting probe.