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

Jackie-Jiang 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 bde7223cf29 Implement canProduceBitmaps and getBitmaps for AND and OR 
filter operators (#19038)
bde7223cf29 is described below

commit bde7223cf2973b93bb9bce3a8f03d356411baa78
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Tue Jul 21 23:58:08 2026 -0700

    Implement canProduceBitmaps and getBitmaps for AND and OR filter operators 
(#19038)
---
 .../core/operator/filter/AndFilterOperator.java    |  25 +++-
 .../core/operator/filter/OrFilterOperator.java     |  25 +++-
 .../operator/filter/AndFilterOperatorTest.java     | 155 +++++++++++++++------
 .../core/operator/filter/OrFilterOperatorTest.java | 136 +++++++++++++-----
 4 files changed, 255 insertions(+), 86 deletions(-)

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 e47cff90589..53ed6e4b53f 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
@@ -103,11 +103,7 @@ public class AndFilterOperator extends BaseFilterOperator {
 
   @Override
   public boolean canOptimizeCount() {
-    boolean allChildrenCanProduceBitmaps = true;
-    for (BaseFilterOperator child : _filterOperators) {
-      allChildrenCanProduceBitmaps &= child.canProduceBitmaps();
-    }
-    return allChildrenCanProduceBitmaps;
+    return canProduceBitmaps();
   }
 
   @Override
@@ -123,6 +119,25 @@ public class AndFilterOperator extends BaseFilterOperator {
     return BufferFastAggregation.andCardinality(bitmaps);
   }
 
+  @Override
+  public boolean canProduceBitmaps() {
+    for (BaseFilterOperator child : _filterOperators) {
+      if (!child.canProduceBitmaps()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public BitmapCollection getBitmaps() {
+    ImmutableRoaringBitmap[] bitmaps = new 
ImmutableRoaringBitmap[_filterOperators.size()];
+    for (int i = 0; i < _filterOperators.size(); i++) {
+      bitmaps[i] = _filterOperators.get(i).getBitmaps().reduce();
+    }
+    return new BitmapCollection(_numDocs, false, 
BufferFastAggregation.and(bitmaps));
+  }
+
   @Override
   public List<Operator> getChildOperators() {
     return new ArrayList<>(_filterOperators);
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/OrFilterOperator.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/OrFilterOperator.java
index 1aa635d3fc6..e2c92bfd0c0 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/OrFilterOperator.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/filter/OrFilterOperator.java
@@ -112,11 +112,7 @@ public class OrFilterOperator extends BaseFilterOperator {
 
   @Override
   public boolean canOptimizeCount() {
-    boolean allChildrenProduceBitmaps = true;
-    for (BaseFilterOperator child : _filterOperators) {
-      allChildrenProduceBitmaps &= child.canProduceBitmaps();
-    }
-    return allChildrenProduceBitmaps;
+    return canProduceBitmaps();
   }
 
   @Override
@@ -130,4 +126,23 @@ public class OrFilterOperator extends BaseFilterOperator {
     }
     return BufferFastAggregation.orCardinality(bitmaps);
   }
+
+  @Override
+  public boolean canProduceBitmaps() {
+    for (BaseFilterOperator child : _filterOperators) {
+      if (!child.canProduceBitmaps()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public BitmapCollection getBitmaps() {
+    ImmutableRoaringBitmap[] bitmaps = new 
ImmutableRoaringBitmap[_filterOperators.size()];
+    for (int i = 0; i < _filterOperators.size(); i++) {
+      bitmaps[i] = _filterOperators.get(i).getBitmaps().reduce();
+    }
+    return new BitmapCollection(_numDocs, false, 
BufferFastAggregation.or(bitmaps));
+  }
 }
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 ad4b10cba50..a939447a41f 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
@@ -19,16 +19,18 @@
 package org.apache.pinot.core.operator.filter;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import org.apache.pinot.core.common.BlockDocIdIterator;
 import org.apache.pinot.core.operator.docidsets.EmptyDocIdSet;
 import org.apache.pinot.core.operator.docidsets.MatchAllDocIdSet;
 import org.apache.pinot.segment.spi.Constants;
 import org.roaringbitmap.buffer.MutableRoaringBitmap;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
 
 public class AndFilterOperatorTest {
 
@@ -44,9 +46,9 @@ public class AndFilterOperatorTest {
     AndFilterOperator andOperator = new AndFilterOperator(operators, null, 
numDocs, false);
 
     BlockDocIdIterator iterator = 
andOperator.nextBlock().getBlockDocIdSet().iterator();
-    Assert.assertEquals(iterator.next(), 3);
-    Assert.assertEquals(iterator.next(), 28);
-    Assert.assertEquals(iterator.next(), Constants.EOF);
+    assertEquals(iterator.next(), 3);
+    assertEquals(iterator.next(), 28);
+    assertEquals(iterator.next(), Constants.EOF);
   }
 
   @Test
@@ -63,9 +65,9 @@ public class AndFilterOperatorTest {
     AndFilterOperator andOperator = new AndFilterOperator(operators, null, 
numDocs, false);
 
     BlockDocIdIterator iterator = 
andOperator.nextBlock().getBlockDocIdSet().iterator();
-    Assert.assertEquals(iterator.next(), 3);
-    Assert.assertEquals(iterator.next(), 6);
-    Assert.assertEquals(iterator.next(), Constants.EOF);
+    assertEquals(iterator.next(), 3);
+    assertEquals(iterator.next(), 6);
+    assertEquals(iterator.next(), Constants.EOF);
   }
 
   @Test
@@ -86,9 +88,9 @@ public class AndFilterOperatorTest {
     AndFilterOperator andOperator = new AndFilterOperator(operators, null, 
numDocs, false);
 
     BlockDocIdIterator iterator = 
andOperator.nextBlock().getBlockDocIdSet().iterator();
-    Assert.assertEquals(iterator.next(), 3);
-    Assert.assertEquals(iterator.next(), 6);
-    Assert.assertEquals(iterator.next(), Constants.EOF);
+    assertEquals(iterator.next(), 3);
+    assertEquals(iterator.next(), 6);
+    assertEquals(iterator.next(), Constants.EOF);
   }
 
   @Test
@@ -122,18 +124,18 @@ public class AndFilterOperatorTest {
     AndFilterOperator andFilterOperator2 = new 
AndFilterOperator(childOperators2, null, numDocs, false);
     BlockDocIdIterator iterator1 = 
andFilterOperator1.getNextBlock().getBlockDocIdSet().iterator();
     BlockDocIdIterator iterator2 = 
andFilterOperator2.getNextBlock().getBlockDocIdSet().iterator();
-    Assert.assertEquals(iterator1.next(), 0);
-    Assert.assertEquals(iterator1.next(), 60);
-    Assert.assertEquals(iterator1.next(), 120);
-    Assert.assertEquals(iterator1.next(), 180);
+    assertEquals(iterator1.next(), 0);
+    assertEquals(iterator1.next(), 60);
+    assertEquals(iterator1.next(), 120);
+    assertEquals(iterator1.next(), 180);
 
-    Assert.assertEquals(iterator2.next(), 0);
-    Assert.assertEquals(iterator2.next(), 60);
-    Assert.assertEquals(iterator2.next(), 120);
-    Assert.assertEquals(iterator2.next(), 180);
+    assertEquals(iterator2.next(), 0);
+    assertEquals(iterator2.next(), 60);
+    assertEquals(iterator2.next(), 120);
+    assertEquals(iterator2.next(), 180);
 
     for (int i = 0; i < numDocs / 10; i++) {
-      Assert.assertEquals(iterator1.next(), iterator2.next());
+      assertEquals(iterator1.next(), iterator2.next());
     }
   }
 
@@ -155,11 +157,11 @@ public class AndFilterOperatorTest {
     AndFilterOperator andOperator = new AndFilterOperator(operators, null, 
numDocs, false);
 
     BlockDocIdIterator iterator = 
andOperator.nextBlock().getBlockDocIdSet().iterator();
-    Assert.assertEquals(iterator.next(), 2);
-    Assert.assertEquals(iterator.next(), 3);
-    Assert.assertEquals(iterator.next(), 6);
-    Assert.assertEquals(iterator.next(), 28);
-    Assert.assertEquals(iterator.next(), Constants.EOF);
+    assertEquals(iterator.next(), 2);
+    assertEquals(iterator.next(), 3);
+    assertEquals(iterator.next(), 6);
+    assertEquals(iterator.next(), 28);
+    assertEquals(iterator.next(), Constants.EOF);
   }
 
   @Test
@@ -171,11 +173,11 @@ public class AndFilterOperatorTest {
     int[] nullDocIds2 = new int[]{3, 4, 5, 6, 7};
 
     AndFilterOperator andFilterOperator = new AndFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
             new TestFilterOperator(docIds2, nullDocIds2, numDocs)), null, 
numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), 
List.of(1, 2));
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of(0, 7, 8, 9));
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), List.of(1, 
2));
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of(0, 7, 8, 9));
   }
 
   @Test
@@ -187,11 +189,11 @@ public class AndFilterOperatorTest {
     int[] nullDocIds2 = new int[]{};
 
     AndFilterOperator andFilterOperator = new AndFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
             new TestFilterOperator(docIds2, nullDocIds2, numDocs)), null, 
numDocs, false);
 
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), 
List.of(0));
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of(1, 2, 3));
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), 
List.of(0));
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of(1, 2, 3));
   }
 
   @Test
