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

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


The following commit(s) were added to refs/heads/master by this push:
     new c3f543c225c improve filter bundle build for not/istrue/isfalse (#18491)
c3f543c225c is described below

commit c3f543c225cc69414dae797d23f81498e5410208
Author: Clint Wylie <[email protected]>
AuthorDate: Sat Sep 6 16:52:51 2025 -0700

    improve filter bundle build for not/istrue/isfalse (#18491)
---
 .../druid/query/filter/BooleanUnaryFilter.java     |  36 ++++++
 .../apache/druid/query/filter/FilterBundle.java    |  10 ++
 .../org/apache/druid/segment/filter/AndFilter.java |   2 +-
 .../druid/segment/filter/IsBooleanFilter.java      | 142 ++++++++++++---------
 .../org/apache/druid/segment/filter/NotFilter.java | 118 +++++++++--------
 .../org/apache/druid/segment/filter/OrFilter.java  |   2 +-
 6 files changed, 194 insertions(+), 116 deletions(-)

diff --git 
a/processing/src/main/java/org/apache/druid/query/filter/BooleanUnaryFilter.java
 
b/processing/src/main/java/org/apache/druid/query/filter/BooleanUnaryFilter.java
new file mode 100644
index 00000000000..d1402bcde31
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/query/filter/BooleanUnaryFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.druid.query.filter;
+
+import org.apache.druid.segment.index.BitmapColumnIndex;
+
+import javax.annotation.Nullable;
+
+public interface BooleanUnaryFilter extends Filter
+{
+  Filter getBaseFilter();
+
+  /**
+   * Specialized alternative to {@link 
#getBitmapColumnIndex(ColumnIndexSelector)} to allow reuse of
+   * {@link BitmapColumnIndex} created as part of a {@link 
FilterBundle.Builder}
+   */
+  @Nullable
+  BitmapColumnIndex getBitmapColumnIndex(int numRows, FilterBundle.Builder 
baseBuilder);
+}
diff --git 
a/processing/src/main/java/org/apache/druid/query/filter/FilterBundle.java 
b/processing/src/main/java/org/apache/druid/query/filter/FilterBundle.java
index 51db678684a..337714013b1 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/FilterBundle.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/FilterBundle.java
@@ -178,6 +178,16 @@ public class FilterBundle
           childBuilders.add(new FilterBundle.Builder(childFilter, 
columnIndexSelector, cursorAutoArrangeFilters));
         }
         this.bitmapColumnIndex = 
bool.getBitmapColumnIndex(columnIndexSelector.getBitmapFactory(), 
childBuilders);
+      } else if (filter instanceof BooleanUnaryFilter) {
+        final BooleanUnaryFilter bool = (BooleanUnaryFilter) filter;
+        childBuilders = new ArrayList<>(1);
+        final FilterBundle.Builder childBuilder = new FilterBundle.Builder(
+            bool.getBaseFilter(),
+            columnIndexSelector,
+            cursorAutoArrangeFilters
+        );
+        childBuilders.add(childBuilder);
+        this.bitmapColumnIndex = 
bool.getBitmapColumnIndex(columnIndexSelector.getNumRows(), childBuilder);
       } else {
         this.childBuilders = List.of();
         this.bitmapColumnIndex = 
filter.getBitmapColumnIndex(columnIndexSelector);
diff --git 
a/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java 
b/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java
index 57cb233000d..e1c96d4ae08 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/AndFilter.java
@@ -152,7 +152,7 @@ public class AndFilter implements BooleanFilter
       @Override
       public int estimatedComputeCost()
       {
-        // There's no additional cost on AND filter, cost in child filters 
would be summed.
+        // There's no additional cost on AND filter, cost in child 
FilterBundle.Builder will be summed
         return 0;
       }
 
diff --git 
a/processing/src/main/java/org/apache/druid/segment/filter/IsBooleanFilter.java 
b/processing/src/main/java/org/apache/druid/segment/filter/IsBooleanFilter.java
index 792b305cccb..c41472107a0 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/filter/IsBooleanFilter.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/filter/IsBooleanFilter.java
@@ -21,8 +21,10 @@ package org.apache.druid.segment.filter;
 
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.query.BitmapResultFactory;
+import org.apache.druid.query.filter.BooleanUnaryFilter;
 import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.query.filter.Filter;
+import org.apache.druid.query.filter.FilterBundle;
 import org.apache.druid.query.filter.ValueMatcher;
 import org.apache.druid.query.filter.vector.BaseVectorValueMatcher;
 import org.apache.druid.query.filter.vector.ReadableVectorMatch;
@@ -51,7 +53,7 @@ import java.util.Set;
  * @see org.apache.druid.query.filter.IsTrueDimFilter
  * @see org.apache.druid.query.filter.IsFalseDimFilter
  */
-public class IsBooleanFilter implements Filter
+public class IsBooleanFilter implements BooleanUnaryFilter
 {
   private final Filter baseFilter;
   private final boolean isTrue;
@@ -67,6 +69,7 @@ public class IsBooleanFilter implements Filter
     return isTrue;
   }
 
+  @Override
   public Filter getBaseFilter()
   {
     return baseFilter;
@@ -74,70 +77,16 @@ public class IsBooleanFilter implements Filter
 
   @Nullable
   @Override
-  public BitmapColumnIndex getBitmapColumnIndex(ColumnIndexSelector selector)
+  public BitmapColumnIndex getBitmapColumnIndex(int numRows, 
FilterBundle.Builder baseBuilder)
   {
-    final BitmapColumnIndex baseIndex = 
baseFilter.getBitmapColumnIndex(selector);
-    if (baseIndex != null && (isTrue || 
baseIndex.getIndexCapabilities().isInvertible())) {
-      return new BitmapColumnIndex()
-      {
-        @Override
-        public ColumnIndexCapabilities getIndexCapabilities()
-        {
-          return baseIndex.getIndexCapabilities();
-        }
-
-        @Override
-        public int estimatedComputeCost()
-        {
-          return baseIndex.estimatedComputeCost();
-        }
-
-        @Override
-        public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory, boolean includeUnknown)
-        {
-          if (isTrue) {
-            return baseIndex.computeBitmapResult(bitmapResultFactory, false);
-          }
-          return bitmapResultFactory.complement(
-              baseIndex.computeBitmapResult(bitmapResultFactory, true),
-              selector.getNumRows()
-          );
-        }
-
-        @Nullable
-        @Override
-        public <T> T computeBitmapResult(
-            BitmapResultFactory<T> bitmapResultFactory,
-            int applyRowCount,
-            int totalRowCount,
-            boolean includeUnknown
-        )
-        {
-          if (isTrue) {
-            return baseIndex.computeBitmapResult(
-                bitmapResultFactory,
-                applyRowCount,
-                totalRowCount,
-                false
-            );
-          }
-
-          final T result = baseIndex.computeBitmapResult(
-              bitmapResultFactory,
-              applyRowCount,
-              totalRowCount,
-              true
-          );
-
-          if (result == null) {
-            return null;
-          }
+    return getBitmapColumnIndex(numRows, isTrue, 
baseBuilder.getBitmapColumnIndex());
+  }
 
-          return bitmapResultFactory.complement(result, selector.getNumRows());
-        }
-      };
-    }
-    return null;
+  @Nullable
+  @Override
+  public BitmapColumnIndex getBitmapColumnIndex(ColumnIndexSelector selector)
+  {
+    return getBitmapColumnIndex(selector.getNumRows(), isTrue, 
baseFilter.getBitmapColumnIndex(selector));
   }
 
   @Override
@@ -238,4 +187,71 @@ public class IsBooleanFilter implements Filter
     // to return a different hash from baseFilter
     return Objects.hash(1, baseFilter, isTrue);
   }
