You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by ro...@apache.org on 2023/05/02 15:18:59 UTC

[pinot] branch master updated: Fix a bug in star-tree filter operator which can incorrecly filter documents (#10707)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new b72051c3eb Fix a bug in star-tree filter operator which can incorrecly filter documents (#10707)
b72051c3eb is described below

commit b72051c3ebda8f21c8120c7af4521d13ee96c7e8
Author: Xiaotian (Jackie) Jiang <17...@users.noreply.github.com>
AuthorDate: Wed May 3 00:18:51 2023 +0900

    Fix a bug in star-tree filter operator which can incorrecly filter documents (#10707)
    
    * Fix a bug in star-tree filter operator which can incorrecly filter documents
---
 .../startree/operator/StarTreeFilterOperator.java  | 37 ++++++----
 .../tests/StarTreeClusterIntegrationTest.java      | 78 ++++++++++------------
 .../tests/startree/StarTreeQueryGenerator.java     | 33 ++++-----
 .../startree/v2/builder/BaseSingleTreeBuilder.java | 12 ++--
 4 files changed, 84 insertions(+), 76 deletions(-)

diff --git a/pinot-core/src/main/java/org/apache/pinot/core/startree/operator/StarTreeFilterOperator.java b/pinot-core/src/main/java/org/apache/pinot/core/startree/operator/StarTreeFilterOperator.java
index b3bc75ea73..f46f7fca53 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/startree/operator/StarTreeFilterOperator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/startree/operator/StarTreeFilterOperator.java
@@ -203,19 +203,26 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
   @Nullable
   private StarTreeResult traverseStarTree() {
     MutableRoaringBitmap matchingDocIds = new MutableRoaringBitmap();
-    Set<String> globalRemainingPredicateColumns = Collections.emptySet();
-    boolean globalRemainingPredicateColumnsSet = false;
+    Set<String> globalRemainingPredicateColumns = null;
 
     StarTree starTree = _starTreeV2.getStarTree();
     List<String> dimensionNames = starTree.getDimensionNames();
     StarTreeNode starTreeRootNode = starTree.getRoot();
 
+    // Track whether we have found a leaf node added to the queue. If we have found a leaf node, and traversed to the
+    // level of the leave node, we can set globalRemainingPredicateColumns if not already set because we know the leaf
+    // node won't split further on other predicate columns.
+    boolean foundLeafNode = starTreeRootNode.isLeaf();
+
     // Use BFS to traverse the star tree
     Queue<StarTreeNode> queue = new ArrayDeque<>();
     queue.add(starTreeRootNode);
     int currentDimensionId = -1;
     Set<String> remainingPredicateColumns = new HashSet<>(_predicateEvaluatorsMap.keySet());
     Set<String> remainingGroupByColumns = new HashSet<>(_groupByColumns);
+    if (foundLeafNode) {
+      globalRemainingPredicateColumns = new HashSet<>(remainingPredicateColumns);
+    }
     IntSet matchingDictIds = null;
     StarTreeNode starTreeNode;
     while ((starTreeNode = queue.poll()) != null) {
@@ -225,6 +232,9 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
         String dimension = dimensionNames.get(dimensionId);
         remainingPredicateColumns.remove(dimension);
         remainingGroupByColumns.remove(dimension);
+        if (foundLeafNode && globalRemainingPredicateColumns == null) {
+          globalRemainingPredicateColumns = new HashSet<>(remainingPredicateColumns);
+        }
         matchingDictIds = null;
         currentDimensionId = dimensionId;
       }
@@ -240,14 +250,6 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
       // remaining predicate columns for this node
       if (starTreeNode.isLeaf()) {
         matchingDocIds.add((long) starTreeNode.getStartDocId(), starTreeNode.getEndDocId());
-        // Only set the global remaining predicate columns once because we traverse the tree with BFS, so the first leaf
-        // node always have all the remaining predicate columns
-        if (!globalRemainingPredicateColumnsSet) {
-          if (!remainingPredicateColumns.isEmpty()) {
-            globalRemainingPredicateColumns = new HashSet<>(remainingPredicateColumns);
-          }
-          globalRemainingPredicateColumnsSet = true;
-        }
         continue;
       }
 
@@ -257,8 +259,8 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
       // Only read star-node when the dimension is not in the global remaining predicate columns or group-by columns
       // because we cannot use star-node in such cases
       StarTreeNode starNode = null;
-      if (!globalRemainingPredicateColumns.contains(childDimension) && !remainingGroupByColumns.contains(
-          childDimension)) {
+      if ((globalRemainingPredicateColumns == null || !globalRemainingPredicateColumns.contains(childDimension))
+          && !remainingGroupByColumns.contains(childDimension)) {
         starNode = starTreeNode.getChildForDimensionValue(StarTreeNode.ALL);
       }
 
@@ -287,18 +289,22 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
           // star-node if so
           if (starNode != null && numMatchingDictIds >= numChildren - 1) {
             List<StarTreeNode> matchingChildNodes = new ArrayList<>();
+            boolean findLeafChildNode = false;
             while (childrenIterator.hasNext()) {
               StarTreeNode childNode = childrenIterator.next();
               if (matchingDictIds.contains(childNode.getDimensionValue())) {
                 matchingChildNodes.add(childNode);
+                findLeafChildNode |= childNode.isLeaf();
               }
             }
             if (matchingChildNodes.size() == numChildren - 1) {
               // All the child nodes (except for the star-node) match the predicate, use the star-node
               queue.add(starNode);
+              foundLeafNode |= starNode.isLeaf();
             } else {
               // Some child nodes do not match the predicate, use the matching child nodes
               queue.addAll(matchingChildNodes);
+              foundLeafNode |= findLeafChildNode;
             }
           } else {
             // Cannot use the star-node, use the matching child nodes
@@ -306,6 +312,7 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
               StarTreeNode childNode = childrenIterator.next();
               if (matchingDictIds.contains(childNode.getDimensionValue())) {
                 queue.add(childNode);
+                foundLeafNode |= childNode.isLeaf();
               }
             }
           }
@@ -318,6 +325,7 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
             // Child node might be null because the matching dictionary id might not exist under this branch
             if (childNode != null) {
               queue.add(childNode);
+              foundLeafNode |= childNode.isLeaf();
             }
           }
         }
@@ -327,6 +335,7 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
         if (starNode != null) {
           // Star-node exists, use it
           queue.add(starNode);
+          foundLeafNode |= starNode.isLeaf();
         } else {
           // Star-node does not exist or cannot be used, add all non-star nodes to the queue
           Iterator<? extends StarTreeNode> childrenIterator = starTreeNode.getChildrenIterator();
@@ -334,13 +343,15 @@ public class StarTreeFilterOperator extends BaseFilterOperator {
             StarTreeNode childNode = childrenIterator.next();
             if (childNode.getDimensionValue() != StarTreeNode.ALL) {
               queue.add(childNode);
+              foundLeafNode |= childNode.isLeaf();
             }
           }
         }
       }
     }
 
