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

jackie 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 b09e9e645f evaluate EQ queries against range index when available 
(#10043)
b09e9e645f is described below

commit b09e9e645feaf013b2257e9cfeaf33d1015892fb
Author: Richard Startin <richardstar...@apache.org>
AuthorDate: Tue Jan 10 22:59:37 2023 +0000

    evaluate EQ queries against range index when available (#10043)
---
 .../core/operator/filter/FilterOperatorUtils.java  |   5 +-
 .../filter/RangeIndexBasedFilterOperator.java      | 328 ++++++++-------------
 .../predicate/EqualsPredicateEvaluatorFactory.java |  44 ++-
 .../predicate/RangePredicateEvaluatorFactory.java  |  37 ++-
 .../filter/predicate/traits/DoubleRange.java       |  25 ++
 .../filter/predicate/traits/DoubleValue.java       |  23 ++
 .../filter/predicate/traits/FloatRange.java        |  25 ++
 .../filter/predicate/traits/FloatValue.java        |  23 ++
 .../operator/filter/predicate/traits/IntRange.java |  25 ++
 .../operator/filter/predicate/traits/IntValue.java |  23 ++
 .../filter/predicate/traits/LongRange.java         |  25 ++
 .../filter/predicate/traits/LongValue.java         |  23 ++
 .../org/apache/pinot/queries/RangeQueriesTest.java |  29 ++
 .../index/readers/BitSlicedRangeIndexReader.java   |  79 ++++-
 .../index/creator/BitSlicedIndexCreatorTest.java   |  36 +++
 .../segment/spi/index/reader/RangeIndexReader.java | 101 ++++++-
 16 files changed, 623 insertions(+), 228 deletions(-)

diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/FilterOperatorUtils.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/FilterOperatorUtils.java
index c343dcea88..aa738beab4 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/FilterOperatorUtils.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/FilterOperatorUtils.java
@@ -61,7 +61,7 @@ public class FilterOperatorUtils {
       if (dataSource.getDataSourceMetadata().isSorted() && 
dataSource.getDictionary() != null) {
         return new SortedIndexBasedFilterOperator(predicateEvaluator, 
dataSource, numDocs);
       }
-      if (dataSource.getRangeIndex() != null) {
+      if (RangeIndexBasedFilterOperator.canEvaluate(predicateEvaluator, 
dataSource)) {
         return new RangeIndexBasedFilterOperator(predicateEvaluator, 
dataSource, numDocs);
       }
       return new ScanBasedFilterOperator(predicateEvaluator, dataSource, 
numDocs, nullHandlingEnabled);
@@ -80,6 +80,9 @@ public class FilterOperatorUtils {
       if (dataSource.getInvertedIndex() != null) {
         return new BitmapBasedFilterOperator(predicateEvaluator, dataSource, 
numDocs);
       }
+      if (RangeIndexBasedFilterOperator.canEvaluate(predicateEvaluator, 
dataSource)) {
+        return new RangeIndexBasedFilterOperator(predicateEvaluator, 
dataSource, numDocs);
+      }
       return new ScanBasedFilterOperator(predicateEvaluator, dataSource, 
numDocs, nullHandlingEnabled);
     }
   }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/RangeIndexBasedFilterOperator.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/RangeIndexBasedFilterOperator.java
index 4b968ba2ed..277fa4429f 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/RangeIndexBasedFilterOperator.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/RangeIndexBasedFilterOperator.java
@@ -20,19 +20,24 @@ package org.apache.pinot.core.operator.filter;
 
 import java.util.Collections;
 import java.util.List;
+import org.apache.pinot.common.request.context.predicate.Predicate;
 import org.apache.pinot.core.common.Operator;
 import org.apache.pinot.core.operator.blocks.FilterBlock;
 import org.apache.pinot.core.operator.dociditerators.ScanBasedDocIdIterator;
 import org.apache.pinot.core.operator.docidsets.BitmapDocIdSet;
 import org.apache.pinot.core.operator.docidsets.FilterBlockDocIdSet;
 import org.apache.pinot.core.operator.filter.predicate.PredicateEvaluator;
-import 
org.apache.pinot.core.operator.filter.predicate.RangePredicateEvaluatorFactory.DoubleRawValueBasedRangePredicateEvaluator;
-import 
org.apache.pinot.core.operator.filter.predicate.RangePredicateEvaluatorFactory.FloatRawValueBasedRangePredicateEvaluator;
-import 
org.apache.pinot.core.operator.filter.predicate.RangePredicateEvaluatorFactory.IntRawValueBasedRangePredicateEvaluator;
-import 
org.apache.pinot.core.operator.filter.predicate.RangePredicateEvaluatorFactory.LongRawValueBasedRangePredicateEvaluator;
-import 
org.apache.pinot.core.operator.filter.predicate.RangePredicateEvaluatorFactory.SortedDictionaryBasedRangePredicateEvaluator;
+import org.apache.pinot.core.operator.filter.predicate.traits.DoubleRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.DoubleValue;
+import org.apache.pinot.core.operator.filter.predicate.traits.FloatRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.FloatValue;
+import org.apache.pinot.core.operator.filter.predicate.traits.IntRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.IntValue;
+import org.apache.pinot.core.operator.filter.predicate.traits.LongRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.LongValue;
 import org.apache.pinot.segment.spi.datasource.DataSource;
 import org.apache.pinot.segment.spi.index.reader.RangeIndexReader;
+import org.apache.pinot.spi.data.FieldSpec;
 import org.apache.pinot.spi.trace.FilterType;
 import org.apache.pinot.spi.trace.InvocationRecording;
 import org.apache.pinot.spi.trace.Tracing;
@@ -44,24 +49,32 @@ public class RangeIndexBasedFilterOperator extends 
BaseFilterOperator {
 
   private static final String EXPLAIN_NAME = "FILTER_RANGE_INDEX";
 
-  private final RangeEvaluator _rangeEvaluator;
-  private final PredicateEvaluator _rangePredicateEvaluator;
+  private final RangeIndexReader<ImmutableRoaringBitmap> _rangeIndexReader;
+  private final PredicateEvaluator _predicateEvaluator;
   private final DataSource _dataSource;
+  private final FieldSpec.DataType _parameterType;
   private final int _numDocs;
 
+  static boolean canEvaluate(PredicateEvaluator predicateEvaluator, DataSource 
dataSource) {
+    Predicate.Type type = predicateEvaluator.getPredicateType();
+    RangeIndexReader<?> rangeIndex = dataSource.getRangeIndex();
+    return rangeIndex != null && (type == Predicate.Type.RANGE || (type == 
Predicate.Type.EQ
+        && dataSource.getRangeIndex().isExact()));
+  }
+
   @SuppressWarnings("unchecked")
-  public RangeIndexBasedFilterOperator(PredicateEvaluator 
rangePredicateEvaluator, DataSource dataSource, int numDocs) {
-    _rangePredicateEvaluator = rangePredicateEvaluator;
-    _rangeEvaluator = 
RangeEvaluator.of((RangeIndexReader<ImmutableRoaringBitmap>) 
dataSource.getRangeIndex(),
-        rangePredicateEvaluator);
+  public RangeIndexBasedFilterOperator(PredicateEvaluator predicateEvaluator, 
DataSource dataSource, int numDocs) {
+    _predicateEvaluator = predicateEvaluator;
+    _rangeIndexReader = (RangeIndexReader<ImmutableRoaringBitmap>) 
dataSource.getRangeIndex();
     _dataSource = dataSource;
     _numDocs = numDocs;
+    _parameterType = predicateEvaluator.isDictionaryBased() ? 
FieldSpec.DataType.INT : predicateEvaluator.getDataType();
   }
 
   @Override
   protected FilterBlock getNextBlock() {
-    if (_rangeEvaluator.isExact()) {
-      ImmutableRoaringBitmap matches = _rangeEvaluator.getMatchingDocIds();
+    if (_rangeIndexReader.isExact()) {
+      ImmutableRoaringBitmap matches = getMatchingDocIds();
       recordFilter(matches);
       return new FilterBlock(new BitmapDocIdSet(matches, _numDocs));
     }
@@ -69,11 +82,11 @@ public class RangeIndexBasedFilterOperator extends 
BaseFilterOperator {
   }
 
   private FilterBlock evaluateLegacyRangeFilter() {
-    ImmutableRoaringBitmap matches = _rangeEvaluator.getMatchingDocIds();
+    ImmutableRoaringBitmap matches = getMatchingDocIds();
     // if the implementation cannot match the entire query exactly, it will
     // yield partial matches, which need to be verified by scanning. If it
     // can answer the query exactly, this will be null.
-    ImmutableRoaringBitmap partialMatches = 
_rangeEvaluator.getPartiallyMatchingDocIds();
+    ImmutableRoaringBitmap partialMatches = getPartiallyMatchingDocIds();
     // this branch is likely until RangeIndexReader reimplemented and enabled 
by default
     if (partialMatches == null) {
       return new FilterBlock(new BitmapDocIdSet(matches == null ? new 
MutableRoaringBitmap() : matches, _numDocs));
@@ -81,7 +94,7 @@ public class RangeIndexBasedFilterOperator extends 
BaseFilterOperator {
     // TODO: support proper null handling in range index.
     // Need to scan the first and last range as they might be partially matched
     ScanBasedFilterOperator scanBasedFilterOperator =
-        new ScanBasedFilterOperator(_rangePredicateEvaluator, _dataSource, 
_numDocs, false);
+        new ScanBasedFilterOperator(_predicateEvaluator, _dataSource, 
_numDocs, false);
     FilterBlockDocIdSet scanBasedDocIdSet = 
scanBasedFilterOperator.getNextBlock().getBlockDocIdSet();
     MutableRoaringBitmap docIds = ((ScanBasedDocIdIterator) 
scanBasedDocIdSet.iterator()).applyAnd(partialMatches);
     if (matches != null) {
@@ -97,24 +110,114 @@ public class RangeIndexBasedFilterOperator extends 
BaseFilterOperator {
     });
   }
 
+  ImmutableRoaringBitmap getMatchingDocIds() {
+    switch (_parameterType) {
+      case INT:
+        if (_predicateEvaluator instanceof IntValue) {
+          return _rangeIndexReader.getMatchingDocIds(((IntValue) 
_predicateEvaluator).getInt());
+        }
+        IntRange intRange = (IntRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getMatchingDocIds(intRange.getInclusiveLowerBound(),
+            intRange.getInclusiveUpperBound());
+      case LONG:
+        if (_predicateEvaluator instanceof LongValue) {
+          return _rangeIndexReader.getMatchingDocIds(((LongValue) 
_predicateEvaluator).getLong());
+        }
+        LongRange longRange = (LongRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getMatchingDocIds(longRange.getInclusiveLowerBound(),
+            longRange.getInclusiveUpperBound());
+      case FLOAT:
+        if (_predicateEvaluator instanceof FloatValue) {
+          return _rangeIndexReader.getMatchingDocIds(((FloatValue) 
_predicateEvaluator).getFloat());
+        }
+        FloatRange floatRange = (FloatRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getMatchingDocIds(floatRange.getInclusiveLowerBound(),
+            floatRange.getInclusiveUpperBound());
+      case DOUBLE:
+        if (_predicateEvaluator instanceof DoubleValue) {
+          return _rangeIndexReader.getMatchingDocIds(((DoubleValue) 
_predicateEvaluator).getDouble());
+        }
+        DoubleRange doubleRange = (DoubleRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getMatchingDocIds(doubleRange.getInclusiveLowerBound(),
+            doubleRange.getInclusiveUpperBound());
+      default:
+        throw unsupportedDataType(_parameterType);
+    }
+  }
+
+  ImmutableRoaringBitmap getPartiallyMatchingDocIds() {
+    assert !_rangeIndexReader.isExact();
+    switch (_parameterType) {
+      case INT:
+        IntRange intRange = (IntRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getPartiallyMatchingDocIds(intRange.getInclusiveLowerBound(),
+            intRange.getInclusiveUpperBound());
+      case LONG:
+        LongRange longRange = (LongRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getPartiallyMatchingDocIds(longRange.getInclusiveLowerBound(),
+            longRange.getInclusiveUpperBound());
+      case FLOAT:
+        FloatRange floatRange = (FloatRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getPartiallyMatchingDocIds(floatRange.getInclusiveLowerBound(),
+            floatRange.getInclusiveUpperBound());
+      case DOUBLE:
+        DoubleRange doubleRange = (DoubleRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getPartiallyMatchingDocIds(doubleRange.getInclusiveLowerBound(),
+            doubleRange.getInclusiveUpperBound());
+      default:
+        throw unsupportedDataType(_parameterType);
+    }
+  }
+
   @Override
   public boolean canOptimizeCount() {
-    return _rangeEvaluator.isExact();
+    return _rangeIndexReader.isExact();
   }
 
   @Override
   public int getNumMatchingDocs() {
-    return _rangeEvaluator.getNumMatchingDocs();
+    switch (_parameterType) {
+      case INT:
+        if (_predicateEvaluator instanceof IntValue) {
+          return _rangeIndexReader.getNumMatchingDocs(((IntValue) 
_predicateEvaluator).getInt());
+        }
+        IntRange intRange = (IntRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getNumMatchingDocs(intRange.getInclusiveLowerBound(),
+            intRange.getInclusiveUpperBound());
+      case LONG:
+        if (_predicateEvaluator instanceof LongValue) {
+          return _rangeIndexReader.getNumMatchingDocs(((LongValue) 
_predicateEvaluator).getLong());
+        }
+        LongRange longRange = (LongRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getNumMatchingDocs(longRange.getInclusiveLowerBound(),
+            longRange.getInclusiveUpperBound());
+      case FLOAT:
+        if (_predicateEvaluator instanceof FloatValue) {
+          return _rangeIndexReader.getNumMatchingDocs(((FloatValue) 
_predicateEvaluator).getFloat());
+        }
+        FloatRange floatRange = (FloatRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getNumMatchingDocs(floatRange.getInclusiveLowerBound(),
+            floatRange.getInclusiveUpperBound());
+      case DOUBLE:
+        if (_predicateEvaluator instanceof DoubleValue) {
+          return _rangeIndexReader.getNumMatchingDocs(((DoubleValue) 
_predicateEvaluator).getDouble());
+        }
+        DoubleRange doubleRange = (DoubleRange) _predicateEvaluator;
+        return 
_rangeIndexReader.getNumMatchingDocs(doubleRange.getInclusiveLowerBound(),
+            doubleRange.getInclusiveUpperBound());
+      default:
+        throw unsupportedDataType(_parameterType);
+    }
   }
 
   @Override
   public boolean canProduceBitmaps() {
-    return _rangeEvaluator.isExact();
+    return _rangeIndexReader.isExact();
   }
 
   @Override
   public BitmapCollection getBitmaps() {
-    return new BitmapCollection(_numDocs, false, 
_rangeEvaluator.getMatchingDocIds());
+    return new BitmapCollection(_numDocs, false, getMatchingDocIds());
   }
 
   @Override
@@ -124,187 +227,16 @@ public class RangeIndexBasedFilterOperator extends 
BaseFilterOperator {
 
   @Override
   public String toExplainString() {
-    return EXPLAIN_NAME + "(indexLookUp:range_index"
-        + ",operator:" + _rangePredicateEvaluator.getPredicateType()
-        + ",predicate:" + _rangePredicateEvaluator.getPredicate().toString()
-        + ')';
-  }
-
-  interface RangeEvaluator {
-    static RangeEvaluator of(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader,
-        PredicateEvaluator predicateEvaluator) {
-      if (predicateEvaluator instanceof 
SortedDictionaryBasedRangePredicateEvaluator) {
-        return new IntRangeEvaluator(rangeIndexReader,
-            (SortedDictionaryBasedRangePredicateEvaluator) predicateEvaluator);
-      } else {
-        switch (predicateEvaluator.getDataType()) {
-          case INT:
-            return new IntRangeEvaluator(rangeIndexReader,
-                (IntRawValueBasedRangePredicateEvaluator) predicateEvaluator);
-          case LONG:
-            return new LongRangeEvaluator(rangeIndexReader,
-                (LongRawValueBasedRangePredicateEvaluator) predicateEvaluator);
-          case FLOAT:
-            return new FloatRangeEvaluator(rangeIndexReader,
-                (FloatRawValueBasedRangePredicateEvaluator) 
predicateEvaluator);
-          case DOUBLE:
-            return new DoubleRangeEvaluator(rangeIndexReader,
-                (DoubleRawValueBasedRangePredicateEvaluator) 
predicateEvaluator);
-          default:
-            throw new IllegalStateException("String and Bytes data type not 
supported for Range Indexing");
-        }
-      }
-    }
-
-    ImmutableRoaringBitmap getMatchingDocIds();
-
-    ImmutableRoaringBitmap getPartiallyMatchingDocIds();
-
-    int getNumMatchingDocs();
-
-    boolean isExact();
-  }
-
-  private static final class IntRangeEvaluator implements RangeEvaluator {
-    final RangeIndexReader<ImmutableRoaringBitmap> _rangeIndexReader;
-    final int _min;
-    final int _max;
-
-    private IntRangeEvaluator(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader, int min, int max) {
-      _rangeIndexReader = rangeIndexReader;
-      _min = min;
-      _max = max;
-    }
-
-    IntRangeEvaluator(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader,
-        IntRawValueBasedRangePredicateEvaluator predicateEvaluator) {
-      this(rangeIndexReader, predicateEvaluator.getInclusiveLowerBound(), 
predicateEvaluator.getInclusiveUpperBound());
-    }
-
-    IntRangeEvaluator(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader,
-        SortedDictionaryBasedRangePredicateEvaluator predicateEvaluator) {
-      // NOTE: End dictionary id is exclusive in 
OfflineDictionaryBasedRangePredicateEvaluator.
-      this(rangeIndexReader, predicateEvaluator.getStartDictId(), 
predicateEvaluator.getEndDictId() - 1);
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getMatchingDocIds() {
-      return _rangeIndexReader.getMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getPartiallyMatchingDocIds() {
-      return _rangeIndexReader.getPartiallyMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public int getNumMatchingDocs() {
-      return _rangeIndexReader.getNumMatchingDocs(_min, _max);
-    }
-
-    @Override
-    public boolean isExact() {
-      return _rangeIndexReader.isExact();
-    }
+    return EXPLAIN_NAME + "(indexLookUp:range_index" + ",operator:" + 
_predicateEvaluator.getPredicateType()
+        + ",predicate:" + _predicateEvaluator.getPredicate().toString() + ')';
   }
 
-  private static final class LongRangeEvaluator implements RangeEvaluator {
-    final RangeIndexReader<ImmutableRoaringBitmap> _rangeIndexReader;
-    final long _min;
-    final long _max;
-
-    LongRangeEvaluator(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader,
-        LongRawValueBasedRangePredicateEvaluator predicateEvaluator) {
-      _rangeIndexReader = rangeIndexReader;
-      _min = predicateEvaluator.getInclusiveLowerBound();
-      _max = predicateEvaluator.getInclusiveUpperBound();
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getMatchingDocIds() {
-      return _rangeIndexReader.getMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getPartiallyMatchingDocIds() {
-      return _rangeIndexReader.getPartiallyMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public int getNumMatchingDocs() {
-      return _rangeIndexReader.getNumMatchingDocs(_min, _max);
-    }
-
-    @Override
-    public boolean isExact() {
-      return _rangeIndexReader.isExact();
-    }
+  static RuntimeException unsupportedPredicateType(Predicate.Type type) {
+    return new IllegalStateException("Range index cannot satisfy " + type);
   }
 
-  private static final class FloatRangeEvaluator implements RangeEvaluator {
-    final RangeIndexReader<ImmutableRoaringBitmap> _rangeIndexReader;
-    final float _min;
-    final float _max;
-
-    FloatRangeEvaluator(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader,
-        FloatRawValueBasedRangePredicateEvaluator predicateEvaluator) {
-      _rangeIndexReader = rangeIndexReader;
-      _min = predicateEvaluator.getInclusiveLowerBound();
-      _max = predicateEvaluator.getInclusiveUpperBound();
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getMatchingDocIds() {
-      return _rangeIndexReader.getMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getPartiallyMatchingDocIds() {
-      return _rangeIndexReader.getPartiallyMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public int getNumMatchingDocs() {
-      return _rangeIndexReader.getNumMatchingDocs(_min, _max);
-    }
-
-    @Override
-    public boolean isExact() {
-      return _rangeIndexReader.isExact();
-    }
-  }
-
-  private static final class DoubleRangeEvaluator implements RangeEvaluator {
-    final RangeIndexReader<ImmutableRoaringBitmap> _rangeIndexReader;
-    final double _min;
-    final double _max;
-
-    DoubleRangeEvaluator(RangeIndexReader<ImmutableRoaringBitmap> 
rangeIndexReader,
-        DoubleRawValueBasedRangePredicateEvaluator predicateEvaluator) {
-      _rangeIndexReader = rangeIndexReader;
-      _min = predicateEvaluator.getInclusiveLowerBound();
-      _max = predicateEvaluator.getInclusiveUpperBound();
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getMatchingDocIds() {
-      return _rangeIndexReader.getMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public ImmutableRoaringBitmap getPartiallyMatchingDocIds() {
-      return _rangeIndexReader.getPartiallyMatchingDocIds(_min, _max);
-    }
-
-    @Override
-    public int getNumMatchingDocs() {
-      return _rangeIndexReader.getNumMatchingDocs(_min, _max);
-    }
-
-    @Override
-    public boolean isExact() {
-      return _rangeIndexReader.isExact();
-    }
+  static RuntimeException unsupportedDataType(FieldSpec.DataType dataType) {
+    return new IllegalStateException("Range index does not support " + 
dataType);
   }
 
   private void recordFilter(ImmutableRoaringBitmap bitmap) {
@@ -312,7 +244,7 @@ public class RangeIndexBasedFilterOperator extends 
BaseFilterOperator {
     if (recording.isEnabled()) {
       recording.setNumDocsMatchingAfterFilter(bitmap == null ? 0 : 
bitmap.getCardinality());
       
recording.setColumnName(_dataSource.getDataSourceMetadata().getFieldSpec().getName());
-      recording.setFilter(FilterType.INDEX, 
_rangePredicateEvaluator.getPredicateType().name());
+      recording.setFilter(FilterType.INDEX, 
_predicateEvaluator.getPredicateType().name());
       recording.setNumDocsScanned(_numDocs);
     }
   }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/EqualsPredicateEvaluatorFactory.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/EqualsPredicateEvaluatorFactory.java
index 814a45253b..dcc3f34f65 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/EqualsPredicateEvaluatorFactory.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/EqualsPredicateEvaluatorFactory.java
@@ -21,6 +21,10 @@ package org.apache.pinot.core.operator.filter.predicate;
 import java.math.BigDecimal;
 import java.util.Arrays;
 import org.apache.pinot.common.request.context.predicate.EqPredicate;
+import org.apache.pinot.core.operator.filter.predicate.traits.DoubleValue;
+import org.apache.pinot.core.operator.filter.predicate.traits.FloatValue;
+import org.apache.pinot.core.operator.filter.predicate.traits.IntValue;
+import org.apache.pinot.core.operator.filter.predicate.traits.LongValue;
 import org.apache.pinot.segment.spi.index.reader.Dictionary;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
 import org.apache.pinot.spi.utils.BooleanUtils;
@@ -82,7 +86,8 @@ public class EqualsPredicateEvaluatorFactory {
     }
   }
 
-  private static final class DictionaryBasedEqPredicateEvaluator extends 
BaseDictionaryBasedPredicateEvaluator {
+  private static final class DictionaryBasedEqPredicateEvaluator extends 
BaseDictionaryBasedPredicateEvaluator
+      implements IntValue {
     final int _matchingDictId;
     final int[] _matchingDictIds;
 
@@ -128,9 +133,15 @@ public class EqualsPredicateEvaluatorFactory {
     public int[] getMatchingDictIds() {
       return _matchingDictIds;
     }
+
+    @Override
+    public int getInt() {
+      return _matchingDictId;
+    }
   }
 
-  private static final class IntRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class IntRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements IntValue {
     final int _matchingValue;
 
     IntRawValueBasedEqPredicateEvaluator(EqPredicate eqPredicate, int 
matchingValue) {
@@ -165,9 +176,15 @@ public class EqualsPredicateEvaluatorFactory {
       }
       return matches;
     }
+
+    @Override
+    public int getInt() {
+      return _matchingValue;
+    }
   }
 
-  private static final class LongRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class LongRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements LongValue {
     final long _matchingValue;
 
     LongRawValueBasedEqPredicateEvaluator(EqPredicate eqPredicate, long 
matchingValue) {
@@ -202,9 +219,15 @@ public class EqualsPredicateEvaluatorFactory {
       }
       return matches;
     }
+
+    @Override
+    public long getLong() {
+      return _matchingValue;
+    }
   }
 
-  private static final class FloatRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class FloatRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements FloatValue {
     final float _matchingValue;
 
     FloatRawValueBasedEqPredicateEvaluator(EqPredicate eqPredicate, float 
matchingValue) {
@@ -239,9 +262,15 @@ public class EqualsPredicateEvaluatorFactory {
       }
       return matches;
     }
+
+    @Override
+    public float getFloat() {
+      return _matchingValue;
+    }
   }
 
-  private static final class DoubleRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class DoubleRawValueBasedEqPredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements DoubleValue {
     final double _matchingValue;
 
     DoubleRawValueBasedEqPredicateEvaluator(EqPredicate eqPredicate, double 
matchingValue) {
@@ -276,6 +305,11 @@ public class EqualsPredicateEvaluatorFactory {
       }
       return matches;
     }
+
+    @Override
+    public double getDouble() {
+      return _matchingValue;
+    }
   }
 
   private static final class BigDecimalRawValueBasedEqPredicateEvaluator 
extends BaseRawValueBasedPredicateEvaluator {
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/RangePredicateEvaluatorFactory.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/RangePredicateEvaluatorFactory.java
index f3b1cef1af..346b8440e1 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/RangePredicateEvaluatorFactory.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/RangePredicateEvaluatorFactory.java
@@ -22,6 +22,10 @@ import com.google.common.base.Preconditions;
 import it.unimi.dsi.fastutil.ints.IntSet;
 import java.math.BigDecimal;
 import org.apache.pinot.common.request.context.predicate.RangePredicate;
+import org.apache.pinot.core.operator.filter.predicate.traits.DoubleRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.FloatRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.IntRange;
+import org.apache.pinot.core.operator.filter.predicate.traits.LongRange;
 import org.apache.pinot.segment.spi.index.reader.Dictionary;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
 import org.apache.pinot.spi.utils.BooleanUtils;
@@ -112,7 +116,8 @@ public class RangePredicateEvaluatorFactory {
     }
   }
 
-  public static final class SortedDictionaryBasedRangePredicateEvaluator 
extends BaseDictionaryBasedPredicateEvaluator {
+  public static final class SortedDictionaryBasedRangePredicateEvaluator 
extends BaseDictionaryBasedPredicateEvaluator
+      implements IntRange {
     final int _startDictId;
     // Exclusive
     final int _endDictId;
@@ -214,6 +219,16 @@ public class RangePredicateEvaluatorFactory {
     public int getNumMatchingItems() {
       return Math.max(_numMatchingDictIds, 0);
     }
+
+    @Override
+    public int getInclusiveLowerBound() {
+      return getStartDictId();
+    }
+
+    @Override
+    public int getInclusiveUpperBound() {
+      return getEndDictId() - 1;
+    }
   }
 
   private static final class UnsortedDictionaryBasedRangePredicateEvaluator
@@ -294,7 +309,8 @@ public class RangePredicateEvaluatorFactory {
     }
   }
 
-  public static final class IntRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class IntRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements IntRange {
     final int _inclusiveLowerBound;
     final int _inclusiveUpperBound;
 
@@ -315,10 +331,12 @@ public class RangePredicateEvaluatorFactory {
       }
     }
 
+    @Override
     public int getInclusiveLowerBound() {
       return _inclusiveLowerBound;
     }
 
+    @Override
     public int getInclusiveUpperBound() {
       return _inclusiveUpperBound;
     }
@@ -347,7 +365,8 @@ public class RangePredicateEvaluatorFactory {
     }
   }
 
-  public static final class LongRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class LongRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements LongRange {
     final long _inclusiveLowerBound;
     final long _inclusiveUpperBound;
 
@@ -368,10 +387,12 @@ public class RangePredicateEvaluatorFactory {
       }
     }
 
+    @Override
     public long getInclusiveLowerBound() {
       return _inclusiveLowerBound;
     }
 
+    @Override
     public long getInclusiveUpperBound() {
       return _inclusiveUpperBound;
     }
@@ -400,7 +421,8 @@ public class RangePredicateEvaluatorFactory {
     }
   }
 
-  public static final class FloatRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class FloatRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator
+      implements FloatRange {
     final float _inclusiveLowerBound;
     final float _inclusiveUpperBound;
 
@@ -421,10 +443,12 @@ public class RangePredicateEvaluatorFactory {
       }
     }
 
+    @Override
     public float getInclusiveLowerBound() {
       return _inclusiveLowerBound;
     }
 
+    @Override
     public float getInclusiveUpperBound() {
       return _inclusiveUpperBound;
     }
@@ -453,7 +477,8 @@ public class RangePredicateEvaluatorFactory {
     }
   }
 
-  public static final class DoubleRawValueBasedRangePredicateEvaluator extends 
BaseRawValueBasedPredicateEvaluator {
+  private static final class DoubleRawValueBasedRangePredicateEvaluator 
extends BaseRawValueBasedPredicateEvaluator
+      implements DoubleRange {
     final double _inclusiveLowerBound;
     final double _inclusiveUpperBound;
 
@@ -474,10 +499,12 @@ public class RangePredicateEvaluatorFactory {
       }
     }
 
+    @Override
     public double getInclusiveLowerBound() {
       return _inclusiveLowerBound;
     }
 
+    @Override
     public double getInclusiveUpperBound() {
       return _inclusiveUpperBound;
     }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/DoubleRange.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/DoubleRange.java
new file mode 100644
index 0000000000..bd2fe790b6
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/DoubleRange.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface DoubleRange {
+  double getInclusiveLowerBound();
+
+  double getInclusiveUpperBound();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/DoubleValue.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/DoubleValue.java
new file mode 100644
index 0000000000..27b29f93e4
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/DoubleValue.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface DoubleValue {
+  double getDouble();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/FloatRange.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/FloatRange.java
new file mode 100644
index 0000000000..44b38ae626
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/FloatRange.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface FloatRange {
+  float getInclusiveLowerBound();
+
+  float getInclusiveUpperBound();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/FloatValue.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/FloatValue.java
new file mode 100644
index 0000000000..4dac96a049
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/FloatValue.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface FloatValue {
+  float getFloat();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/IntRange.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/IntRange.java
new file mode 100644
index 0000000000..c5b77d487f
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/IntRange.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface IntRange {
+  int getInclusiveLowerBound();
+
+  int getInclusiveUpperBound();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/IntValue.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/IntValue.java
new file mode 100644
index 0000000000..4e3aa09ce1
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/IntValue.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface IntValue {
+  int getInt();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/LongRange.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/LongRange.java
new file mode 100644
index 0000000000..f3a4d17aeb
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/LongRange.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface LongRange {
+  long getInclusiveLowerBound();
+
+  long getInclusiveUpperBound();
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/LongValue.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/LongValue.java
new file mode 100644
index 0000000000..62d4c097d3
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/predicate/traits/LongValue.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.filter.predicate.traits;
+
+public interface LongValue {
+  long getLong();
+}
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/queries/RangeQueriesTest.java 
b/pinot-core/src/test/java/org/apache/pinot/queries/RangeQueriesTest.java
index 44ba46b2db..099981d681 100644
--- a/pinot-core/src/test/java/org/apache/pinot/queries/RangeQueriesTest.java
+++ b/pinot-core/src/test/java/org/apache/pinot/queries/RangeQueriesTest.java
@@ -145,6 +145,16 @@ public class RangeQueriesTest extends BaseQueriesTest {
         {buildSelectionQuery(RAW_LONG_COL, 250, 500, false), 250, 500, false},
         {buildSelectionQuery(RAW_FLOAT_COL, 250, 500, false), 250, 500, false},
         {buildSelectionQuery(RAW_DOUBLE_COL, 250, 500, false), 250, 500, 
false},
+        {buildSelectionQuery(DICTIONARIZED_INT_COL, 300), 300, 300, true},
+        {buildSelectionQuery(RAW_INT_COL, 300), 300, 300, true},
+        {buildSelectionQuery(RAW_LONG_COL, 300), 300, 300, true},
+        {buildSelectionQuery(RAW_FLOAT_COL, 300), 300, 300, true},
+        {buildSelectionQuery(RAW_DOUBLE_COL, 300), 300, 300, true},
+        {buildSelectionQuery(DICTIONARIZED_INT_COL, 301), 301, 301, true},
+        {buildSelectionQuery(RAW_INT_COL, 301), 301, 301, true},
+        {buildSelectionQuery(RAW_LONG_COL, 301), 301, 301, true},
+        {buildSelectionQuery(RAW_FLOAT_COL, 301), 301, 301, true},
+        {buildSelectionQuery(RAW_DOUBLE_COL, 301), 301, 301, true}
     };
   }
 
@@ -158,6 +168,11 @@ public class RangeQueriesTest extends BaseQueriesTest {
     }
   }
 
+  private static String buildSelectionQuery(String filterCol, Number value) {
+      return "select " + RAW_INT_COL + " from " + RAW_TABLE_NAME + " where " + 
filterCol + " = "
+              + formatValue(filterCol, value);
+  }
+
   @DataProvider
   public static Object[][] countTestCases() {
     return new Object[][]{
@@ -171,6 +186,16 @@ public class RangeQueriesTest extends BaseQueriesTest {
         {buildCountQuery(RAW_LONG_COL, 250, 500, false), 2},
         {buildCountQuery(RAW_FLOAT_COL, 250, 500, false), 2},
         {buildCountQuery(RAW_DOUBLE_COL, 250, 500, false), 2},
+        {buildCountQuery(DICTIONARIZED_INT_COL, 300), 1},
+        {buildCountQuery(RAW_INT_COL, 300), 1},
+        {buildCountQuery(RAW_LONG_COL, 300), 1},
+        {buildCountQuery(RAW_FLOAT_COL, 300), 1},
+        {buildCountQuery(RAW_DOUBLE_COL, 300), 1},
+        {buildCountQuery(DICTIONARIZED_INT_COL, 301), 0},
+        {buildCountQuery(RAW_INT_COL, 301), 0},
+        {buildCountQuery(RAW_LONG_COL, 301), 0},
+        {buildCountQuery(RAW_FLOAT_COL, 301), 0},
+        {buildCountQuery(RAW_DOUBLE_COL, 301), 0}
     };
   }
 
@@ -184,6 +209,10 @@ public class RangeQueriesTest extends BaseQueriesTest {
     }
   }
 
+  private static String buildCountQuery(String filterCol, Number value) {
+    return "select count(*) from " + RAW_TABLE_NAME + " where " + filterCol + 
" = " + formatValue(filterCol, value);
+  }
+
   private static String buildFilter(String filterCol, Number min, Number max) {
     switch (filterCol) {
       case DICTIONARIZED_INT_COL:
diff --git 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/BitSlicedRangeIndexReader.java
 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/BitSlicedRangeIndexReader.java
index 0fde2d9ddc..7b422b74d6 100644
--- 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/BitSlicedRangeIndexReader.java
+++ 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/readers/BitSlicedRangeIndexReader.java
@@ -20,7 +20,6 @@ package org.apache.pinot.segment.local.segment.index.readers;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import javax.annotation.Nullable;
 import 
org.apache.pinot.segment.local.segment.creator.impl.inv.BitSlicedRangeIndexCreator;
 import org.apache.pinot.segment.local.utils.FPOrdering;
 import org.apache.pinot.segment.spi.ColumnMetadata;
@@ -94,6 +93,32 @@ public class BitSlicedRangeIndexReader implements 
RangeIndexReader<ImmutableRoar
     return queryRangeBitmapCardinality(FPOrdering.ordinalOf(min), 
FPOrdering.ordinalOf(max), 0xFFFFFFFFFFFFFFFFL);
   }
 
+  @Override
+  public int getNumMatchingDocs(int value) {
+    if (value < _min) {
+      return 0;
+    }
+    return queryRangeBitmapCardinality(value - _min, _max - _min);
+  }
+
+  @Override
+  public int getNumMatchingDocs(long value) {
+    if (value < _min) {
+      return 0;
+    }
+    return queryRangeBitmapCardinality(value - _min, _max - _min);
+  }
+
+  @Override
+  public int getNumMatchingDocs(float value) {
+    return queryRangeBitmapCardinality(FPOrdering.ordinalOf(value), 
0xFFFFFFFFL);
+  }
+
+  @Override
+  public int getNumMatchingDocs(double value) {
+    return queryRangeBitmapCardinality(FPOrdering.ordinalOf(value), 
0xFFFFFFFFFFFFFFFFL);
+  }
+
   @Override
   public ImmutableRoaringBitmap getMatchingDocIds(int min, int max) {
     // TODO: Handle this before reading the range index
@@ -130,36 +155,39 @@ public class BitSlicedRangeIndexReader implements 
RangeIndexReader<ImmutableRoar
     return queryRangeBitmap(FPOrdering.ordinalOf(min), 
FPOrdering.ordinalOf(max), 0xFFFFFFFFFFFFFFFFL);
   }
 
-  // this index supports exact matches, so always return null for partial 
matches
-
-  @Nullable
   @Override
-  public ImmutableRoaringBitmap getPartiallyMatchingDocIds(int min, int max) {
-    return null;
+  public ImmutableRoaringBitmap getMatchingDocIds(int value) {
+    if (value < _min) {
+      return new MutableRoaringBitmap();
+    }
+    return queryRangeBitmap(value - _min, _max - _min);
   }
 
-  @Nullable
   @Override
-  public ImmutableRoaringBitmap getPartiallyMatchingDocIds(long min, long max) 
{
-    return null;
+  public ImmutableRoaringBitmap getMatchingDocIds(long value) {
+    if (value < _min) {
+      return new MutableRoaringBitmap();
+    }
+    return queryRangeBitmap(value - _min, _max - _min);
   }
 
-  @Nullable
   @Override
-  public ImmutableRoaringBitmap getPartiallyMatchingDocIds(float min, float 
max) {
-    return null;
+  public ImmutableRoaringBitmap getMatchingDocIds(float value) {
+    return queryRangeBitmap(FPOrdering.ordinalOf(value), 0xFFFFFFFFL);
   }
 
-  @Nullable
   @Override
-  public ImmutableRoaringBitmap getPartiallyMatchingDocIds(double min, double 
max) {
-    return null;
+  public ImmutableRoaringBitmap getMatchingDocIds(double value) {
+    return queryRangeBitmap(FPOrdering.ordinalOf(value), 0xFFFFFFFFFFFFFFFFL);
   }
 
   private ImmutableRoaringBitmap queryRangeBitmap(long min, long max, long 
columnMax) {
     RangeBitmap rangeBitmap = mapRangeBitmap();
     if (Long.compareUnsigned(max, columnMax) < 0) {
       if (Long.compareUnsigned(min, 0) > 0) {
+        if (min == max) {
+          return rangeBitmap.eq(min).toMutableRoaringBitmap();
+        }
         return rangeBitmap.between(min, max).toMutableRoaringBitmap();
       }
       return rangeBitmap.lte(max).toMutableRoaringBitmap();
@@ -173,10 +201,22 @@ public class BitSlicedRangeIndexReader implements 
RangeIndexReader<ImmutableRoar
     }
   }
 
+  private ImmutableRoaringBitmap queryRangeBitmap(long value, long columnMax) {
+    RangeBitmap rangeBitmap = mapRangeBitmap();
+    if (Long.compareUnsigned(value, columnMax) < 0) {
+      return rangeBitmap.eq(value).toMutableRoaringBitmap();
+    } else {
+      return new MutableRoaringBitmap();
+    }
+  }
+
   private int queryRangeBitmapCardinality(long min, long max, long columnMax) {
     RangeBitmap rangeBitmap = mapRangeBitmap();
     if (Long.compareUnsigned(max, columnMax) < 0) {
       if (Long.compareUnsigned(min, 0) > 0) {
+        if (min == max) {
+          return (int) rangeBitmap.eqCardinality(min);
+        }
         return (int) rangeBitmap.betweenCardinality(min, max);
       }
       return (int) rangeBitmap.lteCardinality(max);
@@ -188,6 +228,15 @@ public class BitSlicedRangeIndexReader implements 
RangeIndexReader<ImmutableRoar
     }
   }
 
+  private int queryRangeBitmapCardinality(long value, long columnMax) {
+    RangeBitmap rangeBitmap = mapRangeBitmap();
+    if (Long.compareUnsigned(value, columnMax) < 0) {
+      return (int) rangeBitmap.eqCardinality(value);
+    } else {
+      return 0;
+    }
+  }
+
   private RangeBitmap mapRangeBitmap() {
     // note that this is a very cheap operation, no deserialization is required
     ByteBuffer buffer = _dataBuffer.toDirectByteBuffer(_offset, (int) 
(_dataBuffer.size() - _offset));
diff --git 
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/creator/BitSlicedIndexCreatorTest.java
 
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/creator/BitSlicedIndexCreatorTest.java
index 28513e1766..d80723c352 100644
--- 
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/creator/BitSlicedIndexCreatorTest.java
+++ 
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/creator/BitSlicedIndexCreatorTest.java
@@ -171,9 +171,12 @@ public class BitSlicedIndexCreatorTest {
       }
       testRange(reader, dataset, prev, prev + 1);
       testRange(reader, dataset, prev + 1, prev + 1);
+      testPoint(reader, dataset, prev + 1);
       testRange(reader, dataset, prev + 1, Integer.MAX_VALUE);
       testRange(reader, dataset, Integer.MAX_VALUE, Integer.MAX_VALUE);
       testRange(reader, dataset, Integer.MIN_VALUE, Integer.MAX_VALUE);
+      testPoint(reader, dataset, Integer.MIN_VALUE);
+      testPoint(reader, dataset, Integer.MAX_VALUE);
     } finally {
       FileUtils.forceDelete(rangeIndexFile);
     }
@@ -192,6 +195,12 @@ public class BitSlicedIndexCreatorTest {
     }
   }
 
+  private static void testPoint(BitSlicedRangeIndexReader reader, 
Dataset<int[]> dataset, int value) {
+    ImmutableRoaringBitmap reference = dataset.scan(value, value);
+    assertEquals(reader.getMatchingDocIds(value), reference);
+    assertEquals(reader.getNumMatchingDocs(value), reference.getCardinality());
+  }
+
   private void testLong(Dataset<long[]> dataset)
       throws IOException {
     ColumnMetadata metadata = dataset.toColumnMetadata();
@@ -216,9 +225,12 @@ public class BitSlicedIndexCreatorTest {
       }
       testRange(reader, dataset, prev, prev + 1);
       testRange(reader, dataset, prev + 1, prev + 1);
+      testPoint(reader, dataset, prev + 1);
       testRange(reader, dataset, prev + 1, Long.MAX_VALUE);
       testRange(reader, dataset, Long.MAX_VALUE, Long.MAX_VALUE);
       testRange(reader, dataset, Long.MIN_VALUE, Long.MAX_VALUE);
+      testPoint(reader, dataset, Long.MIN_VALUE);
+      testPoint(reader, dataset, Long.MAX_VALUE);
     } finally {
       FileUtils.forceDelete(rangeIndexFile);
     }
@@ -237,6 +249,12 @@ public class BitSlicedIndexCreatorTest {
     }
   }
 
+  private static void testPoint(BitSlicedRangeIndexReader reader, 
Dataset<long[]> dataset, long value) {
+    ImmutableRoaringBitmap reference = dataset.scan(value, value);
+    assertEquals(reader.getMatchingDocIds(value), reference);
+    assertEquals(reader.getNumMatchingDocs(value), reference.getCardinality());
+  }
+
   private void testFloat(Dataset<float[]> dataset)
       throws IOException {
     ColumnMetadata metadata = dataset.toColumnMetadata();
@@ -261,9 +279,12 @@ public class BitSlicedIndexCreatorTest {
       }
       testRange(reader, dataset, prev, prev + 1);
       testRange(reader, dataset, prev + 1, prev + 1);
+      testPoint(reader, dataset, prev + 1);
       testRange(reader, dataset, prev + 1, Float.POSITIVE_INFINITY);
       testRange(reader, dataset, Float.POSITIVE_INFINITY, 
Float.POSITIVE_INFINITY);
       testRange(reader, dataset, Float.NEGATIVE_INFINITY, 
Float.POSITIVE_INFINITY);
+      testPoint(reader, dataset, Float.POSITIVE_INFINITY);
+      testPoint(reader, dataset, Float.NEGATIVE_INFINITY);
     } finally {
       FileUtils.forceDelete(rangeIndexFile);
     }
@@ -282,6 +303,12 @@ public class BitSlicedIndexCreatorTest {
     }
   }
 
+  private static void testPoint(BitSlicedRangeIndexReader reader, 
Dataset<float[]> dataset, float value) {
+    ImmutableRoaringBitmap reference = dataset.scan(value, value);
+    assertEquals(reader.getMatchingDocIds(value), reference);
+    assertEquals(reader.getNumMatchingDocs(value), reference.getCardinality());
+  }
+
   private void testDouble(Dataset<double[]> dataset)
       throws IOException {
     ColumnMetadata metadata = dataset.toColumnMetadata();
@@ -306,9 +333,12 @@ public class BitSlicedIndexCreatorTest {
       }
       testRange(reader, dataset, prev, prev + 1);
       testRange(reader, dataset, prev + 1, prev + 1);
+      testPoint(reader, dataset, prev + 1);
       testRange(reader, dataset, prev + 1, Double.POSITIVE_INFINITY);
       testRange(reader, dataset, Double.POSITIVE_INFINITY, 
Double.POSITIVE_INFINITY);
       testRange(reader, dataset, Double.NEGATIVE_INFINITY, 
Float.POSITIVE_INFINITY);
+      testPoint(reader, dataset, Double.POSITIVE_INFINITY);
+      testPoint(reader, dataset, Double.NEGATIVE_INFINITY);
     } finally {
       FileUtils.forceDelete(rangeIndexFile);
     }
@@ -327,6 +357,12 @@ public class BitSlicedIndexCreatorTest {
     }
   }
 
+  private static void testPoint(BitSlicedRangeIndexReader reader, 
Dataset<double[]> dataset, double value) {
+    ImmutableRoaringBitmap reference = dataset.scan(value, value);
+    assertEquals(reader.getMatchingDocIds(value), reference);
+    assertEquals(reader.getNumMatchingDocs(value), reference.getCardinality());
+  }
+
   private static BitSlicedRangeIndexCreator 
newBitSlicedIndexCreator(ColumnMetadata metadata) {
     return metadata.hasDictionary() ? new BitSlicedRangeIndexCreator(INDEX_DIR,
         metadata.getFieldSpec(), metadata.getCardinality()) : new 
BitSlicedRangeIndexCreator(INDEX_DIR,
diff --git 
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/reader/RangeIndexReader.java
 
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/reader/RangeIndexReader.java
index eabeef502a..f457056f17 100644
--- 
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/reader/RangeIndexReader.java
+++ 
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/reader/RangeIndexReader.java
@@ -21,6 +21,7 @@ package org.apache.pinot.segment.spi.index.reader;
 import java.io.Closeable;
 import javax.annotation.Nullable;
 
+
 /**
  * Interface for indexed range queries
  * @param <T>
@@ -72,6 +73,46 @@ public interface RangeIndexReader<T> extends Closeable {
    */
   int getNumMatchingDocs(double min, double max);
 
+  /**
+   * Returns the number of docs with an equal value.
+   * The count is exact unless {@see getPartiallyMatchingDocIds} returns a 
non-null value.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default int getNumMatchingDocs(int value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Returns the number of docs with an equal value.
+   * The count is exact unless {@see getPartiallyMatchingDocIds} returns a 
non-null value.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default int getNumMatchingDocs(long value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Returns the number of docs with an equal value.
+   * The count is exact unless {@see getPartiallyMatchingDocIds} returns a 
non-null value.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default int getNumMatchingDocs(float value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Returns the number of docs with an equal value.
+   * The count is exact unless {@see getPartiallyMatchingDocIds} returns a 
non-null value.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default int getNumMatchingDocs(double value) {
+    throw new UnsupportedOperationException();
+  }
+
   /**
    * Returns doc ids with a value between min and max, both inclusive.
    * Doc ids returned by this method must correspond to values which
@@ -116,6 +157,50 @@ public interface RangeIndexReader<T> extends Closeable {
   @Nullable
   T getMatchingDocIds(double min, double max);
 
+  /**
+   * Returns doc ids with an equal value.
+   * Doc ids returned by this method must correspond to values which
+   * satisfy the query.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default T getMatchingDocIds(int value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Returns doc ids with an equal value.
+   * Doc ids returned by this method must correspond to values which
+   * satisfy the query.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default T getMatchingDocIds(long value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Returns doc ids with an equal value.
+   * Doc ids returned by this method must correspond to values which
+   * satisfy the query.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default T getMatchingDocIds(float value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Returns doc ids with an equal value.
+   * Doc ids returned by this method must correspond to values which
+   * satisfy the query.
+   * @param value the value
+   * @return the matching doc ids.
+   */
+  default T getMatchingDocIds(double value) {
+    throw new UnsupportedOperationException();
+  }
+
   /**
    * Returns doc ids with a value between min and max, both inclusive.
    * Doc ids returned by this method may correspond to values which
@@ -126,7 +211,9 @@ public interface RangeIndexReader<T> extends Closeable {
    * @return the matching doc ids.
    */
   @Nullable
-  T getPartiallyMatchingDocIds(int min, int max);
+  default T getPartiallyMatchingDocIds(int min, int max) {
+    return null;
+  }
 
   /**
    * Returns doc ids with a value between min and max, both inclusive.
@@ -138,7 +225,9 @@ public interface RangeIndexReader<T> extends Closeable {
    * @return the matching doc ids.
    */
   @Nullable
-  T getPartiallyMatchingDocIds(long min, long max);
+  default T getPartiallyMatchingDocIds(long min, long max) {
+    return null;
+  }
 
   /**
    * Returns doc ids with a value between min and max, both inclusive.
@@ -150,7 +239,9 @@ public interface RangeIndexReader<T> extends Closeable {
    * @return the matching doc ids.
    */
   @Nullable
-  T getPartiallyMatchingDocIds(float min, float max);
+  default T getPartiallyMatchingDocIds(float min, float max) {
+    return null;
+  }
 
   /**
    * Returns doc ids with a value between min and max, both inclusive.
@@ -162,5 +253,7 @@ public interface RangeIndexReader<T> extends Closeable {
    * @return the matching doc ids.
    */
   @Nullable
-  T getPartiallyMatchingDocIds(double min, double max);
+  default T getPartiallyMatchingDocIds(double min, double max) {
+    return null;
+  }
 }


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

Reply via email to