+
+  @Nullable
+  private static BitmapColumnIndex getBitmapColumnIndex(int numRows, boolean 
isTrue, BitmapColumnIndex baseIndex)
+  {
+    if (baseIndex != null && (isTrue || 
baseIndex.getIndexCapabilities().isInvertible())) {
+      return new BitmapColumnIndex()
+      {
+        @Override
+        public ColumnIndexCapabilities getIndexCapabilities()
+        {
+          return baseIndex.getIndexCapabilities();
+        }
+
+        @Override
+        public int estimatedComputeCost()
+        {
+          // There's no additional cost on is boolean filter, cost will come 
from child FilterBundle.Builder
+          return 0;
+        }
+
+        @Override
+        public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory, boolean includeUnknown)
+        {
+          if (isTrue) {
+            return baseIndex.computeBitmapResult(bitmapResultFactory, false);
+          }
+          return bitmapResultFactory.complement(
+              baseIndex.computeBitmapResult(bitmapResultFactory, true),
+              numRows
+          );
+        }
+
+        @Nullable
+        @Override
+        public <T> T computeBitmapResult(
+            BitmapResultFactory<T> bitmapResultFactory,
+            int applyRowCount,
+            int totalRowCount,
+            boolean includeUnknown
+        )
+        {
+          if (isTrue) {
+            return baseIndex.computeBitmapResult(
+                bitmapResultFactory,
+                applyRowCount,
+                totalRowCount,
+                false
+            );
+          }
+
+          final T result = baseIndex.computeBitmapResult(
+              bitmapResultFactory,
+              applyRowCount,
+              totalRowCount,
+              true
+          );
+
+          if (result == null) {
+            return null;
+          }
+
+          return bitmapResultFactory.complement(result, numRows);
+        }
+      };
+    }
+    return null;
+  }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java 
b/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java
index 52e66d561e8..4ce5d15c098 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/NotFilter.java
@@ -21,8 +21,10 @@ package org.apache.druid.segment.filter;
 
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.query.BitmapResultFactory;
+import org.apache.druid.query.filter.BooleanUnaryFilter;
 import org.apache.druid.query.filter.ColumnIndexSelector;
 import org.apache.druid.query.filter.Filter;