@@ -201,11 +203,11 @@ public class AndFilterOperatorTest {
     int[] nullDocIds1 = new int[]{4, 5, 6};
 
     AndFilterOperator andFilterOperator = new AndFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs), 
EmptyFilterOperator.getInstance()), null,
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs), 
EmptyFilterOperator.getInstance()), null,
         numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), 
List.of());
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()),
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), List.of());
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()),
         List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
   }
 
@@ -216,23 +218,23 @@ public class AndFilterOperatorTest {
     int[] nullDocIds1 = new int[]{4, 5, 6};
 
     AndFilterOperator andFilterOperator = new AndFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs), 
new MatchAllFilterOperator(numDocs)), null,
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs), new 
MatchAllFilterOperator(numDocs)), null,
         numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), 
List.of(1, 2, 3));
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of(0, 7, 8, 9));
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()), List.of(1, 
2, 3));
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of(0, 7, 8, 9));
   }
 
   @Test
   public void testAndWithAllMatchesAll() {
     int numDocs = 10;
     AndFilterOperator andFilterOperator =
-        new AndFilterOperator(Arrays.asList(new 
MatchAllFilterOperator(numDocs), new MatchAllFilterOperator(numDocs)),
+        new AndFilterOperator(List.of(new MatchAllFilterOperator(numDocs), new 
MatchAllFilterOperator(numDocs)),
             null, numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()),
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getTrues()),
         List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
-    Assert.assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of());
+    assertEquals(TestUtils.getDocIds(andFilterOperator.getFalses()), 
List.of());
   }
 
   @Test
