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 38598f6041 [core] Decide full-text row filters from data when no 
scalar index can evaluate them (#10381)
38598f6041 is described below

commit 38598f6041efb4f0daca3ee1cdd28980693cb952
Author: Eunbin Son <[email protected]>
AuthorDate: Mon Oct 5 21:41:07 2026 +0900

    [core] Decide full-text row filters from data when no scalar index can 
evaluate them (#10381)
---
 .../table/source/DataEvolutionFullTextRead.java    |  9 +++++-
 .../table/source/FullTextSearchBuilderTest.java    | 35 ++++++++++++++++++----
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/DataEvolutionFullTextRead.java
 
b/paimon-core/src/main/java/org/apache/paimon/table/source/DataEvolutionFullTextRead.java
index a3bbc6d1a6..1ed32cb51f 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/DataEvolutionFullTextRead.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/DataEvolutionFullTextRead.java
@@ -178,7 +178,9 @@ public class DataEvolutionFullTextRead implements 
FullTextRead {
      *       dropped, or a {@code contains} / {@code endsWith} / {@code like} 
leaf, which BTree
      *       answers with every non-null row), the candidates are refined by 
reading their filter
      *       columns if {@code global-index.filter.refine-from-data} allows 
it, and excluded
-     *       otherwise.
+     *       otherwise. When every index covering the filter columns declines 
the predicate, the
+     *       rows are excluded in {@code fast} and otherwise decided by 
reading their filter
+     *       columns.
      *   <li>Rows whose filter columns are not covered follow {@code 
scalar-index.search-mode}:
      *       excluded in {@code fast}, otherwise decided by reading their 
filter columns.
      * </ul>
@@ -247,6 +249,11 @@ public class DataEvolutionFullTextRead implements 
FullTextRead {
                 matched.or(fromIndex);
             } else {
                 warnUnindexedFilter();
+                if (table.coreOptions().scalarIndexSearchMode() != 
GlobalIndexSearchMode.FAST) {
+                    // No scalar index could evaluate the filter: decide these 
rows from the data,
+                    // like rows whose filter columns have no index.
+                    unindexed.or(decidedByIndex);
+                }
             }
         }
         if (!unindexed.isEmpty()) {
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
index 630c82b15d..7baeed2e10 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
@@ -31,6 +31,7 @@ import 
org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
 import org.apache.paimon.globalindex.ResultEntry;
 import org.apache.paimon.globalindex.ScoredGlobalIndexResult;
 import org.apache.paimon.globalindex.btree.BTreeGlobalIndexerFactory;
+import org.apache.paimon.globalindex.fmindex.FMGlobalIndexerFactory;
 import 
org.apache.paimon.globalindex.testfulltext.TestFullTextGlobalIndexReader;
 import 
org.apache.paimon.globalindex.testfulltext.TestFullTextGlobalIndexerFactory;
 import org.apache.paimon.index.GlobalIndexMeta;
@@ -914,6 +915,28 @@ public class FullTextSearchBuilderTest extends 
TableTestBase {
                 .containsExactly(5L);
     }
 
+    @ParameterizedTest(name = "scalar-index.search-mode={0}")
+    @CsvSource({"fast", "full", "detail"})
+    public void 
testFullTextSearchReadsFilterColumnsWhenScalarIndexDeclinesFilter(String 
scalarMode)
+            throws Exception {
+        // FM covers the filter column but cannot evaluate equality, so no 
index decides the
+        // rows. Like rows without an index, full and detail decide them from 
the data.
+        FileStoreTable table = createTable("full_text_declined_" + scalarMode, 
scalarMode, false);
+        writeDocuments(table, RANKED_DOCUMENTS);
+        buildAndCommitIndex(table, RANKED_DOCUMENTS);
+        buildAndCommitBTreeIndex(table, RANKED_DOCUMENTS, 
FMGlobalIndexerFactory.IDENTIFIER);
+
+        Predicate filter =
+                new PredicateBuilder(table.rowType())
+                        .equal(1, BinaryString.fromString("paimon zeta"));
+        RoaringNavigableMap64 rows = searchWithFilter(table, filter, 
1).results();
+        if (scalarMode.equals("fast")) {
+            assertThat(rows).isEmpty();
+        } else {
+            assertThat(rows).containsExactly(5L);
+        }
+    }
+
     @ParameterizedTest(name = "scalar-index.search-mode={0}, 
refine-from-data={1}")
     @CsvSource({"fast, true", "full, true", "fast, false", "full, false"})
     public void 
testFullTextSearchPartiallyIndexedConjunctionIsNeverRankedAsSuperset(
@@ -1947,16 +1970,18 @@ public class FullTextSearchBuilderTest extends 
TableTestBase {
 
     private void buildAndCommitBTreeIndex(FileStoreTable table, String[] 
documents)
             throws Exception {
+        buildAndCommitBTreeIndex(table, documents, 
BTreeGlobalIndexerFactory.IDENTIFIER);
+    }
+
+    private void buildAndCommitBTreeIndex(
+            FileStoreTable table, String[] documents, String indexType) throws 
Exception {
         Options options = table.coreOptions().toConfiguration();
         DataField textField = table.rowType().getField(TEXT_FIELD_NAME);
 
         GlobalIndexSingleColumnWriter writer =
                 (GlobalIndexSingleColumnWriter)
                         GlobalIndexBuilderUtils.createIndexWriter(
-                                table,
-                                BTreeGlobalIndexerFactory.IDENTIFIER,
-                                Collections.singletonList(textField),
-                                options);
+                                table, indexType, 
Collections.singletonList(textField), options);
         for (int i = 0; i < documents.length; i++) {
             writer.write(BinaryString.fromString(documents[i]), i);
         }
@@ -1970,7 +1995,7 @@ public class FullTextSearchBuilderTest extends 
TableTestBase {
                         table.coreOptions(),
                         rowRange,
                         textField.id(),
-                        BTreeGlobalIndexerFactory.IDENTIFIER,
+                        indexType,
                         entries);
 
         DataIncrement dataIncrement = DataIncrement.indexIncrement(indexFiles);

Reply via email to