[core] Rerank primary-key vector candidates with exact distances#550
Conversation
75fb950 to
e2377cd
Compare
leaves12138
left a comment
There was a problem hiding this comment.
The rerank implementation and focused tests look solid overall, but refine-factor validation is currently data-dependent: an empty PK-vector plan returns before parsing the option. Please address the inline finding so invalid query/table configuration fails consistently.
| // separately from table options) so a broad query key cannot be overridden | ||
| // by a more specific table key: query options take precedence as a whole. | ||
| // `search_options` above is the merged view used only to drive the ANN read. | ||
| let refine_factor = configured_refine_factor( |
There was a problem hiding this comment.
configured_refine_factor is reached only after PkVectorScan::plan() and the plan.splits.is_empty() early return above. As a result, an empty PK-vector table accepts refine_factor=0 or refine_factor=abc and returns a normal empty reader, while the identical query starts failing as soon as the table has a searchable split. Java resolves this option in the PrimaryKeyVectorRead constructor before planning, and configuration validity should not depend on whether data currently exists. Please resolve the factor/search limit before the empty-plan return (ideally before planning/index I/O) and add an empty-plan regression test.
There was a problem hiding this comment.
Fixed in 1ab7337. configured_refine_factor / indexed_search_limit are now resolved before PkVectorScan::plan() and the empty-plan early return, so an invalid factor fails loud regardless of whether the table currently has searchable data — matching Java, which resolves this in the PrimaryKeyVectorRead constructor before planning. Added an empty-table regression test covering both invalid forms you named (0 → "must be positive", and a non-integer → "must be an integer") and both option sources (query and table options, since the fix moved resolution of both ahead of planning).
Reconcile the primary-key vector exact rerank feature onto the physical-coordinate read path. On a PK-vector table, execute_read() materializes rows in file-local physical coordinates. This adds: - Dual-limit bucket search: ANN/indexed candidates can over-fetch (indexed_limit = limit × refine_factor) while exact-fallback remains bounded by the final limit. - When a positive refine factor is configured, rerank the over-fetched ANN candidates by rereading their vectors from data files, recomputing exact distance, and re-running the global Top-K merge. - Fail-loud validation: null vector elements and out-of-range split indices return DataInvalid errors instead of panicking or silently corrupting results. Changes: - bucket_search returns separate indexed/exact lists, each bounded independently - PkVectorOrchestrator.search_candidates takes dual limits, returns OrchestratorSearchResult - merge_candidates performs global Top-K over the union of indexed and exact channels - rerank_indexed_positional reads vectors by file-local position, validates ownership order - append_batch_vectors rejects null child elements in vector arrays - build_indexed_splits validates split_index is in range before indexing Tests added: dual-limit bucket behavior, merge_candidates union Top-K, rerank kernel (ownership/dedup/NULL/dimension/file-not-in-plan), e2e refine integration, baseline regression (refine=0 preserves exact output), null element fail-loud, out-of-range split_index fail-loud.
The refine factor was resolved only after PkVectorScan::plan() and the empty-plan early return, so an empty primary-key vector table accepted an invalid or zero refine factor and returned an empty reader, while the same query started failing once the table had a searchable split. Configuration validity must not depend on whether data currently exists. Resolve the refine factor (and its search limit) before planning so an invalid factor fails loud regardless of table state. Add an empty-table regression test.
1ab7337 to
51c5c42
Compare
leaves12138
left a comment
There was a problem hiding this comment.
Re-reviewed at 51c5c42. The refine factor and derived indexed search limit are now resolved before planning and the empty-plan return, so invalid query or table configuration fails consistently; the new empty-table regression covers the reported cases. The dual-limit search and exact positional rerank remain aligned with the Java flow. Verified locally with git diff --check, cargo fmt --check, cargo test -p paimon --lib (1705 passed, 1 ignored), cargo test -p paimon --test pk_vector_baseline_test (8 passed), and cargo check -p paimon-datafusion.
Purpose
Part of #514. Adds exact rerank to primary-key vector search, mirroring Java
PrimaryKeyVectorRead's refine/rerank. On a PK-vector table,execute_read()materializes rows in file-local physical coordinates; this lets a configured refine factor over-fetch approximate (ANN) candidates and reorder them by exact distance for higher recall, while leaving the no-refine path byte-identical.Brief change log
bucket_searchreturns separate indexed/exact candidate lists, each bounded independently — the ANN/indexed channel over-fetches (indexed_limit = limit × refine_factor) while the exact-fallback channel stays bounded by the finallimit.PkVectorOrchestrator::search_candidatestakes the dual limits and returns anOrchestratorSearchResult;merge_candidatesruns a global Top-K over the union of the two channels.rerank_indexed_positionalrereads the over-fetched ANN candidates' vectors from their data files by file-local position (reusing the position read path), recomputes exact distance, and re-runs the global Top-K. Exact-fallback candidates are already exact and are not reranked.refine_factor/refine-factor/rerank_factor/rerank-factor, withfields.<col>.→ index-type →ivf.→ bare prefix order); factor 0 (unset) leavesindexed_limit == limit, so output is byte-identical to the no-rerank path.append_batch_vectorsrejects null child elements in vector arrays (never a defaulted 0.0),build_indexed_splitsvalidatessplit_indexis in range before indexing, and the rerank kernel fails loud on ownership/dedup/NULL/dimension/file-not-in-plan violations — never a panic or a silently corrupted result.Tests
merge_candidatesunion Top-K; rerank kernel (ownership order, dedup, NULL element, dimension mismatch, file-not-in-plan); end-to-end refine integration matching an exact brute-force ground truth.refine_factor == 0preserves the exact pre-rerank output (baseline + Java-fixture read-back tests unchanged).split_index.cargo test -p paimongreen;cargo build -p paimon-datafusionclean (cross-crateSendgate);cargo clippy -D warningsandcargo fmt --checkclean.API and Format
No on-disk format change, no new result columns.
refine_factor == 0(the default) is byte-identical to the current output.Documentation
Code comments only; no user-facing docs change.