@@ -242,12 +244,12 @@ public class AndFilterOperatorTest {
     int[] emptyDocIds = new int[0];
 
     AndFilterOperator andFilterOperator = new AndFilterOperator(
-        Arrays.asList(
+        List.of(
             new TestFilterOperator(regularDocIds, numDocs),
             new TestFilterOperator(emptyDocIds, numDocs)
         ), null, numDocs, false);
 
-    Assert.assertEquals((andFilterOperator.getTrues()).getOptimizedDocIdSet(), 
EmptyDocIdSet.getInstance());
+    assertEquals((andFilterOperator.getTrues()).getOptimizedDocIdSet(), 
EmptyDocIdSet.getInstance());
   }
 
   @Test
@@ -256,11 +258,76 @@ public class AndFilterOperatorTest {
     int numDocs2 = 50;
 
     AndFilterOperator andFilterOperator = new AndFilterOperator(
-        Arrays.asList(
+        List.of(
             new MatchAllFilterOperator(numDocs),
             new MatchAllFilterOperator(numDocs2)
         ), null, numDocs, false);
 
-    Assert.assertTrue(andFilterOperator.getTrues() instanceof 
MatchAllDocIdSet);
+    assertTrue(andFilterOperator.getTrues() instanceof MatchAllDocIdSet);
+  }
+
+  @Test
+  public void testCanProduceBitmapsWhenAllChildrenCan() {
+    int numDocs = 40;
+    AndFilterOperator andOperator = new AndFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 10), bitmapOp(numDocs, false, 
3, 10, 20)), null, numDocs, false);
+    assertTrue(andOperator.canProduceBitmaps());
+    assertTrue(andOperator.canOptimizeCount());
+  }
+
+  @Test
+  public void testCannotProduceBitmapsWhenAnyChildCannot() {
+    int numDocs = 40;
+    AndFilterOperator andOperator = new AndFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 10), new TestFilterOperator(new 
int[]{3, 10, 20}, numDocs)), null,
+        numDocs, false);
+    assertFalse(andOperator.canProduceBitmaps());
+    assertFalse(andOperator.canOptimizeCount());
+  }
+
+  @Test
+  public void testGetBitmapsIntersectionForTwoChildren() {
+    int numDocs = 40;
+    AndFilterOperator andOperator = new AndFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 10, 15, 16, 28), 
bitmapOp(numDocs, false, 3, 6, 8, 20, 28)), null,
+        numDocs, false);
+    assertEquals(andOperator.getBitmaps().reduce().toArray(), new int[]{3, 
28});
+  }
+
+  @Test
+  public void testGetBitmapsIntersectionForThreeChildren() {
+    int numDocs = 40;
+    AndFilterOperator andOperator = new AndFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 6, 10, 15, 16, 28), 
bitmapOp(numDocs, false, 3, 6, 8, 20, 28),
+            bitmapOp(numDocs, false, 1, 2, 3, 6, 30)), null, numDocs, false);
+    assertEquals(andOperator.getBitmaps().reduce().toArray(), new int[]{3, 6});
+  }
+
+  @Test
+  public void testGetBitmapsWithExclusiveChild() {
+    int numDocs = 10;
+    // Second child is exclusive: it matches every doc except {2, 4}, so 
reduce() must materialize its complement
+    // before the intersection. The AND is {1, 2, 3, 4, 5} minus {2, 4}.
+    AndFilterOperator andOperator = new AndFilterOperator(
+        List.of(bitmapOp(numDocs, false, 1, 2, 3, 4, 5), bitmapOp(numDocs, 
true, 2, 4)), null, numDocs, false);
+    assertEquals(andOperator.getBitmaps().reduce().toArray(), new int[]{1, 3, 
5});
+  }
+
+  @Test
+  public void testGetBitmapsWithNestedAnd() {
+    int numDocs = 40;
+    AndFilterOperator childAnd = new AndFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 6, 28), bitmapOp(numDocs, 
false, 3, 6, 8, 28)), null, numDocs,
+        false);
+    AndFilterOperator andOperator =
+        new AndFilterOperator(List.of(childAnd, bitmapOp(numDocs, false, 3, 6, 
30)), null, numDocs, false);
+    assertTrue(andOperator.canProduceBitmaps());
+    assertEquals(andOperator.getBitmaps().reduce().toArray(), new int[]{3, 6});
+  }
+
+  private static BitmapBasedFilterOperator bitmapOp(int numDocs, boolean 
exclusive, int... docIds) {
+    MutableRoaringBitmap bitmap = new MutableRoaringBitmap();
+    bitmap.add(docIds);
+    return new BitmapBasedFilterOperator(bitmap.toImmutableRoaringBitmap(), 
exclusive, numDocs);
   }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/OrFilterOperatorTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/OrFilterOperatorTest.java
