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]