vector-search: support scalar pre-filter before Top-K - #760
Conversation
| entries.push(ExecutionPreparedFilterEntry { | ||
| context: Arc::downgrade(context), | ||
| prepared_filter: Arc::downgrade(&prepared_filter), | ||
| }); |
There was a problem hiding this comment.
[MAJOR] The execution-scoped prefilter cache only retains a weak reference to the prepared filter, so one logical execution can resolve different target snapshots across partitions. Once an earlier partition stream is exhausted and drops the last strong reference, a later partition using the same live TaskContext creates a new OnceCell and resolves the table snapshot again.
Keep the prepared filter strongly owned for the complete execution across all partitions, releasing it only when that execution finishes. Please add a sequential-partition test that updates the target between partitions.
| .iter() | ||
| .any(|vector_search| vector_search.include_row_ids.is_some()) | ||
| .any(|vector_search| vector_search.effective_include_row_ids().is_some()) | ||
| { |
There was a problem hiding this comment.
[MAJOR] Filtered batch searches discard Lumina batch execution and rebuild the same shared allow-list once per query. Each fallback call materializes the shared RoaringTreemap into a new Vec<u64>, producing O(Q * B) conversion/allocation work plus Q sequential searches per shard.
When every query shares the same bitmap, materialize it once and call search_with_filter with the full query batch. Keep the per-query fallback only for differing filters.
Summary
Support scalar pre-filtering before Top-K for data-evolution/global-index vector search, including DataFusion literal and lateral
vector_searchqueries.The scalar predicate is evaluated against the same pinned snapshot as vector search, producing global row IDs that are localized per vector-index shard and passed to the vector backend as an allow-list. A normal Paimon read is used so scalar global indexes such as BTree can prune the filter read while residual evaluation, partial index coverage, and deletion visibility remain exact.
Changes
VectorSearchBuilder::with_filterandBatchVectorSearchBuilder::with_filteron the data-evolution path.vector_searchwhile retaining anInexactresidual correctness check.EXPLAINoutput.Testing
cargo fmt --all --checkgit diff --checkcargo test -p paimon --lib table::vector_search_builder::testscargo test -p paimon-datafusion --lib filter_pushdown::tests::test_vector_prefilter_rejects_nan_literalscargo test -p paimon-datafusion --test read_tables vector_search_tests::test_vindex_build_then_vector_search_query -- --nocapturecargo test -p paimon-datafusion --test read_tables vector_search_tests::test_vector_search_lateral_join_uses_query_vectors -- --nocapturecargo clippy -p paimon --lib --tests -- -D warningscargo clippy -p paimon-datafusion --lib --tests -- -D warningsCovered cases include filter-before-Top-K ordering, empty matches, batch reuse, non-zero shard offsets, raw fallback for unindexed rows, time-travel snapshot pinning, literal SQL, lateral SQL, mixed/cross-side conjuncts, and NaN pushdown safety.
Notes
VectorSearch::include_row_idspublic API remains unchanged; this PR addsPreparedVectorSearchFilterand shared-filter builder support.hybrid_searchfeature.