index 6287cf51f66..7c95bd9e683 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/OrFilterOperatorTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/filter/OrFilterOperatorTest.java
@@ -19,7 +19,6 @@
 package org.apache.pinot.core.operator.filter;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.TreeSet;
@@ -28,9 +27,13 @@ import org.apache.pinot.core.common.BlockDocIdIterator;
 import org.apache.pinot.core.operator.docidsets.EmptyDocIdSet;
 import org.apache.pinot.core.operator.docidsets.MatchAllDocIdSet;
 import org.apache.pinot.segment.spi.Constants;
-import org.testng.Assert;
+import org.roaringbitmap.buffer.MutableRoaringBitmap;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
 
 public class OrFilterOperatorTest {
 
@@ -40,8 +43,8 @@ public class OrFilterOperatorTest {
     int[] docIds1 = new int[]{2, 3, 10, 15, 16, 28};
     int[] docIds2 = new int[]{3, 6, 8, 20, 28};
     TreeSet<Integer> treeSet = new TreeSet<>();
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds1)));
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds2)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds1)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds2)));
     Iterator<Integer> expectedIterator = treeSet.iterator();
 
     List<BaseFilterOperator> operators = new ArrayList<>();
@@ -52,8 +55,9 @@ public class OrFilterOperatorTest {
     BlockDocIdIterator iterator = 
orOperator.nextBlock().getBlockDocIdSet().iterator();
     int docId;
     while ((docId = iterator.next()) != Constants.EOF) {
-      Assert.assertEquals(docId, expectedIterator.next().intValue());
+      assertEquals(docId, expectedIterator.next().intValue());
     }
+    assertFalse(expectedIterator.hasNext());
   }
 
   @Test