+import org.apache.druid.query.filter.FilterBundle;
 import org.apache.druid.query.filter.ValueMatcher;
 import org.apache.druid.query.filter.vector.BaseVectorValueMatcher;
 import org.apache.druid.query.filter.vector.ReadableVectorMatch;
@@ -42,18 +44,18 @@ import java.util.Set;
 
 /**
  * Nice filter you have there... NOT!
- *
+ * <p>
  * This filter inverts the {@code includeUnknown} flag to properly ap Druids 
native two-valued logic (true, false) to
  * SQL three-valued logic (true, false, unknown). At the top level, this flag 
is always passed in as 'false', and is
  * only flipped by this filter. Other logical filters ({@link AndFilter} and 
{@link OrFilter}) propagate the value of
  * {@code includeUnknown} to their children.
- *
+ * <p>
  * For example, if the base filter is equality, by default value matchers and 
indexes only return true for the rows
  * that are equal to the value. When wrapped in a not filter, the not filter 
indicates that the equality matchers and
  * indexes should also include the null or 'unknown' values as matches, so 
that inverting the match does not incorrectly
  * include these null values as matches.
  */
-public class NotFilter implements Filter
+public class NotFilter implements BooleanUnaryFilter
 {
   private final Filter baseFilter;
 
@@ -64,56 +66,16 @@ public class NotFilter implements Filter
 
   @Nullable
   @Override
-  public BitmapColumnIndex getBitmapColumnIndex(ColumnIndexSelector selector)
+  public BitmapColumnIndex getBitmapColumnIndex(int numRows, 
FilterBundle.Builder baseBuilder)
   {
-    final BitmapColumnIndex baseIndex = 
baseFilter.getBitmapColumnIndex(selector);
-    if (baseIndex != null && baseIndex.getIndexCapabilities().isInvertible()) {
-      return new BitmapColumnIndex()
-      {
-        @Override
-        public ColumnIndexCapabilities getIndexCapabilities()
-        {
-          return baseIndex.getIndexCapabilities();
-        }
-
-        @Override
-        public int estimatedComputeCost()
-        {
-          return baseIndex.estimatedComputeCost();
-        }
-
-        @Override
-        public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory, boolean includeUnknown)
-        {
-          return bitmapResultFactory.complement(
-              baseIndex.computeBitmapResult(bitmapResultFactory, 
!includeUnknown),
-              selector.getNumRows()
-          );
-        }
+    return getBitmapColumnIndex(numRows, baseBuilder.getBitmapColumnIndex());
+  }
 
-        @Nullable
-        @Override
-        public <T> T computeBitmapResult(
-            BitmapResultFactory<T> bitmapResultFactory,
-            int applyRowCount,
-            int totalRowCount,
-            boolean includeUnknown
-        )
-        {
-          final T result = baseIndex.computeBitmapResult(
-              bitmapResultFactory,
-              applyRowCount,
-              totalRowCount,
-              !includeUnknown
-          );
-          if (result == null) {
-            return null;
-          }
-          return bitmapResultFactory.complement(result, selector.getNumRows());
-        }
-      };
-    }
-    return null;
+  @Nullable
+  @Override
+  public BitmapColumnIndex getBitmapColumnIndex(ColumnIndexSelector selector)
+  {
+    return getBitmapColumnIndex(selector.getNumRows(), 
baseFilter.getBitmapColumnIndex(selector));
   }
 
   @Override
