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

gianm 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 034ef1beacd feat: Add indexes to ConstantColumns. (#19793)
034ef1beacd is described below

commit 034ef1beacd8831f983b34cc577e336b60ec1004
Author: Gian Merlino <[email protected]>
AuthorDate: Thu Jul 30 11:10:29 2026 -0700

    feat: Add indexes to ConstantColumns. (#19793)
    
    This patch adds indexes to ConstantColumns (itself added in #19675),
    starting with ValueIndexes, ValueSetIndexes, and DruidPredicateIndexes.
---
 .../druid/segment/column/ConstantColumns.java      |   4 +-
 .../segment/index/ConstantColumnIndexSupplier.java | 237 ++++++++++++++++
 .../index/ConstantColumnIndexSupplierTest.java     | 304 +++++++++++++++++++++
 3 files changed, 544 insertions(+), 1 deletion(-)

diff --git 
a/processing/src/main/java/org/apache/druid/segment/column/ConstantColumns.java 
b/processing/src/main/java/org/apache/druid/segment/column/ConstantColumns.java
index e2e495e70af..ebf947bba09 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/column/ConstantColumns.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/column/ConstantColumns.java
@@ -26,6 +26,7 @@ import org.apache.druid.error.DruidException;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.segment.data.ConstantColumnarInts;
 import org.apache.druid.segment.data.ConstantUtf8Indexed;
+import org.apache.druid.segment.index.ConstantColumnIndexSupplier;
 
 import javax.annotation.Nullable;
 import java.nio.ByteBuffer;
@@ -84,7 +85,8 @@ public final class ConstantColumns
     final ColumnBuilder builder = new ColumnBuilder()
         .setType(type)
         .setHasMultipleValues(false)
-        .setHasNulls(value == null);
+        .setHasNulls(value == null)
+        .setIndexSupplier(new ConstantColumnIndexSupplier(type, value, 
numRows, bitmapFactory), true, false);
 
     if (type.is(ValueType.STRING)) {
       final ByteBuffer utf8 = value == null ? null : 
StringUtils.toUtf8ByteBuffer((String) value);
diff --git 
a/processing/src/main/java/org/apache/druid/segment/index/ConstantColumnIndexSupplier.java
 
b/processing/src/main/java/org/apache/druid/segment/index/ConstantColumnIndexSupplier.java
new file mode 100644
index 00000000000..224eef69edc
--- /dev/null
+++ 
b/processing/src/main/java/org/apache/druid/segment/index/ConstantColumnIndexSupplier.java
@@ -0,0 +1,237 @@
+/*
+ * 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.segment.index;
+
+import org.apache.druid.collections.bitmap.BitmapFactory;
+import org.apache.druid.error.DruidException;
+import org.apache.druid.math.expr.ExprEval;
+import org.apache.druid.math.expr.ExpressionType;
+import org.apache.druid.query.filter.DruidDoublePredicate;
+import org.apache.druid.query.filter.DruidFloatPredicate;
+import org.apache.druid.query.filter.DruidLongPredicate;
+import org.apache.druid.query.filter.DruidPredicateFactory;
+import org.apache.druid.query.filter.DruidPredicateMatch;
+import org.apache.druid.segment.column.ColumnIndexSupplier;
+import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.segment.column.TypeSignature;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.segment.index.semantic.DruidPredicateIndexes;
+import org.apache.druid.segment.index.semantic.NullValueIndex;
+import org.apache.druid.segment.index.semantic.ValueIndexes;
+import org.apache.druid.segment.index.semantic.ValueSetIndexes;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Index supplier for a primitive column whose value is the same for every 
row. All indexes produced by this class
+ * are either {@link AllTrueBitmapColumnIndex}, {@link 
AllFalseBitmapColumnIndex},
+ * or {@link AllUnknownBitmapColumnIndex}.
+ */
+public class ConstantColumnIndexSupplier implements ColumnIndexSupplier
+{
+  /**
+   * The constant value, normalized to {@link #type}.
+   */
+  @Nullable
+  private final Object value;
+
+  /**
+   * Type of {@link #value}.
+   */
+  private final ColumnType type;
+
+  /**
+   * Expression type equivalent of {@link #type}.
+   */
+  private final ExpressionType exprType;
+
+  private final int numRows;
+  private final BitmapFactory bitmapFactory;
+
+  public ConstantColumnIndexSupplier(
+      final ColumnType type,
+      @Nullable final Object value,
+      final int numRows,
+      final BitmapFactory bitmapFactory
+  )
+  {
+    if (!type.is(ValueType.STRING) && !type.isNumeric()) {
+      throw DruidException.defensive("Cannot handle type[%s]", type);
+    }
+    this.type = type;
+    this.numRows = numRows;
+    this.bitmapFactory = bitmapFactory;
+    this.exprType = ExpressionType.fromColumnTypeStrict(type);
+
+    // Normalize the value to the expected type.
+    final ExprEval<?> constant = ExprEval.bestEffortOf(value).castTo(exprType);
+    if (constant.value() == null) {
+      this.value = null;
+    } else {
+      // FLOAT has no ExpressionType of its own, so explicitly narrow it back 
down.
+      this.value = type.is(ValueType.FLOAT) ? (float) constant.asDouble() : 
constant.value();
+    }
+  }
+
+  @Nullable
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T> T as(Class<T> clazz)
+  {
+    if (clazz.equals(NullValueIndex.class)) {
+      final BitmapColumnIndex nullIndex = value == null ? allTrue() : 
allFalse();
+      return (T) (NullValueIndex) () -> nullIndex;
+    } else if (clazz.equals(ValueIndexes.class)) {
+      return (T) new ConstantValueIndexes();
+    } else if (clazz.equals(ValueSetIndexes.class)) {
+      return (T) new ConstantValueSetIndexes();
+    } else if (clazz.equals(DruidPredicateIndexes.class)) {
+      return (T) new ConstantDruidPredicateIndexes();
+    }
+    return null;
+  }
+
+  private BitmapColumnIndex allTrue()
+  {
+    return new AllTrueBitmapColumnIndex(bitmapFactory, numRows);
+  }
+
+  private BitmapColumnIndex allFalse()
+  {
+    return new AllFalseBitmapColumnIndex(bitmapFactory);
+  }
+
+  private BitmapColumnIndex allUnknown()
+  {
+    return new AllUnknownBitmapColumnIndex(bitmapFactory, numRows);
+  }
+
+  /**
+   * Whether some incoming value is equal to {@link #value}.
+   */
+  private boolean matchesConstant(@Nullable Object matchValue, 
TypeSignature<ValueType> matchValueType)
+  {
+    if (matchValue == null) {
+      return value == null;
+    }
+    if (value == null) {
+      return false;
+    }
+    final ExprEval<?> eval = 
ExprEval.ofType(ExpressionType.fromColumnTypeStrict(matchValueType), 
matchValue);
+    final ExprEval<?> castForComparison = 
ExprEval.castForEqualityComparison(eval, exprType);
+    if (castForComparison == null || castForComparison.value() == null) {
+      return false;
+    }
+    return switch (type.getType()) {
+      case STRING -> value.equals(castForComparison.asString());
+      case LONG -> (Long) value == castForComparison.asLong();
+      // compare bits instead of == to canonicalize NaN
+      case FLOAT -> Float.floatToIntBits((Float) value) == 
Float.floatToIntBits((float) castForComparison.asDouble());
+      case DOUBLE -> Double.doubleToLongBits((Double) value) == 
Double.doubleToLongBits(castForComparison.asDouble());
+      default -> throw DruidException.defensive("Cannot match values for 
type[%s]", type);
+    };
+  }
+
+  private class ConstantValueIndexes implements ValueIndexes
+  {
+    @Nullable
+    @Override
+    public BitmapColumnIndex forValue(@Nonnull Object matchValue, 
TypeSignature<ValueType> matchValueType)
+    {
+      if (!matchValueType.isPrimitive()) {
+        return null;
+      } else if (value == null) {
+        return allUnknown();
+      } else {
+        return matchesConstant(matchValue, matchValueType) ? allTrue() : 
allFalse();
+      }
+    }
+  }
+
+  private class ConstantValueSetIndexes implements ValueSetIndexes
+  {
+    @Nullable
+    @Override
+    public BitmapColumnIndex forSortedValues(@Nonnull List<?> sortedValues, 
TypeSignature<ValueType> matchValueType)
+    {
+      if (!matchValueType.isPrimitive()) {
+        return null;
+      }
+
+      if (sortedValues.isEmpty()) {
+        return allFalse();
+      }
+
+      if (matchValueType.getType() == type.getType()) {
+        if (Collections.binarySearch(sortedValues, value, 
matchValueType.getNullableStrategy()) >= 0) {
+          return allTrue();
+        }
+      } else {
+        for (final Object matchValue : sortedValues) {
+          if (matchesConstant(matchValue, matchValueType)) {
+            return allTrue();
+          }
+        }
+      }
+
+      return value == null ? allUnknown() : allFalse();
+    }
+  }
+
+  private class ConstantDruidPredicateIndexes implements DruidPredicateIndexes
+  {
+    @Override
+    public BitmapColumnIndex forPredicate(DruidPredicateFactory matcherFactory)
+    {
+      final DruidPredicateMatch match;
+      switch (type.getType()) {
+        case STRING: {
+          match = matcherFactory.makeStringPredicate().apply((String) value);
+          break;
+        }
+        case LONG: {
+          final DruidLongPredicate predicate = 
matcherFactory.makeLongPredicate();
+          match = value == null ? predicate.applyNull() : 
predicate.applyLong((Long) value);
+          break;
+        }
+        case FLOAT: {
+          final DruidFloatPredicate predicate = 
matcherFactory.makeFloatPredicate();
+          match = value == null ? predicate.applyNull() : 
predicate.applyFloat((Float) value);
+          break;
+        }
+        case DOUBLE: {
+          final DruidDoublePredicate predicate = 
matcherFactory.makeDoublePredicate();
+          match = value == null ? predicate.applyNull() : 
predicate.applyDouble((Double) value);
+          break;
+        }
+        default:
+          throw DruidException.defensive("Cannot apply predicates for 
type[%s]", type);
+      }
+      return switch (match) {
+        case TRUE -> allTrue();
+        case FALSE -> allFalse();
+        case UNKNOWN -> allUnknown();
+      };
+    }
+  }
+}
diff --git 
a/processing/src/test/java/org/apache/druid/segment/index/ConstantColumnIndexSupplierTest.java
 
b/processing/src/test/java/org/apache/druid/segment/index/ConstantColumnIndexSupplierTest.java
new file mode 100644
index 00000000000..8b480daf013
--- /dev/null
+++ 
b/processing/src/test/java/org/apache/druid/segment/index/ConstantColumnIndexSupplierTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.segment.index;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.druid.collections.bitmap.BitmapFactory;
+import org.apache.druid.collections.bitmap.ImmutableBitmap;
+import org.apache.druid.error.DruidException;
+import org.apache.druid.query.BitmapResultFactory;
+import org.apache.druid.query.DefaultBitmapResultFactory;
+import org.apache.druid.query.filter.SelectorPredicateFactory;
+import org.apache.druid.segment.column.BaseColumnHolder;
+import org.apache.druid.segment.column.ColumnIndexSupplier;
+import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.segment.column.ConstantColumns;
+import org.apache.druid.segment.data.RoaringBitmapSerdeFactory;
+import org.apache.druid.segment.index.semantic.DruidPredicateIndexes;
+import org.apache.druid.segment.index.semantic.NullValueIndex;
+import org.apache.druid.segment.index.semantic.ValueIndexes;
+import org.apache.druid.segment.index.semantic.ValueSetIndexes;
+import org.apache.druid.testing.InitializedNullHandlingTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+
+public class ConstantColumnIndexSupplierTest extends 
InitializedNullHandlingTest
+{
+  private static final int NUM_ROWS = 100;
+  private static final BitmapFactory BITMAP_FACTORY = 
RoaringBitmapSerdeFactory.getInstance().getBitmapFactory();
+  private static final BitmapResultFactory<ImmutableBitmap> 
BITMAP_RESULT_FACTORY =
+      new DefaultBitmapResultFactory(BITMAP_FACTORY);
+
+  @Test
+  public void testUnsupportedType()
+  {
+    Assertions.assertThrows(
+        DruidException.class,
+        () -> makeSupplier(ColumnType.STRING_ARRAY, null)
+    );
+  }
+
+  @Test
+  public void testNullValueIndex()
+  {
+    assertIsTrue(makeSupplier(ColumnType.STRING, 
null).as(NullValueIndex.class).get());
+    assertIsTrue(makeSupplier(ColumnType.LONG, 
null).as(NullValueIndex.class).get());
+    assertIsTrue(makeSupplier(ColumnType.LONG, 
"abc").as(NullValueIndex.class).get()); // true due to coercion
+    assertIsFalse(makeSupplier(ColumnType.STRING, 
"abc").as(NullValueIndex.class).get());
+    assertIsFalse(makeSupplier(ColumnType.LONG, 
10L).as(NullValueIndex.class).get());
+  }
+
+  @Test
+  public void testValueIndexesString()
+  {
+    final ValueIndexes indexes = makeSupplier(ColumnType.STRING, 
"10").as(ValueIndexes.class);
+
+    assertIsTrue(indexes.forValue("10", ColumnType.STRING));
+    assertIsFalse(indexes.forValue("abc", ColumnType.STRING));
+    assertIsTrue(indexes.forValue(10L, ColumnType.LONG));
+    assertIsFalse(indexes.forValue(11L, ColumnType.LONG));
+    assertIsFalse(indexes.forValue(10.0, ColumnType.DOUBLE)); // Because it 
stringifies to 10.0
+    Assertions.assertNull(indexes.forValue(new Object[]{"10"}, 
ColumnType.STRING_ARRAY));
+  }
+
+  @Test
+  public void testValueIndexesLong()
+  {
+    final ValueIndexes indexes = makeSupplier(ColumnType.LONG, 
10L).as(ValueIndexes.class);
+
+    assertIsTrue(indexes.forValue(10L, ColumnType.LONG));
+    assertIsFalse(indexes.forValue(11L, ColumnType.LONG));
+    assertIsTrue(indexes.forValue("10", ColumnType.STRING));
+    assertIsTrue(indexes.forValue("10.0", ColumnType.STRING));
+    assertIsFalse(indexes.forValue("abc", ColumnType.STRING));
+    assertIsTrue(indexes.forValue(10.0, ColumnType.DOUBLE));
+    assertIsFalse(indexes.forValue(10.5, ColumnType.DOUBLE));
+  }
+
+  @Test
+  public void testValueIndexesFloat()
+  {
+    final ValueIndexes indexes = makeSupplier(ColumnType.FLOAT, 
1.1f).as(ValueIndexes.class);
+
+    assertIsTrue(indexes.forValue(1.1f, ColumnType.FLOAT));
+    assertIsTrue(indexes.forValue(1.1, ColumnType.DOUBLE));
+    assertIsFalse(indexes.forValue(1.2, ColumnType.DOUBLE));
+    assertIsFalse(indexes.forValue(1L, ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueIndexesDouble()
+  {
+    final ValueIndexes indexes = makeSupplier(ColumnType.DOUBLE, 
1.5).as(ValueIndexes.class);
+
+    assertIsTrue(indexes.forValue(1.5, ColumnType.DOUBLE));
+    assertIsFalse(indexes.forValue(1.6, ColumnType.DOUBLE));
+    assertIsTrue(indexes.forValue("1.5", ColumnType.STRING));
+    assertIsFalse(indexes.forValue(1L, ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueIndexesNullConstant()
+  {
+    assertIsUnknown(makeSupplier(ColumnType.STRING, 
null).as(ValueIndexes.class).forValue("abc", ColumnType.STRING));
+    assertIsUnknown(makeSupplier(ColumnType.LONG, 
null).as(ValueIndexes.class).forValue(10L, ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueSetIndexesString()
+  {
+    final ValueSetIndexes indexes = makeSupplier(ColumnType.STRING, 
"10").as(ValueSetIndexes.class);
+
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of("10", "abc"), 
ColumnType.STRING));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of("11", "abc"), 
ColumnType.STRING));
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of(1L, 10L), 
ColumnType.LONG));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1L, 11L), 
ColumnType.LONG));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(10.0), 
ColumnType.DOUBLE)); // Because it stringifies to 10.0
+  }
+
+  @Test
+  public void testValueSetIndexesLong()
+  {
+    final ValueSetIndexes indexes = makeSupplier(ColumnType.LONG, 
10L).as(ValueSetIndexes.class);
+
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of(1L, 10L, 100L), 
ColumnType.LONG));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1L, 100L), 
ColumnType.LONG));
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of("1", "10"), 
ColumnType.STRING));
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of("1", "10.0"), 
ColumnType.STRING));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of("1", "abc"), 
ColumnType.STRING));
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of(9.5, 10.0), 
ColumnType.DOUBLE));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(9.5, 10.5), 
ColumnType.DOUBLE));
+    assertIsFalse(indexes.forSortedValues(Collections.emptyList(), 
ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueSetIndexesFloat()
+  {
+    final ValueSetIndexes indexes = makeSupplier(ColumnType.FLOAT, 
1.1f).as(ValueSetIndexes.class);
+
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of(1.0f, 1.1f), 
ColumnType.FLOAT));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1.0f, 1.2f), 
ColumnType.FLOAT));
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of(1.1), 
ColumnType.DOUBLE));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1.2), 
ColumnType.DOUBLE));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1L), 
ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueSetIndexesDouble()
+  {
+    final ValueSetIndexes indexes = makeSupplier(ColumnType.DOUBLE, 
1.5).as(ValueSetIndexes.class);
+
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of(1.0, 1.5), 
ColumnType.DOUBLE));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1.0, 1.6), 
ColumnType.DOUBLE));
+    assertIsTrue(indexes.forSortedValues(ImmutableList.of("1.5"), 
ColumnType.STRING));
+    assertIsFalse(indexes.forSortedValues(ImmutableList.of(1L), 
ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueSetIndexesNullConstant()
+  {
+    final ValueSetIndexes indexes = makeSupplier(ColumnType.LONG, 
null).as(ValueSetIndexes.class);
+
+    assertIsUnknown(indexes.forSortedValues(ImmutableList.of(1L, 10L), 
ColumnType.LONG));
+    assertIsFalse(indexes.forSortedValues(Collections.emptyList(), 
ColumnType.LONG));
+    // null in the value set matches null rows, matching the behavior of 
dictionary backed value set indexes
+    assertIsTrue(indexes.forSortedValues(Collections.singletonList(null), 
ColumnType.LONG));
+  }
+
+  @Test
+  public void testValueSetIndexesNonPrimitiveMatchType()
+  {
+    final ValueSetIndexes indexes = makeSupplier(ColumnType.LONG, 
10L).as(ValueSetIndexes.class);
+    Assertions.assertNull(indexes.forSortedValues(ImmutableList.of(new 
Object[]{10L}), ColumnType.LONG_ARRAY));
+  }
+
+  @Test
+  public void testPredicateIndexesString()
+  {
+    final DruidPredicateIndexes indexes = makeSupplier(ColumnType.STRING, 
"abc").as(DruidPredicateIndexes.class);
+
+    assertIsTrue(indexes.forPredicate(new SelectorPredicateFactory("abc")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory("def")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory(null)));
+  }
+
+  @Test
+  public void testPredicateIndexesLong()
+  {
+    final DruidPredicateIndexes indexes = makeSupplier(ColumnType.LONG, 
10L).as(DruidPredicateIndexes.class);
+
+    assertIsTrue(indexes.forPredicate(new SelectorPredicateFactory("10")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory("11")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory("abc")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory(null)));
+  }
+
+  @Test
+  public void testPredicateIndexesFloat()
+  {
+    final DruidPredicateIndexes indexes = makeSupplier(ColumnType.FLOAT, 
1.1f).as(DruidPredicateIndexes.class);
+
+    assertIsTrue(indexes.forPredicate(new SelectorPredicateFactory("1.1")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory("1.2")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory(null)));
+  }
+
+  @Test
+  public void testPredicateIndexesDouble()
+  {
+    final DruidPredicateIndexes indexes = makeSupplier(ColumnType.DOUBLE, 
1.5).as(DruidPredicateIndexes.class);
+
+    assertIsTrue(indexes.forPredicate(new SelectorPredicateFactory("1.5")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory("1.6")));
+    assertIsFalse(indexes.forPredicate(new SelectorPredicateFactory(null)));
+  }
+
+  @Test
+  public void testPredicateIndexesNullConstant()
+  {
+    assertIsUnknown(
+        makeSupplier(ColumnType.LONG, null).as(DruidPredicateIndexes.class)
+                                           .forPredicate(new 
SelectorPredicateFactory("10"))
+    );
+    assertIsTrue(
+        makeSupplier(ColumnType.LONG, null).as(DruidPredicateIndexes.class)
+                                           .forPredicate(new 
SelectorPredicateFactory(null))
+    );
+    assertIsUnknown(
+        makeSupplier(ColumnType.STRING, null).as(DruidPredicateIndexes.class)
+                                             .forPredicate(new 
SelectorPredicateFactory("abc"))
+    );
+    assertIsTrue(
+        makeSupplier(ColumnType.STRING, null).as(DruidPredicateIndexes.class)
+                                             .forPredicate(new 
SelectorPredicateFactory(null))
+    );
+  }
+
+  @Test
+  public void testUnsupportedIndexes()
+  {
+    Assertions.assertNull(makeSupplier(ColumnType.STRING, 
"abc").as(String.class));
+  }
+
+  @Test
+  public void testConstantColumnHolderSuppliesIndexes()
+  {
+    final BaseColumnHolder holder =
+        ConstantColumns.makeConstantColumnHolder(ColumnType.STRING, "abc", 
NUM_ROWS, BITMAP_FACTORY);
+    final ColumnIndexSupplier indexSupplier = holder.getIndexSupplier();
+    Assertions.assertInstanceOf(ConstantColumnIndexSupplier.class, 
indexSupplier);
+    assertIsTrue(indexSupplier.as(ValueIndexes.class).forValue("abc", 
ColumnType.STRING));
+    assertIsFalse(indexSupplier.as(NullValueIndex.class).get());
+  }
+
+  private static ConstantColumnIndexSupplier makeSupplier(ColumnType type, 
@Nullable Object value)
+  {
+    return new ConstantColumnIndexSupplier(type, value, NUM_ROWS, 
BITMAP_FACTORY);
+  }
+
+  private static void assertIsTrue(@Nullable BitmapColumnIndex index)
+  {
+    Assertions.assertNotNull(index);
+    Assertions.assertEquals(NUM_ROWS, cardinality(index, false));
+    Assertions.assertEquals(NUM_ROWS, cardinality(index, true));
+  }
+
+  private static void assertIsFalse(@Nullable BitmapColumnIndex index)
+  {
+    Assertions.assertNotNull(index);
+    Assertions.assertEquals(0, cardinality(index, false));
+    Assertions.assertEquals(0, cardinality(index, true));
+  }
+
+  private static void assertIsUnknown(@Nullable BitmapColumnIndex index)
+  {
+    Assertions.assertNotNull(index);
+    Assertions.assertEquals(0, cardinality(index, false));
+    Assertions.assertEquals(NUM_ROWS, cardinality(index, true));
+  }
+
+  private static int cardinality(BitmapColumnIndex index, boolean 
includeUnknown)
+  {
+    return index.computeBitmapResult(BITMAP_RESULT_FACTORY, 
includeUnknown).size();
+  }
+}


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

Reply via email to