@@ -63,9 +67,9 @@ public class OrFilterOperatorTest {
     int[] docIds2 = new int[]{3, 6, 8, 20, 28};
     int[] docIds3 = new int[]{1, 2, 3, 6, 30};
     TreeSet<Integer> treeSet = new TreeSet<>();
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds1)));
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds2)));
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds3)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds1)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds2)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds3)));
     Iterator<Integer> expectedIterator = treeSet.iterator();
 
     List<BaseFilterOperator> operators = new ArrayList<>();
@@ -77,8 +81,9 @@ public class OrFilterOperatorTest {
     BlockDocIdIterator iterator = 
orOperator.nextBlock().getBlockDocIdSet().iterator();
     int docId;
     while ((docId = iterator.next()) != Constants.EOF) {
-      Assert.assertEquals(docId, expectedIterator.next().intValue());
+      assertEquals(docId, expectedIterator.next().intValue());
     }
+    assertFalse(expectedIterator.hasNext());
   }
 
   @Test
@@ -88,9 +93,9 @@ public class OrFilterOperatorTest {
     int[] docIds2 = new int[]{3, 6, 8, 20, 28};
     int[] docIds3 = new int[]{1, 2, 3, 6, 30};
     TreeSet<Integer> treeSet = new TreeSet<>();
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds1)));
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds2)));
-    treeSet.addAll(Arrays.asList(ArrayUtils.toObject(docIds3)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds1)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds2)));
+    treeSet.addAll(List.of(ArrayUtils.toObject(docIds3)));
     Iterator<Integer> expectedIterator = treeSet.iterator();
 
     List<BaseFilterOperator> childOperators = new ArrayList<>();
@@ -106,8 +111,9 @@ public class OrFilterOperatorTest {
     BlockDocIdIterator iterator = 
orOperator.nextBlock().getBlockDocIdSet().iterator();
     int docId;
     while ((docId = iterator.next()) != Constants.EOF) {
-      Assert.assertEquals(docId, expectedIterator.next().intValue());
+      assertEquals(docId, expectedIterator.next().intValue());
     }
+    assertFalse(expectedIterator.hasNext());
   }
 
   @Test