-    return new StarTreeResult(matchingDocIds, globalRemainingPredicateColumns);
+    return new StarTreeResult(matchingDocIds,
+        globalRemainingPredicateColumns != null ? globalRemainingPredicateColumns : Collections.emptySet());
   }
 
   /**
diff --git a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/StarTreeClusterIntegrationTest.java b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/StarTreeClusterIntegrationTest.java
index 87fb039d53..e36f3d6aac 100644
--- a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/StarTreeClusterIntegrationTest.java
+++ b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/StarTreeClusterIntegrationTest.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Random;
 import org.apache.commons.io.FileUtils;
 import org.apache.pinot.integration.tests.startree.SegmentInfoProvider;
 import org.apache.pinot.integration.tests.startree.StarTreeQueryGenerator;
@@ -33,11 +34,12 @@ import org.apache.pinot.spi.config.table.StarTreeIndexConfig;
 import org.apache.pinot.spi.config.table.TableConfig;
 import org.apache.pinot.spi.data.Schema;
 import org.apache.pinot.util.TestUtils;
-import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+
 
 /**
  * Integration test for Star-Tree based indexes.
@@ -66,6 +68,9 @@ public class StarTreeClusterIntegrationTest extends BaseClusterIntegrationTest {
           AggregationFunctionType.DISTINCTCOUNTBITMAP);
   private static final int NUM_QUERIES_TO_GENERATE = 100;
 
+  private final long _randomSeed = System.currentTimeMillis();
+  private final Random _random = new Random(_randomSeed);
+
   private StarTreeQueryGenerator _starTree1QueryGenerator;
   private StarTreeQueryGenerator _starTree2QueryGenerator;
 
@@ -95,27 +100,26 @@ public class StarTreeClusterIntegrationTest extends BaseClusterIntegrationTest {
     Schema schema = createSchema();
     addSchema(schema);
 
-    // Randomly pick some dimensions and metrics for star-trees
-    List<String> starTree1Dimensions = new ArrayList<>(NUM_STAR_TREE_DIMENSIONS);
-    List<String> starTree2Dimensions = new ArrayList<>(NUM_STAR_TREE_DIMENSIONS);
+    // Pick fixed dimensions and metrics for the first star-tree
+    List<String> starTree1Dimensions =
+        Arrays.asList("OriginCityName", "DepTimeBlk", "LongestAddGTime", "CRSDepTime", "DivArrDelay");
+    List<String> starTree1Metrics =
+        Arrays.asList("CarrierDelay", "DepDelay", "LateAircraftDelay", "ArrivalDelayGroups", "ArrDel15");
+    int starTree1MaxLeafRecords = 10;
+
+    // Randomly pick some dimensions and metrics for the second star-tree
     List<String> allDimensions = new ArrayList<>(schema.getDimensionNames());
-    Collections.shuffle(allDimensions);
-    for (int i = 0; i < NUM_STAR_TREE_DIMENSIONS; i++) {
-      starTree1Dimensions.add(allDimensions.get(2 * i));
-      starTree2Dimensions.add(allDimensions.get(2 * i + 1));
-    }
-    List<String> starTree1Metrics = new ArrayList<>(NUM_STAR_TREE_METRICS);
-    List<String> starTree2Metrics = new ArrayList<>(NUM_STAR_TREE_METRICS);
+    Collections.shuffle(allDimensions, _random);
+    List<String> starTree2Dimensions = allDimensions.subList(0, NUM_STAR_TREE_DIMENSIONS);
     List<String> allMetrics = new ArrayList<>(schema.getMetricNames());
-    Collections.shuffle(allMetrics);
-    for (int i = 0; i < NUM_STAR_TREE_METRICS; i++) {
-      starTree1Metrics.add(allMetrics.get(2 * i));
-      starTree2Metrics.add(allMetrics.get(2 * i + 1));
-    }
+    Collections.shuffle(allMetrics, _random);
+    List<String> starTree2Metrics = allMetrics.subList(0, NUM_STAR_TREE_METRICS);
+    int starTree2MaxLeafRecords = 100;
+
     TableConfig tableConfig = createOfflineTableConfig();
     tableConfig.getIndexingConfig().setStarTreeIndexConfigs(
-        Arrays.asList(getStarTreeIndexConfig(starTree1Dimensions, starTree1Metrics),
-            getStarTreeIndexConfig(starTree2Dimensions, starTree2Metrics)));
+        Arrays.asList(getStarTreeIndexConfig(starTree1Dimensions, starTree1Metrics, starTree1MaxLeafRecords),
+            getStarTreeIndexConfig(starTree2Dimensions, starTree2Metrics, starTree2MaxLeafRecords)));
     addTableConfig(tableConfig);
 
     // Unpack the Avro files
@@ -132,22 +136,23 @@ public class StarTreeClusterIntegrationTest extends BaseClusterIntegrationTest {
       aggregationFunctions.add(functionType.getName());
     }
     _starTree1QueryGenerator = new StarTreeQueryGenerator(DEFAULT_TABLE_NAME, starTree1Dimensions, starTree1Metrics,
-        segmentInfoProvider.getSingleValueDimensionValuesMap(), aggregationFunctions);
+        segmentInfoProvider.getSingleValueDimensionValuesMap(), aggregationFunctions, _random);
     _starTree2QueryGenerator = new StarTreeQueryGenerator(DEFAULT_TABLE_NAME, starTree2Dimensions, starTree2Metrics,
-        segmentInfoProvider.getSingleValueDimensionValuesMap(), aggregationFunctions);
+        segmentInfoProvider.getSingleValueDimensionValuesMap(), aggregationFunctions, _random);
 
     // Wait for all documents loaded
     waitForAllDocsLoaded(600_000L);
   }
 
-  private static StarTreeIndexConfig getStarTreeIndexConfig(List<String> dimensions, List<String> metrics) {
+  private static StarTreeIndexConfig getStarTreeIndexConfig(List<String> dimensions, List<String> metrics,
+      int maxLeafRecords) {
     List<String> functionColumnPairs = new ArrayList<>();
     for (AggregationFunctionType functionType : AGGREGATION_FUNCTION_TYPES) {
       for (String metric : metrics) {
         functionColumnPairs.add(new AggregationFunctionColumnPair(functionType, metric).toColumnName());
       }
     }
-    return new StarTreeIndexConfig(dimensions, null, functionColumnPairs, 100);
+    return new StarTreeIndexConfig(dimensions, null, functionColumnPairs, maxLeafRecords);
   }
 
   @Test
@@ -160,23 +165,13 @@ public class StarTreeClusterIntegrationTest extends BaseClusterIntegrationTest {
   }
 
   @Test
-  public void testPredicateOnMetrics()
+  public void testHardCodedQueries()
       throws Exception {
-    String starQuery;
-
-    // Query containing predicate on one metric only
-    starQuery = "SELECT SUM(DepDelayMinutes) FROM mytable WHERE DepDelay > 0";
-    testStarQuery(starQuery);
-    starQuery = "SELECT SUM(DepDelayMinutes) FROM mytable WHERE DepDelay BETWEEN 0 and 10000";
-    testStarQuery(starQuery);
-
-    // Query containing predicate on multiple metrics
-    starQuery = "SELECT SUM(DepDelayMinutes) FROM mytable WHERE DepDelay > 0 AND ArrDelay > 0";
-    testStarQuery(starQuery);
-
-    // Query containing predicate on multiple metrics and dimensions
-    starQuery = "SELECT SUM(DepDelayMinutes) FROM mytable WHERE DepDelay > 0 AND ArrDelay > 0 AND OriginStateName = "
-        + "'Massachusetts'";
+    // This query can test the case of one predicate matches all the child nodes but star-node cannot be used because
+    // the predicate is included as remaining predicate from another branch
+    String starQuery = "SELECT DepTimeBlk, COUNT(*) FROM mytable "
+        + "WHERE CRSDepTime BETWEEN 1137 AND 1849 AND DivArrDelay > 218 AND CRSDepTime NOT IN (35, 1633, 1457, 140) "
+        + "AND LongestAddGTime NOT IN (17, 105, 20, 22) GROUP BY DepTimeBlk ORDER BY DepTimeBlk";
     testStarQuery(starQuery);
   }
 
@@ -185,9 +180,10 @@ public class StarTreeClusterIntegrationTest extends BaseClusterIntegrationTest {
     String referenceQuery = "SET useStarTree = false; " + starQuery;
     JsonNode starResponse = postQuery(starQuery);
     JsonNode referenceResponse = postQuery(referenceQuery);
-    Assert.assertEquals(starResponse.get("resultTable"), referenceResponse.get("resultTable"),
-        "Query comparison failed for: \nStar Query: " + starQuery + "\nStar Response: " + starResponse
-            + "\nReference Query: " + referenceQuery + "\nReference Response: " + referenceResponse);
+    assertEquals(starResponse.get("resultTable"), referenceResponse.get("resultTable"), String.format(
+        "Query comparison failed for: \n"
+            + "Star Query: %s\nStar Response: %s\nReference Query: %s\nReference Response: %s\nRandom Seed: %d",
+        starQuery, starResponse, referenceQuery, referenceResponse, _randomSeed));
   }
 
   @AfterClass
diff --git a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/startree/StarTreeQueryGenerator.java b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/startree/StarTreeQueryGenerator.java
index 81a8ff1335..827aaa7245 100644
--- a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/startree/StarTreeQueryGenerator.java
+++ b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/startree/StarTreeQueryGenerator.java
@@ -51,7 +51,6 @@ public class StarTreeQueryGenerator {
   private static final int MAX_NUM_GROUP_BYS = 3;
   private static final int MAX_NUM_IN_VALUES = 5;
   private static final int SHUFFLE_THRESHOLD = 5 * MAX_NUM_IN_VALUES;
-  private static final Random RANDOM = new Random();
   // Add more comparators here to generate them in the 'WHERE' clause.
   private static final List<String> COMPARATORS = Arrays.asList("=", "<>", "<", ">", "<=", ">=");
 
@@ -60,14 +59,16 @@ public class StarTreeQueryGenerator {
   private final List<String> _metricColumns;
   private final Map<String, List<Object>> _singleValueDimensionValuesMap;
   private final List<String> _aggregationFunctions;
+  private final Random _random;
 
   public StarTreeQueryGenerator(String tableName, List<String> singleValueDimensionColumns, List<String> metricColumns,
-      Map<String, List<Object>> singleValueDimensionValuesMap, List<String> aggregationFunctions) {
+      Map<String, List<Object>> singleValueDimensionValuesMap, List<String> aggregationFunctions, Random random) {
     _tableName = tableName;
     _singleValueDimensionColumns = singleValueDimensionColumns;
     _metricColumns = metricColumns;
     _singleValueDimensionValuesMap = singleValueDimensionValuesMap;
     _aggregationFunctions = aggregationFunctions;
+    _random = random;
   }
 
   /**
@@ -77,7 +78,7 @@ public class StarTreeQueryGenerator {
    * @return aggregation function.
    */
   private String generateAggregation(String metricColumn) {
-    return String.format("%s(%s)", _aggregationFunctions.get(RANDOM.nextInt(_aggregationFunctions.size())),
+    return String.format("%s(%s)", _aggregationFunctions.get(_random.nextInt(_aggregationFunctions.size())),
         metricColumn);
   }
 
@@ -87,11 +88,11 @@ public class StarTreeQueryGenerator {
    * @return aggregation section.
    */
   private String generateAggregations() {
-    int numAggregations = RANDOM.nextInt(MAX_NUM_AGGREGATIONS) + 1;
+    int numAggregations = _random.nextInt(MAX_NUM_AGGREGATIONS) + 1;
     int numMetrics = _metricColumns.size();
     String[] aggregations = new String[numAggregations];
     for (int i = 0; i < numAggregations; i++) {
-      aggregations[i] = generateAggregation(_metricColumns.get(RANDOM.nextInt(numMetrics)));
+      aggregations[i] = generateAggregation(_metricColumns.get(_random.nextInt(numMetrics)));
     }
     return StringUtils.join(aggregations, ", ");
   }
@@ -105,10 +106,10 @@ public class StarTreeQueryGenerator {
   private String generateComparisonPredicate(String dimensionColumn) {
     StringBuilder stringBuilder = new StringBuilder(dimensionColumn);
 
-    stringBuilder.append(' ').append(COMPARATORS.get(RANDOM.nextInt(COMPARATORS.size()))).append(' ');
+    stringBuilder.append(' ').append(COMPARATORS.get(_random.nextInt(COMPARATORS.size()))).append(' ');
 
     List<Object> valueArray = _singleValueDimensionValuesMap.get(dimensionColumn);
-    Object value = valueArray.get(RANDOM.nextInt(valueArray.size()));
+    Object value = valueArray.get(_random.nextInt(valueArray.size()));
     if (value instanceof String) {
       stringBuilder.append('\'').append(((String) value).replaceAll("'", "''")).append('\'');
     } else {
@@ -128,8 +129,8 @@ public class StarTreeQueryGenerator {
     StringBuilder stringBuilder = new StringBuilder(dimensionColumn).append(BETWEEN);
 
     List<Object> valueArray = _singleValueDimensionValuesMap.get(dimensionColumn);
-    Object value1 = valueArray.get(RANDOM.nextInt(valueArray.size()));
-    Object value2 = valueArray.get(RANDOM.nextInt(valueArray.size()));
+    Object value1 = valueArray.get(_random.nextInt(valueArray.size()));
+    Object value2 = valueArray.get(_random.nextInt(valueArray.size()));
 
     Preconditions.checkState((value1 instanceof String && value2 instanceof String) || (value1 instanceof Number
         && value2 instanceof Number));
@@ -163,7 +164,7 @@ public class StarTreeQueryGenerator {
    */
   private String generateInPredicate(String dimensionColumn) {
     StringBuilder stringBuilder = new StringBuilder(dimensionColumn);
-    if (RANDOM.nextBoolean()) {
+    if (_random.nextBoolean()) {
       stringBuilder.append(IN).append('(');
     } else {
       stringBuilder.append(NOT_IN).append('(');
@@ -171,7 +172,7 @@ public class StarTreeQueryGenerator {
 
     List<Object> valueArray = _singleValueDimensionValuesMap.get(dimensionColumn);
     int size = valueArray.size();
-    int numValues = Math.min(RANDOM.nextInt(MAX_NUM_IN_VALUES) + 1, size);
+    int numValues = Math.min(_random.nextInt(MAX_NUM_IN_VALUES) + 1, size);
     if (size < SHUFFLE_THRESHOLD) {
       // For smaller size values, use shuffle strategy.
       Collections.shuffle(valueArray);
@@ -190,7 +191,7 @@ public class StarTreeQueryGenerator {
       // For larger size values, use random indices strategy.
       Set<Integer> indices = new HashSet<>();
       while (indices.size() < numValues) {
-        indices.add(RANDOM.nextInt(size));
+        indices.add(_random.nextInt(size));
       }
       boolean isFirst = true;
       for (int index : indices) {
@@ -218,7 +219,7 @@ public class StarTreeQueryGenerator {
    */
   @Nullable
   private String generatePredicates() {
-    int numPredicates = RANDOM.nextInt(MAX_NUM_PREDICATES + 1);
+    int numPredicates = _random.nextInt(MAX_NUM_PREDICATES + 1);
     if (numPredicates == 0) {
       return null;
     }
@@ -230,8 +231,8 @@ public class StarTreeQueryGenerator {
       if (i != 0) {
         stringBuilder.append(AND);
       }
-      String dimensionName = _singleValueDimensionColumns.get(RANDOM.nextInt(numDimensions));
-      switch (RANDOM.nextInt(3)) {
+      String dimensionName = _singleValueDimensionColumns.get(_random.nextInt(numDimensions));
+      switch (_random.nextInt(3)) {
         case 0:
           stringBuilder.append(generateComparisonPredicate(dimensionName));
           break;
@@ -251,7 +252,7 @@ public class StarTreeQueryGenerator {
    * Randomly generate the group-by columns, may return {@code null}.
    */
   private String generateGroupByColumns() {
-    int numColumns = RANDOM.nextInt(MAX_NUM_GROUP_BYS + 1);
+    int numColumns = _random.nextInt(MAX_NUM_GROUP_BYS + 1);
     if (numColumns == 0) {
       return null;
     }
diff --git a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/startree/v2/builder/BaseSingleTreeBuilder.java b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/startree/v2/builder/BaseSingleTreeBuilder.java
index b5f3f7a571..1c1b03832e 100644
--- a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/startree/v2/builder/BaseSingleTreeBuilder.java
+++ b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/startree/v2/builder/BaseSingleTreeBuilder.java
@@ -380,12 +380,12 @@ abstract class BaseSingleTreeBuilder implements SingleTreeBuilder {
         nodeDimensionValue = dimensionValue;
       }
     }
-    TreeNode laseNode = getNewNode();
-    laseNode._dimensionId = dimensionId;
-    laseNode._dimensionValue = nodeDimensionValue;
-    laseNode._startDocId = nodeStartDocId;
-    laseNode._endDocId = endDocId;
-    nodes.put(nodeDimensionValue, laseNode);
+    TreeNode lastNode = getNewNode();
+    lastNode._dimensionId = dimensionId;
+    lastNode._dimensionValue = nodeDimensionValue;
+    lastNode._startDocId = nodeStartDocId;
+    lastNode._endDocId = endDocId;
+    nodes.put(nodeDimensionValue, lastNode);
     return nodes;
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org