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

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 913dab328c1 branch-4.2: [fix](fe) Pick the first matching Variant 
field pattern when choosing an index, like the BE #69730 (#69853)
913dab328c1 is described below

commit 913dab328c112072e774fa721dffcb1602a60747
Author: Jack <[email protected]>
AuthorDate: Fri Oct 30 09:40:37 2026 +0800

    branch-4.2: [fix](fe) Pick the first matching Variant field pattern when 
choosing an index, like the BE #69730 (#69853)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #69730
    
    Problem Summary: Backport #69730 to `branch-4.2`.
    
    A VARIANT column can declare predefined fields whose patterns overlap,
    for example the glob 'a.*' and the name 'a.b'. The BE writer
    (variant_util.cpp) routes a subcolumn to the first predefined field
    whose pattern matches its path and builds the index of that field.
    `OlapTable.getInvertedIndex` looped over all the fields without stopping
    and kept the last match, so a query on the same path chose the index of
    a different field than the one the data was written with, and fell back
    to another index or to a scan.
    
    The loop now stops at the first match, the same order the BE uses, so a
    write and a query on one path land on the same index.
    
    #### Branch adaptations
    
    On `branch-4.2` the predefined field's pattern type is the thrift
    `TPatternType` (master has `catalog.PatternType`), and `Index` takes
    `IndexDef.IndexType`. The `OlapTable.java` change applies unchanged
    around those lines. The test is master's test with two adaptations: it
    uses `TPatternType` and keeps the branch's own `IndexType` import, and
    it builds its lists and maps with Guava's `ImmutableList.of` /
    `ImmutableMap.of`, like the other tests of the file, because this
    branch's fe-core tests cannot use `List.of` / `Map.of`.
    
    The BE of `branch-4.2` also routes a subcolumn to the first matching
    predefined field: `generate_sub_column_info` in `variant_util.cpp`
    returns on the first match. So the FE change has the same effect on this
    branch. The commit is the cherry-pick of master `7832338fba`.
    
    ### Release note
    
    Fix the index chosen for a Variant subpath when the column declares
    overlapping field patterns.
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [x] Unit Test
    - The test of #69730:
    
InvertedIndexPropertiesTest.testOverlappingFieldPatternsPickTheFirstPredefinedFieldLikeTheBe.
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason
    
    - Behavior changed:
        - [ ] No.
    - [x] Yes. With overlapping field patterns, the first declared match
    decides the index instead of the last.
    
    - Does this need documentation?
        - [x] No.
        - [ ] Yes.
    
    ### Validation
    
    - `InvertedIndexPropertiesTest` on this branch, locally: 28 tests pass,
    including the new
    `testOverlappingFieldPatternsPickTheFirstPredefinedFieldLikeTheBe`; FE
    Checkstyle passes in the same build.
    - With `branch-4.2`'s own `OlapTable.java` put back, the new test fails
    with "expected idx_glob but was idx_exact", which is the last-match
    behavior this change removes.
    - CI on `branch-4.2` (`run buildall`) passed: compile, FE unit tests
    (12000 passed, 65 ignored, including the new test), P0, vault P0,
    external and non-concurrent regression, and the FE coverage check (100%
    of the 2 changed lines).
    - The first cloud P0 run failed 18 tests because the CI agent ran out of
    system memory: queries were cancelled with `MEM_LIMIT_EXCEEDED` ("sys
    available memory less than low water mark") and allocations failed. The
    rerun (`run cloud_p0`) passed with 4975 tests.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../java/org/apache/doris/catalog/OlapTable.java   |  7 +++++-
 .../analysis/InvertedIndexPropertiesTest.java      | 25 ++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index 6b2dd7fa1d3..85d27de8c40 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -3897,7 +3897,10 @@ public class OlapTable extends Table implements 
MTMVRelatedTableIf, GsonPostProc
                 : 
filteredInvertedIndexes.stream().filter(Index::isAnalyzedInvertedIndex).findFirst().orElse(null);
         }
 