@@ -209,8 +171,62 @@ public class NotFilter implements Filter
     return Objects.hash(1, baseFilter);
   }
 
+  @Override
   public Filter getBaseFilter()
   {
     return baseFilter;
   }
+
+  @Nullable
+  private static BitmapColumnIndex getBitmapColumnIndex(int numRows, 
BitmapColumnIndex baseIndex)
+  {
+    if (baseIndex != null && baseIndex.getIndexCapabilities().isInvertible()) {
+      return new BitmapColumnIndex()
+      {
+        @Override
+        public ColumnIndexCapabilities getIndexCapabilities()
+        {
+          return baseIndex.getIndexCapabilities();
+        }
+
+        @Override
+        public int estimatedComputeCost()
+        {
+          // There's no additional cost on NOT filter, cost will come from 
child FilterBundle.Builder
+          return 0;
+        }
+
+        @Override
+        public <T> T computeBitmapResult(BitmapResultFactory<T> 
bitmapResultFactory, boolean includeUnknown)
+        {
+          return bitmapResultFactory.complement(
+              baseIndex.computeBitmapResult(bitmapResultFactory, 
!includeUnknown),
+              numRows
+          );
+        }
+
+        @Nullable
+        @Override
+        public <T> T computeBitmapResult(
+            BitmapResultFactory<T> bitmapResultFactory,
+            int applyRowCount,
+            int totalRowCount,
+            boolean includeUnknown
+        )
+        {
+          final T result = baseIndex.computeBitmapResult(
+              bitmapResultFactory,
+              applyRowCount,
+              totalRowCount,
+              !includeUnknown
+          );
+          if (result == null) {
+            return null;
+          }
+          return bitmapResultFactory.complement(result, numRows);
+        }
+      };
+    }
+    return null;
+  }
 }
diff --git 
a/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java 
b/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java
index dd68dc40c64..53e37036336 100644
--- a/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java
+++ b/processing/src/main/java/org/apache/druid/segment/filter/OrFilter.java
@@ -473,7 +473,7 @@ public class OrFilter implements BooleanFilter
       @Override
       public int estimatedComputeCost()
       {
-        // There's no additional cost on OR filter, cost in child filters 
would be summed.
+        // There's no additional cost on OR filter, cost in child 
FilterBundle.Builder will be summed
         return 0;
       }
 


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

Reply via email to