@@ -119,11 +125,11 @@ public class OrFilterOperatorTest {
     int[] nullDocIds2 = new int[]{3, 4, 5, 6, 7};
 
     OrFilterOperator orFilterOperator = new OrFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
             new TestFilterOperator(docIds2, nullDocIds2, numDocs)), null, 
numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), 
List.of(0, 1, 2, 3));
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), 
List.of(8, 9));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), List.of(0, 
1, 2, 3));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), List.of(8, 
9));
   }
 
   @Test
@@ -135,11 +141,11 @@ public class OrFilterOperatorTest {
     int[] nullDocIds2 = new int[]{};
 
     OrFilterOperator orFilterOperator = new OrFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs),
             new TestFilterOperator(docIds2, nullDocIds2, numDocs)), null, 
numDocs, false);
 
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), 
List.of(0, 1, 2, 3));
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), 
List.of(4, 5, 6, 7, 8, 9));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), List.of(0, 
1, 2, 3));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), List.of(4, 
5, 6, 7, 8, 9));
   }
 
   @Test
@@ -149,11 +155,11 @@ public class OrFilterOperatorTest {
     int[] nullDocIds1 = new int[]{4, 5, 6};
 
     OrFilterOperator orFilterOperator = new OrFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs), 
EmptyFilterOperator.getInstance()), null,
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs), 
EmptyFilterOperator.getInstance()), null,
         numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), 
Arrays.asList(1, 2, 3));
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), 
Arrays.asList(0, 7, 8, 9));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), List.of(1, 
2, 3));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), List.of(0, 
7, 8, 9));
   }
 
   @Test
@@ -163,11 +169,11 @@ public class OrFilterOperatorTest {
     int[] nullDocIds1 = new int[]{4, 5, 6};
 
     OrFilterOperator orFilterOperator = new OrFilterOperator(
-        Arrays.asList(new TestFilterOperator(docIds1, nullDocIds1, numDocs), 
new MatchAllFilterOperator(numDocs)), null,
+        List.of(new TestFilterOperator(docIds1, nullDocIds1, numDocs), new 
MatchAllFilterOperator(numDocs)), null,
         numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), 
Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), 
List.of());
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), List.of(0, 
1, 2, 3, 4, 5, 6, 7, 8, 9));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), List.of());
   }
 
   @Test
@@ -175,11 +181,11 @@ public class OrFilterOperatorTest {
     int numDocs = 10;
 
     OrFilterOperator orFilterOperator =
-        new OrFilterOperator(Arrays.asList(EmptyFilterOperator.getInstance(), 
EmptyFilterOperator.getInstance()), null,
+        new OrFilterOperator(List.of(EmptyFilterOperator.getInstance(), 
EmptyFilterOperator.getInstance()), null,
             numDocs, true);
 
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), 
List.of());
-    Assert.assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), 
Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getTrues()), List.of());
+    assertEquals(TestUtils.getDocIds(orFilterOperator.getFalses()), List.of(0, 
1, 2, 3, 4, 5, 6, 7, 8, 9));
   }
 
   @Test