-        // subPath is not empty, means it is a variant column, find the field 
pattern from children
+        // subPath is not empty, means it is a variant column, find the field 
pattern from children.
+        // The first match wins, the same as the BE writer that picks the 
physical index of a subcolumn
+        // (variant_util.cpp), so a write and a query on one path land on the 
same index even when
+        // the predefined patterns overlap.
         String subPathString = String.join(".", subPath);
         String fieldPattern = "";
         if (column.getChildren() != null) {
@@ -3909,6 +3912,7 @@ public class OlapTable extends Table implements 
MTMVRelatedTableIf, GsonPostProc
                                 .getPathMatcher("glob:" + childName);
                         if 
(matcher.matches(java.nio.file.Paths.get(subPathString))) {
                             fieldPattern = childName;
+                            break;
                         }
                     } catch (Exception e) {
                         continue;
@@ -3916,6 +3920,7 @@ public class OlapTable extends Table implements 
MTMVRelatedTableIf, GsonPostProc
                 } else if (child.getFieldPatternType() == 
TPatternType.MATCH_NAME) {
                     if (childName.equals(subPathString)) {
                         fieldPattern = childName;
+                        break;
                     }
                 }
             }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/InvertedIndexPropertiesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/InvertedIndexPropertiesTest.java
index 7ad2b49f704..f9ec838b43b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/InvertedIndexPropertiesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/InvertedIndexPropertiesTest.java
@@ -25,6 +25,8 @@ import org.apache.doris.catalog.Index;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
+import org.apache.doris.catalog.VariantField;
+import org.apache.doris.catalog.VariantType;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.indexpolicy.IndexPolicy;
 import org.apache.doris.indexpolicy.IndexPolicyMgr;
@@ -35,6 +37,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.info.IndexDefinition;
 import org.apache.doris.nereids.types.StringType;
 import org.apache.doris.thrift.TExprNode;
 import org.apache.doris.thrift.TInvertedIndexFileStorageFormat;
+import org.apache.doris.thrift.TPatternType;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -683,6 +686,28 @@ public class InvertedIndexPropertiesTest {
                 ImmutableMap.of("analyzer", "ik", "lower_case", "false"), 
"ik"));
     }
 
+    @Test
+    public void 
testOverlappingFieldPatternsPickTheFirstPredefinedFieldLikeTheBe() {
+        // The BE writer routes a subcolumn to the first predefined field 
whose pattern matches its path,
+        // so the query side has to pick the index of that same field when the 
patterns overlap.
+        Index globIndex = new Index(30, "idx_glob", ImmutableList.of("v"), 
IndexType.INVERTED,
+                ImmutableMap.of("field_pattern", "a.*"), "");
+        Index exactIndex = new Index(31, "idx_exact", ImmutableList.of("v"), 
IndexType.INVERTED,
+                ImmutableMap.of("field_pattern", "a.b"), "");
+        VariantField glob = new VariantField("a.*", Type.STRING, "", 
TPatternType.MATCH_NAME_GLOB);
+        VariantField exact = new VariantField("a.b", Type.STRING, "", 
TPatternType.MATCH_NAME);
+        OlapTable table = new OlapTable();
+        table.setIndexes(ImmutableList.of(globIndex, exactIndex));
+
+        Column globFirst = new Column("v", new VariantType(new 
ArrayList<>(ImmutableList.of(glob, exact))));
+        Assertions.assertSame(globIndex, table.getInvertedIndex(globFirst, 
ImmutableList.of("a", "b")));
+
+        Column exactFirst = new Column("v", new VariantType(new 
ArrayList<>(ImmutableList.of(exact, glob))));
+        Assertions.assertSame(exactIndex, table.getInvertedIndex(exactFirst, 
ImmutableList.of("a", "b")));
+        // A path only the glob covers still resolves to the glob's index.
+        Assertions.assertSame(globIndex, table.getInvertedIndex(exactFirst, 
ImmutableList.of("a", "c")));
+    }
+
     @Test
     public void testExplicitBuiltinIkFallsBackToTheOnlyLegacyIndex() {
         Column column = new Column("content", PrimitiveType.STRING);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to