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