@@ -188,12 +194,12 @@ public class OrFilterOperatorTest {
     int[] regularDocIds = new int[]{1, 2, 3};
 
     OrFilterOperator orFilterOperator = new OrFilterOperator(
-        Arrays.asList(
+        List.of(
             new TestFilterOperator(regularDocIds, numDocs),
             new MatchAllFilterOperator(numDocs)
         ), null, numDocs, false);
 
-    Assert.assertTrue((orFilterOperator.getTrues()).getOptimizedDocIdSet() 
instanceof MatchAllDocIdSet);
+    assertTrue((orFilterOperator.getTrues()).getOptimizedDocIdSet() instanceof 
MatchAllDocIdSet);
   }
 
   @Test
@@ -202,11 +208,77 @@ public class OrFilterOperatorTest {
     int[] emptyDocIds = new int[0];
 
     OrFilterOperator orFilterOperator = new OrFilterOperator(
-        Arrays.asList(
+        List.of(
             new TestFilterOperator(emptyDocIds, numDocs),
             new TestFilterOperator(emptyDocIds, numDocs)
         ), null, numDocs, false);
 
-    Assert.assertTrue(orFilterOperator.getTrues().getOptimizedDocIdSet() 
instanceof EmptyDocIdSet);
+    assertTrue(orFilterOperator.getTrues().getOptimizedDocIdSet() instanceof 
EmptyDocIdSet);
+  }
+
+  @Test
+  public void testCanProduceBitmapsWhenAllChildrenCan() {
+    int numDocs = 40;
+    OrFilterOperator orOperator = new OrFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 10), bitmapOp(numDocs, false, 
3, 10, 20)), null, numDocs, false);
+    assertTrue(orOperator.canProduceBitmaps());
+    assertTrue(orOperator.canOptimizeCount());
+  }
+
+  @Test
+  public void testCannotProduceBitmapsWhenAnyChildCannot() {
+    int numDocs = 40;
+    OrFilterOperator orOperator = new OrFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 10), new TestFilterOperator(new 
int[]{3, 10, 20}, numDocs)), null,
+        numDocs, false);
+    assertFalse(orOperator.canProduceBitmaps());
+    assertFalse(orOperator.canOptimizeCount());
+  }
+
+  @Test
+  public void testGetBitmapsUnionForTwoChildren() {
+    int numDocs = 40;
+    OrFilterOperator orOperator = new OrFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 10, 15, 16, 28), 
bitmapOp(numDocs, false, 3, 6, 8, 20, 28)), null,
+        numDocs, false);
+    assertEquals(orOperator.getBitmaps().reduce().toArray(), new int[]{2, 3, 
6, 8, 10, 15, 16, 20, 28});
+  }
+
+  @Test
+  public void testGetBitmapsUnionForThreeChildren() {
+    int numDocs = 40;
+    OrFilterOperator orOperator = new OrFilterOperator(
+        List.of(bitmapOp(numDocs, false, 2, 3, 6), bitmapOp(numDocs, false, 3, 
6, 8), bitmapOp(numDocs, false, 1)),
+        null, numDocs, false);
+    assertEquals(orOperator.getBitmaps().reduce().toArray(), new int[]{1, 2, 
3, 6, 8});
+  }
+
+  @Test
+  public void testGetBitmapsWithExclusiveChild() {
+    int numDocs = 10;
+    // Second child is exclusive: it matches every doc except {0..7}, i.e. {8, 
9}, so reduce() must materialize its
+    // complement before the union. The OR is {1, 3} union {8, 9}.
+    OrFilterOperator orOperator = new OrFilterOperator(
+        List.of(bitmapOp(numDocs, false, 1, 3), bitmapOp(numDocs, true, 0, 1, 
2, 3, 4, 5, 6, 7)), null, numDocs,
+        false);
+    assertEquals(orOperator.getBitmaps().reduce().toArray(), new int[]{1, 3, 
8, 9});
+  }
+
+  @Test
+  public void testGetBitmapsWithNestedOr() {
+    int numDocs = 40;
+    OrFilterOperator childOr =
+        new OrFilterOperator(List.of(bitmapOp(numDocs, false, 2, 3), 
bitmapOp(numDocs, false, 6, 8)), null,
+            numDocs, false);
+    OrFilterOperator orOperator =
+        new OrFilterOperator(List.of(childOr, bitmapOp(numDocs, false, 1, 
30)), null, numDocs, false);
+    assertTrue(orOperator.canProduceBitmaps());
+    assertEquals(orOperator.getBitmaps().reduce().toArray(), new int[]{1, 2, 
3, 6, 8, 30});
+  }
+
+  private static BitmapBasedFilterOperator bitmapOp(int numDocs, boolean 
exclusive, int... docIds) {
+    MutableRoaringBitmap bitmap = new MutableRoaringBitmap();
+    bitmap.add(docIds);
+    return new BitmapBasedFilterOperator(bitmap.toImmutableRoaringBitmap(), 
exclusive, numDocs);
   }
 }


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

Reply via email to