This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 9271c51e Fix partition field mapping for vector search source plans 
(#1211)
9271c51e is described below

commit 9271c51e1f1e7e65cbeb1cdb2779767953f70639
Author: Jingsong Lee <[email protected]>
AuthorDate: Sat Oct 24 08:53:08 2026 +0800

    Fix partition field mapping for vector search source plans (#1211)
---
 bindings/python/tests/test_vector_search.py | 25 +++++++++++++++++++++++++
 crates/paimon/src/table/de_vector_read.rs   | 20 ++++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/bindings/python/tests/test_vector_search.py 
b/bindings/python/tests/test_vector_search.py
index 23762d68..70b77cfc 100644
--- a/bindings/python/tests/test_vector_search.py
+++ b/bindings/python/tests/test_vector_search.py
@@ -67,6 +67,31 @@ def 
test_filters_accumulate_and_partition_uses_table_schema(table):
         result.splits()
 
 
[email protected]('mode', ['fast', 'full', 'detail'])
[email protected]('batch', [False, True])
+def test_indexed_partition_filter_uses_table_field_positions(table, mode, 
batch):
+    (table.new_global_index_build_builder().with_index_column('embedding')
+     .with_index_type('ivf-flat').with_options({
+         'ivf-flat.dimension': '2', 'ivf-flat.nlist': '1',
+         'ivf-flat.distance.metric': 'l2'}).execute())
+    (table.new_global_index_build_builder().with_index_column('id')
+     .with_index_type('btree').execute())
+    table = table.copy({'vector-index.search-mode': mode,
+                        'global-index.filter.refine-from-data': 'false'})
+    builder = 
(table.new_batch_vector_search_builder().with_vector_column('embedding')
+               .with_query_vectors([[1, 0], [1, 0]]).with_limit(3) if batch 
else _single(table))
+    # pt is field 2 in the table and field 0 in the partition row. An exact
+    # scalar lookup must retain the matching indexed vector in either API.
+    builder.with_filter({'method': 'equal', 'field': 'id', 'literals': [3]})
+    builder.with_partition_filter({'method': 'equal', 'field': 'pt', 
'literals': [1]})
+    results = builder.execute() if batch else [builder.execute()]
+    for result in results:
+        assert len(result) == 1
+        assert list(result.row_ids().values()) == [1.0]
+        rows = 
pa.Table.from_batches(result.new_read_builder().with_projection(['id']).read())
+        assert rows.column('id').to_pylist() == [3]
+
+
 @pytest.mark.parametrize('batch', [False, True])
 def test_partition_filter_rejects_data_fields(table, batch):
     builder = table.new_batch_vector_search_builder() if batch else 
table.new_vector_search_builder()
diff --git a/crates/paimon/src/table/de_vector_read.rs 
b/crates/paimon/src/table/de_vector_read.rs
index 912f8045..1704da8e 100644
--- a/crates/paimon/src/table/de_vector_read.rs
+++ b/crates/paimon/src/table/de_vector_read.rs
@@ -782,6 +782,26 @@ async fn evaluate_batch_vector_search_with_executor(
                 table.schema().partition_keys(),
             );
             if let Some(partition) = partition {
+                // The split helper projects onto the partition row, while the
+                // source scan expects positions in the complete table schema.
+                let mapping = table
+                    .schema()
+                    .partition_keys()
+                    .iter()
+                    .map(|name| {
+                        table
+                            .schema()
+                            .fields()
+                            .iter()
+                            .position(|field| field.name() == name)
+                    })
+                    .collect::<Vec<_>>();
+                let partition = 
partition.remap_field_index(&mapping).ok_or_else(|| {
+                    crate::Error::DataInvalid {
+                        message: "Cannot map source partition filter to table 
fields".into(),
+                        source: None,
+                    }
+                })?;
                 let partition_ids = Arc::new(
                     
super::search_scan_split::partition_row_ids_for_search_sources(
                         table,

Reply via email to