propagate inverted status with BitmapDocIdIterator to avoid expensive flipping of bitmaps
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIterator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIterator.java
index 08bc89a..d32a29d 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIterator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIterator.java
@@ -32,7 +32,7 @@
private int _nextDocId = 0;
- public AndDocIdIterator(BlockDocIdIterator[] docIdIterators) {
+ public AndDocIdIterator(BlockDocIdIterator... docIdIterators) {
_docIdIterators = docIdIterators;
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/BitmapBasedDocIdIterator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/BitmapBasedDocIdIterator.java
index 222642f..f3d62ee 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/BitmapBasedDocIdIterator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/BitmapBasedDocIdIterator.java
@@ -31,4 +31,11 @@
* Returns a bitmap of the matching document ids.
*/
ImmutableRoaringBitmap getDocIds();
+
+ /**
+ * Returns true if the doc ids represent absence rather than presence and need to be handled specially
+ * */
+ default boolean isInverted() {
+ return false;
+ }
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/InvertedBitmapDocIdIterator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/InvertedBitmapDocIdIterator.java
new file mode 100644
index 0000000..d187790
--- /dev/null
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/InvertedBitmapDocIdIterator.java
@@ -0,0 +1,75 @@
+/**
+ * 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.dociditerators;
+
+import org.apache.pinot.segment.spi.Constants;
+import org.roaringbitmap.PeekableIntIterator;
+import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
+
+
+public class InvertedBitmapDocIdIterator implements BitmapBasedDocIdIterator {
+ private final ImmutableRoaringBitmap _docIds;
+ private final PeekableIntIterator _docIdIterator;
+ private final int _lastDoc;
+ private int _nextDocId;
+ private int _nextNonMatchingDocId;
+
+ public InvertedBitmapDocIdIterator(ImmutableRoaringBitmap docIds, int numDocs) {
+ _docIds = docIds;
+ _docIdIterator = docIds.getIntIterator();
+ _nextDocId = 0;
+
+ int currentDocIdFromChildIterator = _docIdIterator.next();
+ _nextNonMatchingDocId = currentDocIdFromChildIterator == Constants.EOF ? numDocs : currentDocIdFromChildIterator;
+ _lastDoc = numDocs - 1;
+ }
+
+ @Override
+ public int next() {
+ while (_nextDocId == _nextNonMatchingDocId && _docIdIterator.hasNext()) {
+ _nextDocId++;
+ int nextNonMatchingDocId = _docIdIterator.next();
+ _nextNonMatchingDocId = nextNonMatchingDocId == Constants.EOF ? _lastDoc : nextNonMatchingDocId;
+ }
+ if (_nextDocId >= _lastDoc) {
+ return Constants.EOF;
+ }
+ return _nextDocId++;
+ }
+
+ @Override
+ public int advance(int targetDocId) {
+ _nextDocId = targetDocId;
+ if (targetDocId > _nextNonMatchingDocId) {
+ _docIdIterator.advanceIfNeeded(targetDocId);
+ _nextNonMatchingDocId = _docIdIterator.hasNext() ? _docIdIterator.next() : _lastDoc;
+ }
+ return next();
+ }
+
+ @Override
+ public ImmutableRoaringBitmap getDocIds() {
+ return _docIds;
+ }
+
+ @Override
+ public boolean isInverted() {
+ return true;
+ }
+}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/ScanBasedDocIdIterator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/ScanBasedDocIdIterator.java
index 1ed890c..cf3945f 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/ScanBasedDocIdIterator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/dociditerators/ScanBasedDocIdIterator.java
@@ -40,6 +40,14 @@
MutableRoaringBitmap applyAnd(ImmutableRoaringBitmap docIds);
/**
+ * Applies ANDNOT operation to the given bitmap of document ids, returns a bitmap of the matching document ids.
+ */
+ default MutableRoaringBitmap applyAndNot(ImmutableRoaringBitmap docIds, int numDocs) {
+ // FIXME we're scanning anyway, so flipping a bitmap doesn't seem quite so bad, but this can be improved
+ return applyAnd(ImmutableRoaringBitmap.flip(docIds, 0L, numDocs));
+ }
+
+ /**
* Returns the number of entries (SV value contains one entry, MV value contains multiple entries) scanned during the
* iteration. This method should be called after the iteration is done.
*/
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/AndDocIdSet.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/AndDocIdSet.java
index 8b43b7d..ae40037 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/AndDocIdSet.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/AndDocIdSet.java
@@ -27,6 +27,7 @@
import org.apache.pinot.core.common.BlockDocIdIterator;
import org.apache.pinot.core.operator.dociditerators.AndDocIdIterator;
import org.apache.pinot.core.operator.dociditerators.BitmapBasedDocIdIterator;
+import org.apache.pinot.core.operator.dociditerators.InvertedBitmapDocIdIterator;
import org.apache.pinot.core.operator.dociditerators.RangelessBitmapDocIdIterator;
import org.apache.pinot.core.operator.dociditerators.ScanBasedDocIdIterator;
import org.apache.pinot.core.operator.dociditerators.SortedDocIdIterator;
@@ -56,11 +57,13 @@
public final class AndDocIdSet implements FilterBlockDocIdSet {
private final List<FilterBlockDocIdSet> _docIdSets;
private final boolean _cardinalityBasedRankingForScan;
+ private final int _numDocs;
- public AndDocIdSet(List<FilterBlockDocIdSet> docIdSets, Map<String, String> queryOptions) {
+ public AndDocIdSet(List<FilterBlockDocIdSet> docIdSets, Map<String, String> queryOptions, int numDocs) {
_docIdSets = docIdSets;
_cardinalityBasedRankingForScan =
!MapUtils.isEmpty(queryOptions) && QueryOptionsUtils.isAndScanReorderingEnabled(queryOptions);
+ _numDocs = numDocs;
}
@Override
@@ -88,8 +91,16 @@
}
// evaluate the bitmaps in the order of the lowest matching num docIds comes first, so that we minimize the number
- // of containers (range) for comparison from the beginning, as will minimize the effort of bitmap AND application
- bitmapBasedDocIdIterators.sort(Comparator.comparing(x -> x.getDocIds().getCardinality()));
+ // of containers (range) for comparison from the beginning, as will minimize the effort of bitmap AND application.
+ // We also put inverted iterators last to avoid flipping them wherever possible, but if we have to flip one, we
+ // want it to be the largest one
+ bitmapBasedDocIdIterators.sort(Comparator.comparing(x -> {
+ int cardinality = x.getDocIds().getCardinality();
+ if (x.isInverted()) {
+ return Integer.MAX_VALUE - cardinality;
+ }
+ return Integer.MIN_VALUE + cardinality;
+ }));
// Evaluate the scan based operator with the highest cardinality coming first, this potentially reduce the range of
// scanning from the beginning. Automatically place N/A cardinality column (negative infinity) to the back as we
@@ -113,6 +124,7 @@
// BlockDocIdIterator, directly return the merged RangelessBitmapDocIdIterator; otherwise, construct and return
// an AndDocIdIterator with the merged RangelessBitmapDocIdIterator and the remaining BlockDocIdIterators.
+ boolean inverted = false;
ImmutableRoaringBitmap docIds;
if (numSortedDocIdIterators > 0) {
List<IntPair> docIdRanges;
@@ -132,29 +144,52 @@
mutableDocIds.add(docIdRange.getLeft(), docIdRange.getRight() + 1L);
}
for (BitmapBasedDocIdIterator bitmapBasedDocIdIterator : bitmapBasedDocIdIterators) {
- mutableDocIds.and(bitmapBasedDocIdIterator.getDocIds());
+ if (bitmapBasedDocIdIterator.isInverted()) {
+ mutableDocIds.andNot(bitmapBasedDocIdIterator.getDocIds());
+ } else {
+ mutableDocIds.and(bitmapBasedDocIdIterator.getDocIds());
+ }
}
docIds = mutableDocIds;
} else {
if (numBitmapBasedDocIdIterators == 1) {
- docIds = bitmapBasedDocIdIterators.get(0).getDocIds();
+ BitmapBasedDocIdIterator bitmapBasedDocIdIterator = bitmapBasedDocIdIterators.get(0);
+ docIds = bitmapBasedDocIdIterator.getDocIds();
+ inverted = bitmapBasedDocIdIterator.isInverted();
} else {
- MutableRoaringBitmap mutableDocIds = bitmapBasedDocIdIterators.get(0).getDocIds().toMutableRoaringBitmap();
+ BitmapBasedDocIdIterator firstBitmapBasedDocIdIterator = bitmapBasedDocIdIterators.get(0);
+ MutableRoaringBitmap mutableDocIds = firstBitmapBasedDocIdIterator.getDocIds().toMutableRoaringBitmap();
+ if (firstBitmapBasedDocIdIterator.isInverted()) {
+ // because of the sort order, this means all the filters are inverted, so this may be avoidable,
+ // but it's also the largest bitmap so we should only create a small bitmap
+ mutableDocIds.flip(0L, _numDocs);
+ }
for (int i = 1; i < numBitmapBasedDocIdIterators; i++) {
- mutableDocIds.and(bitmapBasedDocIdIterators.get(i).getDocIds());
+ BitmapBasedDocIdIterator bitmapBasedDocIdIterator = bitmapBasedDocIdIterators.get(i);
+ if (bitmapBasedDocIdIterator.isInverted()) {
+ mutableDocIds.andNot(bitmapBasedDocIdIterators.get(i).getDocIds());
+ } else {
+ mutableDocIds.and(bitmapBasedDocIdIterators.get(i).getDocIds());
+ }
}
docIds = mutableDocIds;
}
}
for (ScanBasedDocIdIterator scanBasedDocIdIterator : scanBasedDocIdIterators) {
- docIds = scanBasedDocIdIterator.applyAnd(docIds);
+ if (inverted) {
+ docIds = scanBasedDocIdIterator.applyAndNot(docIds, _numDocs);
+ inverted = false;
+ } else {
+ docIds = scanBasedDocIdIterator.applyAnd(docIds);
+ }
}
- RangelessBitmapDocIdIterator rangelessBitmapDocIdIterator = new RangelessBitmapDocIdIterator(docIds);
+ BitmapBasedDocIdIterator lastBitmapDocIdIterator =
+ inverted ? new InvertedBitmapDocIdIterator(docIds, _numDocs) : new RangelessBitmapDocIdIterator(docIds);
if (numRemainingDocIdIterators == 0) {
- return rangelessBitmapDocIdIterator;
+ return lastBitmapDocIdIterator;
} else {
BlockDocIdIterator[] docIdIterators = new BlockDocIdIterator[numRemainingDocIdIterators + 1];
- docIdIterators[0] = rangelessBitmapDocIdIterator;
+ docIdIterators[0] = lastBitmapDocIdIterator;
for (int i = 0; i < numRemainingDocIdIterators; i++) {
docIdIterators[i + 1] = remainingDocIdIterators.get(i);
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/BitmapDocIdSet.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/BitmapDocIdSet.java
index a69ac00..fee9d0e 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/BitmapDocIdSet.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/BitmapDocIdSet.java
@@ -18,15 +18,21 @@
*/
package org.apache.pinot.core.operator.docidsets;
+import org.apache.pinot.core.operator.dociditerators.BitmapBasedDocIdIterator;
import org.apache.pinot.core.operator.dociditerators.BitmapDocIdIterator;
+import org.apache.pinot.core.operator.dociditerators.InvertedBitmapDocIdIterator;
import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
public class BitmapDocIdSet implements FilterBlockDocIdSet {
- private final BitmapDocIdIterator _iterator;
+ private final BitmapBasedDocIdIterator _iterator;
public BitmapDocIdSet(ImmutableRoaringBitmap docIds, int numDocs) {
- _iterator = new BitmapDocIdIterator(docIds, numDocs);
+ this(docIds, numDocs, false);
+ }
+
+ public BitmapDocIdSet(ImmutableRoaringBitmap docIds, int numDocs, boolean inverted) {
+ _iterator = inverted ? new InvertedBitmapDocIdIterator(docIds, numDocs) : new BitmapDocIdIterator(docIds, numDocs);
}
public BitmapDocIdSet(BitmapDocIdIterator iterator) {
@@ -34,7 +40,7 @@
}
@Override
- public BitmapDocIdIterator iterator() {
+ public BitmapBasedDocIdIterator iterator() {
return _iterator;
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/NotDocIdSet.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/NotDocIdSet.java
index 1fe6462..0c5b2a1 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/NotDocIdSet.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/NotDocIdSet.java
@@ -33,6 +33,7 @@
@Override
public BlockDocIdIterator iterator() {
+ // FIXME if the child set is an inverted bitmap, we could just invert it again
return new NotDocIdIterator(_childDocIdSet.iterator(), _numDocs);
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/OrDocIdSet.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/OrDocIdSet.java
index 6ead802..fa60f9a 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/OrDocIdSet.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/docidsets/OrDocIdSet.java
@@ -88,7 +88,11 @@
}
}
for (BitmapBasedDocIdIterator bitmapBasedDocIdIterator : bitmapBasedDocIdIterators) {
- docIds.or(bitmapBasedDocIdIterator.getDocIds());
+ if (bitmapBasedDocIdIterator.isInverted()) {
+ docIds.orNot(bitmapBasedDocIdIterator.getDocIds(), _numDocs);
+ } else {
+ docIds.or(bitmapBasedDocIdIterator.getDocIds());
+ }
}
BitmapDocIdIterator bitmapDocIdIterator = new BitmapDocIdIterator(docIds, _numDocs);
int numRemainingDocIdIterators = remainingDocIdIterators.size();
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/AndFilterOperator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/AndFilterOperator.java
index de2062f..3eb5265 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/AndFilterOperator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/AndFilterOperator.java
@@ -36,13 +36,16 @@
private final List<BaseFilterOperator> _filterOperators;
private final Map<String, String> _queryOptions;
- public AndFilterOperator(List<BaseFilterOperator> filterOperators, Map<String, String> queryOptions) {
+ private final int _numDocs;
+
+ public AndFilterOperator(List<BaseFilterOperator> filterOperators, Map<String, String> queryOptions, int numDocs) {
_filterOperators = filterOperators;
_queryOptions = queryOptions;
+ _numDocs = numDocs;
}
- public AndFilterOperator(List<BaseFilterOperator> filterOperators) {
- this(filterOperators, null);
+ public AndFilterOperator(List<BaseFilterOperator> filterOperators, int numDocs) {
+ this(filterOperators, null, numDocs);
}
@Override
@@ -52,7 +55,7 @@
for (BaseFilterOperator filterOperator : _filterOperators) {
filterBlockDocIdSets.add(filterOperator.nextBlock().getBlockDocIdSet());
}
- return new FilterBlock(new AndDocIdSet(filterBlockDocIdSets, _queryOptions));
+ return new FilterBlock(new AndDocIdSet(filterBlockDocIdSets, _queryOptions, _numDocs));
}
@Override
@@ -77,7 +80,6 @@
return BufferFastAggregation.andCardinality(bitmaps);
}
-
@Override
public List<Operator> getChildOperators() {
return new ArrayList<>(_filterOperators);
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/BitmapBasedFilterOperator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/BitmapBasedFilterOperator.java
index bd8fb41..5abcafe 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/BitmapBasedFilterOperator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/BitmapBasedFilterOperator.java
@@ -65,11 +65,7 @@
@Override
protected FilterBlock getNextBlock() {
if (_docIds != null) {
- if (_exclusive) {
- return new FilterBlock(new BitmapDocIdSet(ImmutableRoaringBitmap.flip(_docIds, 0L, _numDocs), _numDocs));
- } else {
- return new FilterBlock(new BitmapDocIdSet(_docIds, _numDocs));
- }
+ return new FilterBlock(new BitmapDocIdSet(_docIds, _numDocs, _exclusive));
}
int[] dictIds = _exclusive ? _predicateEvaluator.getNonMatchingDictIds() : _predicateEvaluator.getMatchingDictIds();
@@ -79,33 +75,20 @@
}
if (numDictIds == 1) {
ImmutableRoaringBitmap docIds = _invertedIndexReader.getDocIds(dictIds[0]);
- if (_exclusive) {
- if (docIds instanceof MutableRoaringBitmap) {
- MutableRoaringBitmap mutableRoaringBitmap = (MutableRoaringBitmap) docIds;
- mutableRoaringBitmap.flip(0L, _numDocs);
- return new FilterBlock(new BitmapDocIdSet(mutableRoaringBitmap, _numDocs));
- } else {
- return new FilterBlock(new BitmapDocIdSet(ImmutableRoaringBitmap.flip(docIds, 0L, _numDocs), _numDocs));
- }
- } else {
- return new FilterBlock(new BitmapDocIdSet(docIds, _numDocs));
- }
+ return new FilterBlock(new BitmapDocIdSet(docIds, _numDocs, _exclusive));
} else {
ImmutableRoaringBitmap[] bitmaps = new ImmutableRoaringBitmap[numDictIds];
for (int i = 0; i < numDictIds; i++) {
bitmaps[i] = _invertedIndexReader.getDocIds(dictIds[i]);
}
MutableRoaringBitmap docIds = ImmutableRoaringBitmap.or(bitmaps);
- if (_exclusive) {
- docIds.flip(0L, _numDocs);
- }
InvocationRecording recording = Tracing.activeRecording();
if (recording.isEnabled()) {
recording.setColumnName(_predicateEvaluator.getPredicate().getLhs().getIdentifier());
recording.setNumDocsMatchingAfterFilter(docIds.getCardinality());
recording.setFilter(FilterType.INDEX, String.valueOf(_predicateEvaluator.getPredicateType()));
}
- return new FilterBlock(new BitmapDocIdSet(docIds, _numDocs));
+ return new FilterBlock(new BitmapDocIdSet(docIds, _numDocs, _exclusive));
}
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/CombinedFilterOperator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/CombinedFilterOperator.java
index 89b856d..0dab2b7 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/CombinedFilterOperator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/CombinedFilterOperator.java
@@ -38,12 +38,14 @@
private final BaseFilterOperator _mainFilterOperator;
private final BaseFilterOperator _subFilterOperator;
private final Map<String, String> _queryOptions;
+ private final int _numDocs;
public CombinedFilterOperator(BaseFilterOperator mainFilterOperator, BaseFilterOperator subFilterOperator,
- Map<String, String> queryOptions) {
+ Map<String, String> queryOptions, int numDocs) {
_mainFilterOperator = mainFilterOperator;
_subFilterOperator = subFilterOperator;
_queryOptions = queryOptions;
+ _numDocs = numDocs;
}
@Override
@@ -61,6 +63,7 @@
Tracing.activeRecording().setNumChildren(2);
FilterBlockDocIdSet mainFilterDocIdSet = _mainFilterOperator.nextBlock().getNonScanFilterBLockDocIdSet();
FilterBlockDocIdSet subFilterDocIdSet = _subFilterOperator.nextBlock().getBlockDocIdSet();
- return new FilterBlock(new AndDocIdSet(Arrays.asList(mainFilterDocIdSet, subFilterDocIdSet), _queryOptions));
+ return new FilterBlock(
+ new AndDocIdSet(Arrays.asList(mainFilterDocIdSet, subFilterDocIdSet), _queryOptions, _numDocs));
}
}
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 c343dce..9328bf7 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
@@ -107,7 +107,7 @@
} else {
// Return the AND filter operator with re-ordered child filter operators
FilterOperatorUtils.reorderAndFilterChildOperators(queryContext, childFilterOperators);
- return new AndFilterOperator(childFilterOperators, queryContext.getQueryOptions());
+ return new AndFilterOperator(childFilterOperators, queryContext.getQueryOptions(), numDocs);
}
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/ScanBasedFilterOperator.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/ScanBasedFilterOperator.java
index 89305d2..60f1916 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/ScanBasedFilterOperator.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/ScanBasedFilterOperator.java
@@ -59,7 +59,6 @@
}
}
-
@Override
public List<Operator> getChildOperators() {
return Collections.emptyList();
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java b/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java
index 1489118..181a4e2 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java
@@ -90,7 +90,7 @@
List<Pair<AggregationFunction[], TransformOperator>> aggToTransformOpList =
AggregationFunctionUtils.buildFilteredAggTransformPairs(_indexSegment, _queryContext,
- filterOperatorPair.getRight(), transformOperator, null);
+ filterOperatorPair.getRight(), transformOperator, null, numTotalDocs);
return new FilteredAggregationOperator(_queryContext.getAggregationFunctions(), aggToTransformOpList, numTotalDocs);
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/plan/GroupByPlanNode.java b/pinot-core/src/main/java/org/apache/pinot/core/plan/GroupByPlanNode.java
index 99fdec9..33f9212 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/plan/GroupByPlanNode.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/plan/GroupByPlanNode.java
@@ -76,7 +76,7 @@
List<Pair<AggregationFunction[], TransformOperator>> aggToTransformOpList =
AggregationFunctionUtils.buildFilteredAggTransformPairs(_indexSegment, _queryContext,
- filterOperatorPair.getRight(), transformOperator, groupByExpressions);
+ filterOperatorPair.getRight(), transformOperator, groupByExpressions, numTotalDocs);
return new FilteredGroupByOperator(_queryContext.getAggregationFunctions(), aggToTransformOpList,
_queryContext.getGroupByExpressions().toArray(new ExpressionContext[0]), numTotalDocs, _queryContext);
}
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionUtils.java b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionUtils.java
index 0dcecb0..9083078 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionUtils.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionUtils.java
@@ -211,7 +211,7 @@
*/
public static List<Pair<AggregationFunction[], TransformOperator>> buildFilteredAggTransformPairs(
IndexSegment indexSegment, QueryContext queryContext, BaseFilterOperator mainPredicateFilterOperator,
- TransformOperator mainTransformOperator, @Nullable ExpressionContext[] groupByExpressions) {
+ TransformOperator mainTransformOperator, @Nullable ExpressionContext[] groupByExpressions, int numDocs) {
Map<FilterContext, Pair<List<AggregationFunction>, TransformOperator>> filterContextToAggFuncsMap = new HashMap<>();
List<AggregationFunction> nonFilteredAggregationFunctions = new ArrayList<>();
List<Pair<AggregationFunction, FilterContext>> aggregationFunctions =
@@ -232,7 +232,7 @@
buildFilterOperator(indexSegment, queryContext, currentFilterExpression);
BaseFilterOperator wrappedFilterOperator =
new CombinedFilterOperator(mainPredicateFilterOperator, filterPlanOpPair.getRight(),
- queryContext.getQueryOptions());
+ queryContext.getQueryOptions(), numDocs);
TransformOperator newTransformOperator =
buildTransformOperatorForFilteredAggregates(indexSegment, queryContext, wrappedFilterOperator,
groupByExpressions);
diff --git a/pinot-core/src/test/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIteratorTest.java b/pinot-core/src/test/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIteratorTest.java
index c556114..eb80820 100644
--- a/pinot-core/src/test/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIteratorTest.java
+++ b/pinot-core/src/test/java/org/apache/pinot/core/operator/dociditerators/AndDocIdIteratorTest.java
@@ -18,8 +18,8 @@
*/
package org.apache.pinot.core.operator.dociditerators;
-import org.apache.pinot.core.common.BlockDocIdIterator;
import org.apache.pinot.segment.spi.Constants;
+import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
import org.roaringbitmap.buffer.MutableRoaringBitmap;
import org.testng.annotations.Test;
@@ -41,10 +41,9 @@
bitmap2.add(docIds2);
MutableRoaringBitmap bitmap3 = new MutableRoaringBitmap();
bitmap3.add(docIds3);
- AndDocIdIterator andDocIdIterator = new AndDocIdIterator(new BlockDocIdIterator[]{
- new RangelessBitmapDocIdIterator(bitmap1), new RangelessBitmapDocIdIterator(bitmap2),
- new RangelessBitmapDocIdIterator(bitmap3)
- });
+ AndDocIdIterator andDocIdIterator =
+ new AndDocIdIterator(new RangelessBitmapDocIdIterator(bitmap1), new RangelessBitmapDocIdIterator(bitmap2),
+ new RangelessBitmapDocIdIterator(bitmap3));
assertEquals(andDocIdIterator.next(), 2);
assertEquals(andDocIdIterator.next(), 7);
@@ -53,4 +52,49 @@
assertEquals(andDocIdIterator.next(), 20);
assertEquals(andDocIdIterator.next(), Constants.EOF);
}
+
+ @Test
+ public void testAndDocIdIteratorWithInvertedChildren() {
+ int numDocs = 20;
+ // not = [4, 6, 8, 11, 14, 17, 19]
+ int[] docIds1 = new int[]{0, 1, 2, 3, 5, 7, 10, 12, 13, 15, 16, 18, 20};
+ // not = [0, 3, 8, 10, 14, 17, 18]
+ int[] docIds2 = new int[]{1, 2, 4, 5, 6, 7, 9, 11, 12, 13, 15, 16, 19, 20};
+ // not = [1, 5, 6, 9, 12, 14, 17, 18]
+ int[] docIds3 = new int[]{0, 2, 3, 4, 7, 8, 10, 11, 13, 15, 16, 19, 20};
+ int[] expected = new int[]{14, 17};
+
+ ImmutableRoaringBitmap bitmap1 = ImmutableRoaringBitmap.bitmapOf(docIds1);
+ ImmutableRoaringBitmap bitmap2 = ImmutableRoaringBitmap.bitmapOf(docIds2);
+ ImmutableRoaringBitmap bitmap3 = ImmutableRoaringBitmap.bitmapOf(docIds3);
+ AndDocIdIterator andDocIdIterator = new AndDocIdIterator(new InvertedBitmapDocIdIterator(bitmap1, numDocs),
+ new InvertedBitmapDocIdIterator(bitmap2, numDocs), new InvertedBitmapDocIdIterator(bitmap3, numDocs));
+
+ for (int docId : expected) {
+ assertEquals(andDocIdIterator.next(), docId);
+ }
+ assertEquals(andDocIdIterator.next(), Constants.EOF);
+ }
+
+ @Test
+ public void testAndDocIdIteratorWithMixedChildren() {
+ int numDocs = 20;
+ int[] docIds1 = new int[]{0, 1, 2, 3, 5, 7, 10, 12, 13, 14, 15, 16, 17, 18, 20};
+ // not = [0, 3, 8, 10, 14, 17, 18]
+ int[] docIds2 = new int[]{1, 2, 4, 5, 6, 7, 9, 11, 12, 13, 15, 16, 19, 20};
+ // not = [1, 5, 6, 9, 12, 14, 17, 18]
+ int[] docIds3 = new int[]{0, 2, 3, 4, 7, 8, 10, 11, 13, 15, 16, 19, 20};
+ int[] expected = new int[]{14, 17, 18};
+
+ ImmutableRoaringBitmap bitmap1 = ImmutableRoaringBitmap.bitmapOf(docIds1);
+ ImmutableRoaringBitmap bitmap2 = ImmutableRoaringBitmap.bitmapOf(docIds2);
+ ImmutableRoaringBitmap bitmap3 = ImmutableRoaringBitmap.bitmapOf(docIds3);
+ AndDocIdIterator andDocIdIterator = new AndDocIdIterator(new BitmapDocIdIterator(bitmap1, numDocs),
+ new InvertedBitmapDocIdIterator(bitmap2, numDocs), new InvertedBitmapDocIdIterator(bitmap3, numDocs));
+
+ for (int docId : expected) {
+ assertEquals(andDocIdIterator.next(), docId);
+ }
+ assertEquals(andDocIdIterator.next(), Constants.EOF);
+ }
}
diff --git a/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/AndFilterOperatorTest.java b/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/AndFilterOperatorTest.java
index 6481ae2..c639ae3 100644
--- a/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/AndFilterOperatorTest.java
+++ b/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/AndFilterOperatorTest.java
@@ -22,6 +22,7 @@
import java.util.List;
import org.apache.pinot.core.common.BlockDocIdIterator;
import org.apache.pinot.segment.spi.Constants;
+import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
import org.roaringbitmap.buffer.MutableRoaringBitmap;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -37,7 +38,7 @@
List<BaseFilterOperator> operators = new ArrayList<>();
operators.add(new TestFilterOperator(docIds1));
operators.add(new TestFilterOperator(docIds2));
- AndFilterOperator andOperator = new AndFilterOperator(operators);
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
Assert.assertEquals(iterator.next(), 3);
@@ -55,7 +56,7 @@
operators.add(new TestFilterOperator(docIds1));
operators.add(new TestFilterOperator(docIds2));
operators.add(new TestFilterOperator(docIds3));
- AndFilterOperator andOperator = new AndFilterOperator(operators);
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
Assert.assertEquals(iterator.next(), 3);
@@ -72,12 +73,12 @@
List<BaseFilterOperator> childOperators = new ArrayList<>();
childOperators.add(new TestFilterOperator(docIds1));
childOperators.add(new TestFilterOperator(docIds2));
- AndFilterOperator childAndOperator = new AndFilterOperator(childOperators);
+ AndFilterOperator childAndOperator = new AndFilterOperator(childOperators, 40);
List<BaseFilterOperator> operators = new ArrayList<>();
operators.add(childAndOperator);
operators.add(new TestFilterOperator(docIds3));
- AndFilterOperator andOperator = new AndFilterOperator(operators);
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
Assert.assertEquals(iterator.next(), 3);
@@ -112,8 +113,8 @@
numDocs));
}
- AndFilterOperator andFilterOperator1 = new AndFilterOperator(childOperators1);
- AndFilterOperator andFilterOperator2 = new AndFilterOperator(childOperators2);
+ AndFilterOperator andFilterOperator1 = new AndFilterOperator(childOperators1, numDocs);
+ AndFilterOperator andFilterOperator2 = new AndFilterOperator(childOperators2, numDocs);
BlockDocIdIterator iterator1 = andFilterOperator1.getNextBlock().getBlockDocIdSet().iterator();
BlockDocIdIterator iterator2 = andFilterOperator2.getNextBlock().getBlockDocIdSet().iterator();
Assert.assertEquals(iterator1.next(), 0);
@@ -145,7 +146,7 @@
List<BaseFilterOperator> operators = new ArrayList<>();
operators.add(childOrOperator);
operators.add(new TestFilterOperator(docIds1));
- AndFilterOperator andOperator = new AndFilterOperator(operators);
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
Assert.assertEquals(iterator.next(), 2);
@@ -154,4 +155,72 @@
Assert.assertEquals(iterator.next(), 28);
Assert.assertEquals(iterator.next(), Constants.EOF);
}
+
+ @Test
+ public void testComplexOrWithNot() {
+ int[] include1 = new int[]{2, 3, 6, 10, 15, 16, 28};
+ int[] include2 = new int[]{3, 6, 8, 20, 28};
+ int[] exclude1 = new int[]{1, 2, 3, 6, 30}; // negation = {4, 5, 7, ... 29}
+ int[] expected = new int[]{3, 6, 10, 15, 16, 28};
+
+ List<BaseFilterOperator> childOperators = new ArrayList<>();
+ childOperators.add(new BitmapBasedFilterOperator(ImmutableRoaringBitmap.bitmapOf(exclude1), true, 40));
+ childOperators.add(new BitmapBasedFilterOperator(ImmutableRoaringBitmap.bitmapOf(include2), false, 40));
+ OrFilterOperator childOrOperator = new OrFilterOperator(childOperators, 40);
+
+ List<BaseFilterOperator> operators = new ArrayList<>();
+ operators.add(childOrOperator);
+ operators.add(new TestFilterOperator(include1));
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
+
+ BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
+ for (int j : expected) {
+ Assert.assertEquals(iterator.next(), j);
+ }
+ Assert.assertEquals(iterator.next(), Constants.EOF);
+ }
+
+ @Test
+ public void testComplexAndWithNot() {
+ int[] include1 = new int[]{2, 3, 6, 10, 15, 16, 28};
+ int[] include2 = new int[]{3, 6, 8, 20, 28};
+ int[] exclude1 = new int[]{1, 2, 3, 6, 30}; // negation = {4, 5, 7, ... 29}
+ int[] expected = new int[]{28};
+
+ List<BaseFilterOperator> operators = new ArrayList<>();
+ operators.add(new BitmapBasedFilterOperator(ImmutableRoaringBitmap.bitmapOf(exclude1), true, 40));
+ operators.add(new BitmapBasedFilterOperator(ImmutableRoaringBitmap.bitmapOf(include2), false, 40));
+ operators.add(new TestFilterOperator(include1));
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
+
+ BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
+ for (int j : expected) {
+ Assert.assertEquals(iterator.next(), j);
+ }
+ Assert.assertEquals(iterator.next(), Constants.EOF);
+ }
+
+ @Test
+ public void testAndWithNot() {
+ int[] include1 = new int[]{2, 3, 6, 10, 15, 16, 28};
+ int[] include2 = new int[]{3, 6, 8, 20, 28};
+ int[] exclude1 = new int[]{1, 2, 3, 6, 30}; // negation = {4, 5, 7, ... 29}
+ int[] expected = new int[]{28};
+
+ List<BaseFilterOperator> childOperators = new ArrayList<>();
+ childOperators.add(new BitmapBasedFilterOperator(ImmutableRoaringBitmap.bitmapOf(exclude1), true, 40));
+ childOperators.add(new BitmapBasedFilterOperator(ImmutableRoaringBitmap.bitmapOf(include2), false, 40));
+ AndFilterOperator childAndOperator = new AndFilterOperator(childOperators, 40);
+
+ List<BaseFilterOperator> operators = new ArrayList<>();
+ operators.add(childAndOperator);
+ operators.add(new TestFilterOperator(include1));
+ AndFilterOperator andOperator = new AndFilterOperator(operators, 40);
+
+ BlockDocIdIterator iterator = andOperator.nextBlock().getBlockDocIdSet().iterator();
+ for (int j : expected) {
+ Assert.assertEquals(iterator.next(), j);
+ }
+ Assert.assertEquals(iterator.next(), Constants.EOF);
+ }
}
diff --git a/pinot-perf/src/main/java/org/apache/pinot/perf/BenchmarkAndDocIdIterator.java b/pinot-perf/src/main/java/org/apache/pinot/perf/BenchmarkAndDocIdIterator.java
index 6982dc5..7979f42 100644
--- a/pinot-perf/src/main/java/org/apache/pinot/perf/BenchmarkAndDocIdIterator.java
+++ b/pinot-perf/src/main/java/org/apache/pinot/perf/BenchmarkAndDocIdIterator.java
@@ -61,7 +61,8 @@
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchAndFilterOperator(MyState myState, Blackhole bh) {
for (int i = 0; i < 100; i++) {
- bh.consume(new AndFilterOperator(myState._childOperators).nextBlock().getBlockDocIdSet().iterator());
+ bh.consume(new AndFilterOperator(myState._childOperators, NUM_DOCS).nextBlock()
+ .getBlockDocIdSet().iterator());
}
}
@@ -70,7 +71,8 @@
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchAndFilterOperatorDegenerate(MyState myState, Blackhole bh) {
for (int i = 0; i < 100; i++) {
- bh.consume(new AndFilterOperator(myState._childOperatorsNoOrdering).nextBlock().getBlockDocIdSet().iterator());
+ bh.consume(new AndFilterOperator(myState._childOperatorsNoOrdering, NUM_DOCS).nextBlock()
+ .getBlockDocIdSet().iterator());
}
}