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

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


The following commit(s) were added to refs/heads/master by this push:
     new 045d8f0e2c [core] Limit vector live-row planning to indexed ranges 
(#8586)
045d8f0e2c is described below

commit 045d8f0e2c77e0bc3a99522489700646e6d76d57
Author: QuakeWang <[email protected]>
AuthorDate: Wed Jul 15 08:13:16 2026 +0800

    [core] Limit vector live-row planning to indexed ranges (#8586)
---
 .../paimon/table/source/AbstractVectorRead.java    | 11 +++--
 .../table/source/GlobalIndexLiveRowFilter.java     | 22 +++++++---
 .../table/source/VectorSearchBuilderTest.java      | 50 ++++++++++++++++++++++
 3 files changed, 75 insertions(+), 8 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractVectorRead.java
 
b/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractVectorRead.java
index 2789799bd0..931d78389a 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractVectorRead.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractVectorRead.java
@@ -127,13 +127,18 @@ public abstract class AbstractVectorRead implements 
Serializable {
     }
 
     protected List<RoaringNavigableMap64> 
preFilters(List<IndexVectorSearchSplit> splits) {
-        RoaringNavigableMap64 liveRows = 
GlobalIndexLiveRowFilter.liveRows(table, partitionFilter);
+        List<Range> indexedRowRanges = new ArrayList<>(splits.size());
+        for (IndexVectorSearchSplit split : splits) {
+            indexedRowRanges.add(new Range(split.rowRangeStart(), 
split.rowRangeEnd()));
+        }
+
+        RoaringNavigableMap64 liveRows =
+                GlobalIndexLiveRowFilter.liveRows(table, partitionFilter, 
indexedRowRanges);
         RoaringNavigableMap64 matchedRows = scalarMatchedRows(splits);
 
         List<RoaringNavigableMap64> includeRowIds = new 
ArrayList<>(splits.size());
         boolean hasFilter = false;
-        for (IndexVectorSearchSplit split : splits) {
-            Range splitRange = new Range(split.rowRangeStart(), 
split.rowRangeEnd());
+        for (Range splitRange : indexedRowRanges) {
             RoaringNavigableMap64 splitRows = bitmapOf(splitRange);
 
             RoaringNavigableMap64 include = new RoaringNavigableMap64();
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/GlobalIndexLiveRowFilter.java
 
b/paimon-core/src/main/java/org/apache/paimon/table/source/GlobalIndexLiveRowFilter.java
index 63a33479ad..6539629251 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/GlobalIndexLiveRowFilter.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/GlobalIndexLiveRowFilter.java
@@ -23,6 +23,7 @@ import org.apache.paimon.deletionvectors.DeletionVector;
 import org.apache.paimon.io.DataFileMeta;
 import org.apache.paimon.partition.PartitionPredicate;
 import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.source.snapshot.SnapshotReader;
 import org.apache.paimon.table.source.snapshot.TimeTravelUtil;
 import org.apache.paimon.utils.Range;
 import org.apache.paimon.utils.RoaringNavigableMap64;
@@ -39,6 +40,14 @@ class GlobalIndexLiveRowFilter {
     @Nullable
     static RoaringNavigableMap64 liveRows(
             @Nullable FileStoreTable table, @Nullable PartitionPredicate 
partitionFilter) {
+        return liveRows(table, partitionFilter, null);
+    }
+
+    @Nullable
+    static RoaringNavigableMap64 liveRows(
+            @Nullable FileStoreTable table,
+            @Nullable PartitionPredicate partitionFilter,
+            @Nullable List<Range> rowRanges) {
         if (table == null || !table.coreOptions().deletionVectorsEnabled()) {
             return null;
         }
@@ -48,14 +57,17 @@ class GlobalIndexLiveRowFilter {
             return null;
         }
 
-        RoaringNavigableMap64 liveRows = new RoaringNavigableMap64();
-        for (Split split :
+        SnapshotReader snapshotReader =
                 table.newSnapshotReader()
                         .withPartitionFilter(partitionFilter)
                         .withMode(ScanMode.ALL)
-                        .withSnapshot(snapshot)
-                        .read()
-                        .splits()) {
+                        .withSnapshot(snapshot);
+        if (rowRanges != null) {
+            snapshotReader.withRowRanges(rowRanges);
+        }
+
+        RoaringNavigableMap64 liveRows = new RoaringNavigableMap64();
+        for (Split split : snapshotReader.read().splits()) {
             if (split instanceof DataSplit) {
                 addLiveRows(table, liveRows, (DataSplit) split);
             }
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
index 93e219f442..f704e1f370 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
@@ -24,6 +24,7 @@ import org.apache.paimon.data.GenericArray;
 import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.data.InternalRow;
 import org.apache.paimon.data.serializer.InternalRowSerializer;
+import org.apache.paimon.fs.Path;
 import org.apache.paimon.globalindex.GlobalIndexBuilderUtils;
 import org.apache.paimon.globalindex.GlobalIndexMultiColumnWriter;
 import org.apache.paimon.globalindex.GlobalIndexResult;
@@ -220,6 +221,55 @@ public class VectorSearchBuilderTest extends TableTestBase 
{
         assertThat(readIds(table, result)).containsExactly(2, 3);
     }
 
+    @Test
+    public void testVectorLiveRowPlanningSkipsUnindexedDeletionVectors() 
throws Exception {
+        catalog.createTable(
+                identifier("vector_search_unindexed_deletion_vector"),
+                vectorSchemaBuilder(VECTOR_FIELD_NAME)
+                        .option(CoreOptions.DELETION_VECTORS_ENABLED.key(), 
"true")
+                        .build(),
+                false);
+        FileStoreTable table = 
getTable(identifier("vector_search_unindexed_deletion_vector"));
+
+        float[][] indexedVectors = {{0.0f, 0.0f}, {1.0f, 0.0f}};
+        writeVectors(table, indexedVectors);
+        writeVectors(table, new float[][] {{2.0f, 0.0f}, {3.0f, 0.0f}});
+        buildAndCommitVectorIndex(table, indexedVectors, new Range(0, 1));
+        commitDeletionVectors(table, 3L);
+
+        DeletionFile unindexedDeletionFile = null;
+        for (Split split : table.newSnapshotReader().read().splits()) {
+            if (!(split instanceof DataSplit)) {
+                continue;
+            }
+            DataSplit dataSplit = (DataSplit) split;
+            List<DeletionFile> deletionFiles = 
dataSplit.deletionFiles().orElse(null);
+            if (deletionFiles == null) {
+                continue;
+            }
+            for (int i = 0; i < dataSplit.dataFiles().size(); i++) {
+                if (dataSplit
+                        .dataFiles()
+                        .get(i)
+                        .nonNullRowIdRange()
+                        .hasIntersection(new Range(3, 3))) {
+                    unindexedDeletionFile = deletionFiles.get(i);
+                }
+            }
+        }
+        assertThat(unindexedDeletionFile).isNotNull();
+        assertThat(table.fileIO().delete(new 
Path(unindexedDeletionFile.path()), false)).isTrue();
+
+        GlobalIndexResult result =
+                table.newVectorSearchBuilder()
+                        .withVector(new float[] {0.0f, 0.0f})
+                        .withLimit(2)
+                        .withVectorColumn(VECTOR_FIELD_NAME)
+                        .executeLocal();
+
+        assertThat(result.results()).containsExactly(0L, 1L);
+    }
+
     @Test
     public void testBatchVectorSearchExcludesDeletedIndexedRows() throws 
Exception {
         catalog.createTable(

Reply via email to