This is an automated email from the ASF dual-hosted git repository.
cwylie 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 64fcb32bcfd add native 'array contains element' filter (#15366)
64fcb32bcfd is described below
commit 64fcb32bcfdf4586012c7ee29e057070aa46aeb6
Author: Clint Wylie <[email protected]>
AuthorDate: Wed Nov 29 03:33:00 2023 -0800
add native 'array contains element' filter (#15366)
* add native arrayContainsElement filter to use array column element indexes
---
.../apache/druid/benchmark/query/SqlBenchmark.java | 19 +-
.../benchmark/query/SqlExpressionBenchmark.java | 56 +-
.../druid/indexing/input/GeneratorInputSource.java | 6 +-
.../indexing/input/GeneratorInputSourceTest.java | 19 +-
.../java/org/apache/druid/math/expr/ExprEval.java | 7 +
.../java/org/apache/druid/math/expr/Function.java | 6 +-
.../query/filter/ArrayContainsElementFilter.java | 530 ++++++++++
.../org/apache/druid/query/filter/DimFilter.java | 3 +-
.../apache/druid/query/filter/DimFilterUtils.java | 2 +-
.../apache/druid/query/filter/EqualityFilter.java | 68 +-
.../druid/segment/generator/DataGenerator.java | 16 +
.../nested/VariantColumnAndIndexSupplier.java | 20 +-
.../serde/NestedCommonFormatColumnPartSerde.java | 1 +
.../org/apache/druid/math/expr/FunctionTest.java | 7 +
.../org/apache/druid/math/expr/OutputTypeTest.java | 5 +
.../filter/ArrayContainsElementFilterTests.java | 732 +++++++++++++
.../druid/segment/filter/BaseFilterTest.java | 1 +
.../druid/segment/filter/EqualityFilterTests.java | 621 ++++++------
.../druid/segment/filter/RangeFilterTests.java | 1070 ++++++++++----------
.../druid/segment/generator/SegmentGenerator.java | 19 +-
.../segment/nested/VariantColumnSupplierTest.java | 4 +-
.../builtin/ArrayContainsOperatorConversion.java | 39 +
.../druid/sql/calcite/CalciteArraysQueryTest.java | 19 +-
.../sql/calcite/CalciteNestedDataQueryTest.java | 18 +-
24 files changed, 2338 insertions(+), 950 deletions(-)
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java
index 9bfe925ccf3..f153a4ff28b 100644
---
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java
@@ -413,7 +413,24 @@ public class SqlBenchmark
"SELECT APPROX_COUNT_DISTINCT_BUILTIN(dimZipf) FROM foo",
"SELECT APPROX_COUNT_DISTINCT_DS_HLL(dimZipf) FROM foo",
"SELECT APPROX_COUNT_DISTINCT_DS_HLL_UTF8(dimZipf) FROM foo",
- "SELECT APPROX_COUNT_DISTINCT_DS_THETA(dimZipf) FROM foo"
+ "SELECT APPROX_COUNT_DISTINCT_DS_THETA(dimZipf) FROM foo",
+ // 32: LATEST aggregator long
+ "SELECT LATEST(long1) FROM foo",
+ // 33: LATEST aggregator double
+ "SELECT LATEST(double4) FROM foo",
+ // 34: LATEST aggregator double
+ "SELECT LATEST(float3) FROM foo",
+ // 35: LATEST aggregator double
+ "SELECT LATEST(float3), LATEST(long1), LATEST(double4) FROM foo",
+ // 36,37: filter numeric nulls
+ "SELECT SUM(long5) FROM foo WHERE long5 IS NOT NULL",
+ "SELECT string2, SUM(long5) FROM foo WHERE long5 IS NOT NULL GROUP BY 1",
+ // 38: EARLIEST aggregator long
+ "SELECT EARLIEST(long1) FROM foo",
+ // 39: EARLIEST aggregator double
+ "SELECT EARLIEST(double4) FROM foo",
+ // 40: EARLIEST aggregator float
+ "SELECT EARLIEST(float3) FROM foo"
);
@Param({"5000000"})
diff --git
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
index dbdd4ea1962..83b3ed531f0 100644
---
a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
+++
b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlExpressionBenchmark.java
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.druid.common.config.NullHandling;
+import org.apache.druid.data.input.impl.DimensionsSpec;
import org.apache.druid.java.util.common.granularity.Granularities;
import org.apache.druid.java.util.common.guava.Sequence;
import org.apache.druid.java.util.common.io.Closer;
@@ -31,10 +32,12 @@ import org.apache.druid.math.expr.ExpressionProcessing;
import org.apache.druid.query.DruidProcessingConfig;
import org.apache.druid.query.QueryContexts;
import org.apache.druid.query.QueryRunnerFactoryConglomerate;
+import org.apache.druid.segment.IndexSpec;
import org.apache.druid.segment.QueryableIndex;
import org.apache.druid.segment.generator.GeneratorBasicSchemas;
import org.apache.druid.segment.generator.GeneratorSchemaInfo;
import org.apache.druid.segment.generator.SegmentGenerator;
+import org.apache.druid.segment.transform.TransformSpec;
import org.apache.druid.server.QueryStackTests;
import org.apache.druid.server.SpecificSegmentsQuerySegmentWalker;
import org.apache.druid.server.security.AuthConfig;
@@ -197,23 +200,8 @@ public class SqlExpressionBenchmark
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long4), 'PT1H', 1), string2,
SUM(long1 * double4) FROM foo GROUP BY 1,2 ORDER BY 3",
// 37: time shift + expr agg (group by), uniform distribution high
cardinality
"SELECT TIME_SHIFT(MILLIS_TO_TIMESTAMP(long5), 'PT1H', 1), string2,
SUM(long1 * double4) FROM foo GROUP BY 1,2 ORDER BY 3",
- // 38: LATEST aggregator long
- "SELECT LATEST(long1) FROM foo",
- // 39: LATEST aggregator double
- "SELECT LATEST(double4) FROM foo",
- // 40: LATEST aggregator double
- "SELECT LATEST(float3) FROM foo",
- // 41: LATEST aggregator double
- "SELECT LATEST(float3), LATEST(long1), LATEST(double4) FROM foo",
- // 42,43: filter numeric nulls
- "SELECT SUM(long5) FROM foo WHERE long5 IS NOT NULL",
- "SELECT string2, SUM(long5) FROM foo WHERE long5 IS NOT NULL GROUP BY 1",
- // 44: EARLIEST aggregator long
- "SELECT EARLIEST(long1) FROM foo",
- // 45: EARLIEST aggregator double
- "SELECT EARLIEST(double4) FROM foo",
- // 46: EARLIEST aggregator float
- "SELECT EARLIEST(float3) FROM foo"
+ // 38: array filtering
+ "SELECT string1, long1 FROM foo WHERE ARRAY_CONTAINS(\"multi-string3\",
100) GROUP BY 1,2"
);
@Param({"5000000"})
@@ -225,6 +213,12 @@ public class SqlExpressionBenchmark
})
private String vectorize;
+ @Param({
+ "explicit",
+ "auto"
+ })
+ private String schema;
+
@Param({
// non-expression reference
"0",
@@ -266,16 +260,7 @@ public class SqlExpressionBenchmark
"35",
"36",
"37",
- "38",
- "39",
- "40",
- "41",
- "42",
- "43",
- "44",
- "45",
- "46",
- "47"
+ "38"
})
private String query;
@@ -300,8 +285,21 @@ public class SqlExpressionBenchmark
final PlannerConfig plannerConfig = new PlannerConfig();
final SegmentGenerator segmentGenerator = closer.register(new
SegmentGenerator());
- log.info("Starting benchmark setup using cacheDir[%s], rows[%,d].",
segmentGenerator.getCacheDir(), rowsPerSegment);
- final QueryableIndex index = segmentGenerator.generate(dataSegment,
schemaInfo, Granularities.NONE, rowsPerSegment);
+ log.info("Starting benchmark setup using cacheDir[%s], rows[%,d],
schema[%s].", segmentGenerator.getCacheDir(), rowsPerSegment, schema);
+ final QueryableIndex index;
+ if ("auto".equals(schema)) {
+ index = segmentGenerator.generate(
+ dataSegment,
+ schemaInfo,
+ DimensionsSpec.builder().useSchemaDiscovery(true).build(),
+ TransformSpec.NONE,
+ IndexSpec.DEFAULT,
+ Granularities.NONE,
+ rowsPerSegment
+ );
+ } else {
+ index = segmentGenerator.generate(dataSegment, schemaInfo,
Granularities.NONE, rowsPerSegment);
+ }
final QueryRunnerFactoryConglomerate conglomerate =
QueryStackTests.createQueryRunnerFactoryConglomerate(
closer,
diff --git
a/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java
b/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java
index 63526d8e502..b5dc706f374 100644
---
a/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java
+++
b/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java
@@ -34,6 +34,7 @@ import org.apache.druid.data.input.InputSplit;
import org.apache.druid.data.input.InputStats;
import org.apache.druid.data.input.MapBasedInputRow;
import org.apache.druid.data.input.SplitHintSpec;
+import org.apache.druid.data.input.impl.MapInputRowParser;
import org.apache.druid.data.input.impl.SplittableInputSource;
import org.apache.druid.guice.IndexingServiceInputSourceModule;
import org.apache.druid.java.util.common.CloseableIterators;
@@ -179,7 +180,10 @@ public class GeneratorInputSource extends
AbstractInputSource implements Splitta
public InputRow next()
{
rowCount++;
- return generator.nextRow();
+ return MapInputRowParser.parse(
+ inputRowSchema,
+
generator.nextRaw(inputRowSchema.getTimestampSpec().getTimestampColumn())
+ );
}
});
}
diff --git
a/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java
b/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java
index cbf63add1c5..3f87dd4c056 100644
---
a/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java
+++
b/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java
@@ -25,8 +25,12 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.apache.druid.data.input.InputRow;
+import org.apache.druid.data.input.InputRowSchema;
import org.apache.druid.data.input.InputSourceReader;
import org.apache.druid.data.input.InputSplit;
+import org.apache.druid.data.input.impl.DimensionsSpec;
+import org.apache.druid.data.input.impl.MapInputRowParser;
+import org.apache.druid.data.input.impl.TimestampSpec;
import org.apache.druid.guice.IndexingServiceInputSourceModule;
import org.apache.druid.jackson.DefaultObjectMapper;
import org.apache.druid.java.util.common.DateTimes;
@@ -128,11 +132,20 @@ public class GeneratorInputSourceTest
timestampIncrement
);
- InputSourceReader reader = inputSource.fixedFormatReader(null, null);
+ InputRowSchema rowSchema = new InputRowSchema(
+ new TimestampSpec(null, null, null),
+ DimensionsSpec.builder().useSchemaDiscovery(true).build(),
+ null
+ );
+
+ InputSourceReader reader = inputSource.fixedFormatReader(
+ rowSchema,
+ null
+ );
CloseableIterator<InputRow> iterator = reader.read();
InputRow first = iterator.next();
- InputRow generatorFirst = generator.nextRow();
+ InputRow generatorFirst = MapInputRowParser.parse(rowSchema,
generator.nextRaw(rowSchema.getTimestampSpec().getTimestampColumn()));
Assert.assertEquals(generatorFirst, first);
Assert.assertTrue(iterator.hasNext());
int i;
@@ -157,7 +170,7 @@ public class GeneratorInputSourceTest
);
Assert.assertEquals(2, inputSource.estimateNumSplits(null, null));
- Assert.assertEquals(false, inputSource.needsFormat());
+ Assert.assertFalse(inputSource.needsFormat());
Assert.assertEquals(2, inputSource.createSplits(null, null).count());
Assert.assertEquals(
new Long(2048L),
diff --git a/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java
b/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java
index af1faf9eaf5..35f59e5ebe7 100644
--- a/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java
+++ b/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java
@@ -653,6 +653,13 @@ public abstract class ExprEval<T>
@Nullable
public static ExprEval<?> castForEqualityComparison(ExprEval<?>
valueToCompare, ExpressionType typeToCompareWith)
{
+ if (valueToCompare.isArray() && !typeToCompareWith.isArray()) {
+ final Object[] array = valueToCompare.asArray();
+ // cannot cast array to scalar if array length is greater than 1
+ if (array != null && array.length > 1) {
+ return null;
+ }
+ }
ExprEval<?> cast = valueToCompare.castTo(typeToCompareWith);
if (ExpressionType.LONG.equals(typeToCompareWith) &&
valueToCompare.asDouble() != cast.asDouble()) {
// make sure the DOUBLE value when cast to LONG is the same before and
after the cast
diff --git a/processing/src/main/java/org/apache/druid/math/expr/Function.java
b/processing/src/main/java/org/apache/druid/math/expr/Function.java
index 3af7aa181da..61bce0b2b20 100644
--- a/processing/src/main/java/org/apache/druid/math/expr/Function.java
+++ b/processing/src/main/java/org/apache/druid/math/expr/Function.java
@@ -3327,11 +3327,11 @@ public interface Function extends NamedFunction
@Override
public ExpressionType getOutputType(Expr.InputBindingInspector inspector,
List<Expr> args)
{
- ExpressionType type = ExpressionType.LONG;
+ ExpressionType type = null;
for (Expr arg : args) {
- type = ExpressionTypeConversion.function(type,
arg.getOutputType(inspector));
+ type = ExpressionTypeConversion.leastRestrictiveType(type,
arg.getOutputType(inspector));
}
- return ExpressionType.asArrayType(type);
+ return type == null ? null :
ExpressionTypeFactory.getInstance().ofArray(type);
}
/**
diff --git
a/processing/src/main/java/org/apache/druid/query/filter/ArrayContainsElementFilter.java
b/processing/src/main/java/org/apache/druid/query/filter/ArrayContainsElementFilter.java
new file mode 100644
index 00000000000..a8a245327cf
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/query/filter/ArrayContainsElementFilter.java
@@ -0,0 +1,530 @@
+/*
+ * 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 com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.RangeSet;
+import org.apache.druid.error.InvalidInput;
+import org.apache.druid.java.util.common.IAE;
+import org.apache.druid.math.expr.ExprEval;
+import org.apache.druid.math.expr.ExpressionType;
+import org.apache.druid.query.cache.CacheKeyBuilder;
+import org.apache.druid.query.filter.vector.VectorValueMatcher;
+import
org.apache.druid.query.filter.vector.VectorValueMatcherColumnProcessorFactory;
+import org.apache.druid.segment.BaseDoubleColumnValueSelector;
+import org.apache.druid.segment.BaseFloatColumnValueSelector;
+import org.apache.druid.segment.BaseLongColumnValueSelector;
+import org.apache.druid.segment.ColumnInspector;
+import org.apache.druid.segment.ColumnProcessors;
+import org.apache.druid.segment.ColumnSelector;
+import org.apache.druid.segment.ColumnSelectorFactory;
+import org.apache.druid.segment.DimensionSelector;
+import org.apache.druid.segment.column.ColumnCapabilities;
+import org.apache.druid.segment.column.ColumnIndexSupplier;
+import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.segment.column.NullableTypeStrategy;
+import org.apache.druid.segment.column.TypeSignature;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.segment.filter.Filters;
+import org.apache.druid.segment.index.AllUnknownBitmapColumnIndex;
+import org.apache.druid.segment.index.BitmapColumnIndex;
+import org.apache.druid.segment.index.semantic.ArrayElementIndexes;
+import org.apache.druid.segment.nested.StructuredData;
+import org.apache.druid.segment.vector.VectorColumnSelectorFactory;
+
+import javax.annotation.Nullable;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Check to see if an array contains a specific element. This filter is not an
exact replica of SQL ARRAY_CONTAINS
+ * or the native array_contains expression, which when given something like
ARRAY_CONTAINS(arrayColumn, ARRAY[1,2,3])
+ * will check that arrayColumn contains all elements of the match value. To
model this functionality, use an
+ * {@link AndDimFilter} with an element filter for each element to match.
+ */
+public class ArrayContainsElementFilter extends AbstractOptimizableDimFilter
implements Filter
+{
+ private final String column;
+ private final ColumnType elementMatchValueType;
+ @Nullable
+ private final Object elementMatchValue;
+ private final ExprEval<?> elementMatchValueEval;
+
+ @Nullable
+ private final FilterTuning filterTuning;
+ private final DruidPredicateFactory predicateFactory;
+
+ @JsonCreator
+ public ArrayContainsElementFilter(
+ @JsonProperty("column") String column,
+ @JsonProperty("elementMatchValueType") ColumnType elementMatchValueType,
+ @JsonProperty("elementMatchValue") @Nullable Object elementMatchValue,
+ @JsonProperty("filterTuning") @Nullable FilterTuning filterTuning
+ )
+ {
+ if (column == null) {
+ throw InvalidInput.exception("Invalid array_contains filter, column
cannot be null");
+ }
+ this.column = column;
+ if (elementMatchValueType == null) {
+ throw InvalidInput.exception("Invalid array_contains filter on column
[%s], elementMatchValueType cannot be null", column);
+ }
+ this.elementMatchValueType = elementMatchValueType;
+ this.elementMatchValue = elementMatchValue;
+ this.elementMatchValueEval =
ExprEval.ofType(ExpressionType.fromColumnTypeStrict(elementMatchValueType),
elementMatchValue);
+ this.filterTuning = filterTuning;
+ this.predicateFactory = new
ArrayContainsPredicateFactory(elementMatchValueEval);
+ }
+
+ @Override
+ public byte[] getCacheKey()
+ {
+ final NullableTypeStrategy<Object> typeStrategy =
elementMatchValueEval.type().getNullableStrategy();
+ final int size =
typeStrategy.estimateSizeBytes(elementMatchValueEval.value());
+ final ByteBuffer valueBuffer = ByteBuffer.allocate(size);
+ typeStrategy.write(valueBuffer, elementMatchValueEval.value(), size);
+ return new CacheKeyBuilder(DimFilterUtils.ARRAY_CONTAINS_CACHE_ID)
+ .appendByte(DimFilterUtils.STRING_SEPARATOR)
+ .appendString(column)
+ .appendByte(DimFilterUtils.STRING_SEPARATOR)
+ .appendString(elementMatchValueType.asTypeString())
+ .appendByte(DimFilterUtils.STRING_SEPARATOR)
+ .appendByteArray(valueBuffer.array())
+ .build();
+ }
+
+ @Override
+ public DimFilter optimize()
+ {
+ return this;
+ }
+
+ @Override
+ public Filter toFilter()
+ {
+ return this;
+ }
+
+ @JsonProperty
+ public String getColumn()
+ {
+ return column;
+ }
+
+ @JsonProperty
+ public ColumnType getElementMatchValueType()
+ {
+ return elementMatchValueType;
+ }
+
+ @JsonProperty
+ public Object getElementMatchValue()
+ {
+ return elementMatchValue;
+ }
+
+ @Nullable
+ @JsonProperty
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public FilterTuning getFilterTuning()
+ {
+ return filterTuning;
+ }
+
+ @Override
+ public String toString()
+ {
+ DimFilter.DimFilterToStringBuilder bob =
+ new
DimFilter.DimFilterToStringBuilder().append("array_contains_element(")
+ .appendDimension(column, null)
+ .append(", ")
+ .append(
+
elementMatchValueType.isArray()
+ ?
Arrays.deepToString(elementMatchValueEval.asArray())
+ :
elementMatchValueEval.value()
+ )
+ .append(")");
+ if (!ColumnType.STRING.equals(elementMatchValueType)) {
+ bob.append(" (" + elementMatchValueType.asTypeString() + ")");
+ }
+ return bob.appendFilterTuning(filterTuning).build();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArrayContainsElementFilter that = (ArrayContainsElementFilter) o;
+ if (!column.equals(that.column)) {
+ return false;
+ }
+ if (!Objects.equals(elementMatchValueType, that.elementMatchValueType)) {
+ return false;
+ }
+ if (!Objects.equals(filterTuning, that.filterTuning)) {
+ return false;
+ }
+ if (elementMatchValueType.isArray()) {
+ return Arrays.deepEquals(elementMatchValueEval.asArray(),
that.elementMatchValueEval.asArray());
+ } else {
+ return Objects.equals(elementMatchValueEval.value(),
that.elementMatchValueEval.value());
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(column, elementMatchValueType,
elementMatchValueEval.value(), filterTuning);
+ }
+
+ @Override
+ public RangeSet<String> getDimensionRangeSet(String dimension)
+ {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public BitmapColumnIndex getBitmapColumnIndex(ColumnIndexSelector selector)
+ {
+ if (!Filters.checkFilterTuningUseIndex(column, selector, filterTuning)) {
+ return null;
+ }
+
+ final ColumnIndexSupplier indexSupplier =
selector.getIndexSupplier(column);
+ if (indexSupplier == null) {
+ return new AllUnknownBitmapColumnIndex(selector);
+ }
+ final ArrayElementIndexes elementIndexes =
indexSupplier.as(ArrayElementIndexes.class);
+ if (elementIndexes != null) {
+ return elementIndexes.containsValue(elementMatchValueEval.value(),
elementMatchValueType);
+ }
+
+ if (elementMatchValueEval.valueOrDefault() != null &&
selector.getColumnCapabilities(column) != null &&
!selector.getColumnCapabilities(column).isArray()) {
+ // column is not an array, behave like a normal equality filter
+ return EqualityFilter.getEqualityIndex(column, elementMatchValueEval,
elementMatchValueType, selector);
+ }
+ // column exists, but has no indexes we can use
+ return null;
+ }
+
+ @Override
+ public ValueMatcher makeMatcher(ColumnSelectorFactory factory)
+ {
+ return ColumnProcessors.makeProcessor(
+ column,
+ new TypedConstantElementValueMatcherFactory(elementMatchValueEval,
predicateFactory),
+ factory
+ );
+ }
+
+ @Override
+ public VectorValueMatcher makeVectorMatcher(VectorColumnSelectorFactory
factory)
+ {
+ final ColumnCapabilities capabilities =
factory.getColumnCapabilities(column);
+
+ if (elementMatchValueEval.valueOrDefault() != null &&
elementMatchValueType.isPrimitive() && (capabilities == null ||
capabilities.isPrimitive())) {
+ return ColumnProcessors.makeVectorProcessor(
+ column,
+ VectorValueMatcherColumnProcessorFactory.instance(),
+ factory
+ ).makeMatcher(elementMatchValueEval.value(), elementMatchValueType);
+ }
+ return ColumnProcessors.makeVectorProcessor(
+ column,
+ VectorValueMatcherColumnProcessorFactory.instance(),
+ factory
+ ).makeMatcher(predicateFactory);
+ }
+
+ @Override
+ public boolean supportsSelectivityEstimation(ColumnSelector columnSelector,
ColumnIndexSelector indexSelector)
+ {
+ return Filters.supportsSelectivityEstimation(this, column, columnSelector,
indexSelector);
+ }
+
+ @Override
+ public boolean canVectorizeMatcher(ColumnInspector inspector)
+ {
+ return true;
+ }
+
+ @Override
+ public Set<String> getRequiredColumns()
+ {
+ return ImmutableSet.of(column);
+ }
+
+ @Override
+ public boolean supportsRequiredColumnRewrite()
+ {
+ return true;
+ }
+
+ @Override
+ public Filter rewriteRequiredColumns(Map<String, String> columnRewrites)
+ {
+ String rewriteDimensionTo = columnRewrites.get(column);
+
+ if (rewriteDimensionTo == null) {
+ throw new IAE(
+ "Received a non-applicable rewrite: %s, filter's dimension: %s",
+ columnRewrites,
+ columnRewrites
+ );
+ }
+
+ return new ArrayContainsElementFilter(
+ rewriteDimensionTo,
+ elementMatchValueType,
+ elementMatchValue,
+ filterTuning
+ );
+ }
+
+ private static class ArrayContainsPredicateFactory implements
DruidPredicateFactory
+ {
+ private final ExprEval<?> elementMatchValue;
+ private final EqualityFilter.EqualityPredicateFactory
equalityPredicateFactory;
+ private final Supplier<Predicate<String>> stringPredicateSupplier;
+ private final Supplier<DruidLongPredicate> longPredicateSupplier;
+ private final Supplier<DruidFloatPredicate> floatPredicateSupplier;
+ private final Supplier<DruidDoublePredicate> doublePredicateSupplier;
+ private final ConcurrentHashMap<TypeSignature<ValueType>,
Predicate<Object[]>> arrayPredicates;
+ private final Supplier<Predicate<Object[]>>
typeDetectingArrayPredicateSupplier;
+ private final Supplier<Predicate<Object>> objectPredicateSupplier;
+
+ public ArrayContainsPredicateFactory(ExprEval<?> elementMatchValue)
+ {
+ this.elementMatchValue = elementMatchValue;
+ this.equalityPredicateFactory = new
EqualityFilter.EqualityPredicateFactory(elementMatchValue);
+ // if element match value is an array, scalar matches can never be true
+ final Object matchVal = elementMatchValue.valueOrDefault();
+ if (matchVal == null || (elementMatchValue.isArray() &&
elementMatchValue.asArray().length > 1)) {
+ this.stringPredicateSupplier = Predicates::alwaysFalse;
+ this.longPredicateSupplier = () -> DruidLongPredicate.ALWAYS_FALSE;
+ this.doublePredicateSupplier = () -> DruidDoublePredicate.ALWAYS_FALSE;
+ this.floatPredicateSupplier = () -> DruidFloatPredicate.ALWAYS_FALSE;
+ } else {
+ this.stringPredicateSupplier =
equalityPredicateFactory::makeStringPredicate;
+ this.longPredicateSupplier =
equalityPredicateFactory::makeLongPredicate;
+ this.doublePredicateSupplier =
equalityPredicateFactory::makeDoublePredicate;
+ this.floatPredicateSupplier =
equalityPredicateFactory::makeFloatPredicate;
+ }
+ this.objectPredicateSupplier = makeObjectPredicateSupplier();
+ this.arrayPredicates = new ConcurrentHashMap<>();
+ this.typeDetectingArrayPredicateSupplier =
makeTypeDetectingArrayPredicate();
+ }
+
+ @Override
+ public Predicate<String> makeStringPredicate()
+ {
+ return stringPredicateSupplier.get();
+ }
+
+ @Override
+ public DruidLongPredicate makeLongPredicate()
+ {
+ return longPredicateSupplier.get();
+ }
+
+ @Override
+ public DruidFloatPredicate makeFloatPredicate()
+ {
+ return floatPredicateSupplier.get();
+ }
+
+ @Override
+ public DruidDoublePredicate makeDoublePredicate()
+ {
+ return doublePredicateSupplier.get();
+ }
+
+ @Override
+ public Predicate<Object[]> makeArrayPredicate(@Nullable
TypeSignature<ValueType> arrayType)
+ {
+ if (arrayType == null) {
+ // fall back to per row detection if input array type is unknown
+ return typeDetectingArrayPredicateSupplier.get();
+ }
+
+ return new FallbackPredicate<>(computeArrayPredicate(arrayType),
ExpressionType.fromColumnTypeStrict(arrayType));
+ }
+
+ @Override
+ public Predicate<Object> makeObjectPredicate()
+ {
+ return objectPredicateSupplier.get();
+ }
+
+ private Supplier<Predicate<Object>> makeObjectPredicateSupplier()
+ {
+ return Suppliers.memoize(() -> input -> {
+ if (input == null) {
+ return false;
+ }
+ final ExprEval<?> inputEval =
ExprEval.bestEffortOf(StructuredData.unwrap(input));
+ final Predicate<Object[]> matcher = new FallbackPredicate<>(
+
computeArrayPredicate(ExpressionType.toColumnType(inputEval.asArrayType())),
+ inputEval.asArrayType()
+ );
+ return matcher.apply(inputEval.asArray());
+ });
+ }
+
+ private Predicate<Object[]> computeArrayPredicate(TypeSignature<ValueType>
arrayType)
+ {
+ return arrayPredicates.computeIfAbsent(arrayType, (existing) ->
makeArrayPredicateInternal(arrayType));
+ }
+
+ private Supplier<Predicate<Object[]>> makeTypeDetectingArrayPredicate()
+ {
+ return Suppliers.memoize(() -> input -> {
+ if (input == null) {
+ return false;
+ }
+ // just use object predicate logic
+ final Predicate<Object> objectPredicate =
objectPredicateSupplier.get();
+ return objectPredicate.apply(input);
+ });
+ }
+
+ private Predicate<Object[]>
makeArrayPredicateInternal(TypeSignature<ValueType> arrayType)
+ {
+ final ExpressionType expressionType =
ExpressionType.fromColumnTypeStrict(arrayType);
+
+ final Comparator elementComparator =
arrayType.getElementType().getNullableStrategy();
+
+ final ExprEval<?> castForComparison = ExprEval.castForEqualityComparison(
+ elementMatchValue,
+ (ExpressionType) expressionType.getElementType()
+ );
+ if (castForComparison == null) {
+ return Predicates.alwaysFalse();
+ }
+ final Object matchVal = castForComparison.value();
+ return input -> {
+ if (input == null) {
+ return false;
+ }
+ boolean anyMatch = false;
+ for (Object elem : input) {
+ anyMatch = anyMatch || elementComparator.compare(elem, matchVal) ==
0;
+ }
+ return anyMatch;
+ };
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArrayContainsPredicateFactory that = (ArrayContainsPredicateFactory) o;
+ if (!Objects.equals(elementMatchValue.type(),
that.elementMatchValue.type())) {
+ return false;
+ }
+ if (elementMatchValue.isArray()) {
+ return Arrays.deepEquals(elementMatchValue.asArray(),
that.elementMatchValue.asArray());
+ }
+ return Objects.equals(elementMatchValue.value(),
that.elementMatchValue.value());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(elementMatchValue);
+ }
+ }
+
+ /**
+ * {@link EqualityFilter.TypedConstantValueMatcherFactory} with special
handling for scalar processors in the case
+ * matchValue is null or an array (which is not possible in equality filter,
but is allowed by this filter).
+ * Uses {@link ArrayContainsPredicateFactory} for the base predicate factory
so that it performs element matching
+ * instead of standard equality matching.
+ */
+ private static class TypedConstantElementValueMatcherFactory extends
EqualityFilter.TypedConstantValueMatcherFactory
+ {
+ public TypedConstantElementValueMatcherFactory(
+ ExprEval<?> matchValue,
+ DruidPredicateFactory predicateFactory
+ )
+ {
+ super(matchValue, predicateFactory);
+ }
+
+ @Override
+ public ValueMatcher makeDimensionProcessor(DimensionSelector selector,
boolean multiValue)
+ {
+ if (matchValue.valueOrDefault() == null || matchValue.isArray()) {
+ return predicateMatcherFactory.makeDimensionProcessor(selector,
multiValue);
+ }
+ return super.makeDimensionProcessor(selector, multiValue);
+ }
+
+ @Override
+ public ValueMatcher makeFloatProcessor(BaseFloatColumnValueSelector
selector)
+ {
+ if (matchValue.valueOrDefault() == null || matchValue.isArray()) {
+ return predicateMatcherFactory.makeFloatProcessor(selector);
+ }
+ return super.makeFloatProcessor(selector);
+ }
+
+ @Override
+ public ValueMatcher makeDoubleProcessor(BaseDoubleColumnValueSelector
selector)
+ {
+ if (matchValue.valueOrDefault() == null || matchValue.isArray()) {
+ return predicateMatcherFactory.makeDoubleProcessor(selector);
+ }
+ return super.makeDoubleProcessor(selector);
+ }
+
+ @Override
+ public ValueMatcher makeLongProcessor(BaseLongColumnValueSelector selector)
+ {
+ if (matchValue.valueOrDefault() == null || matchValue.isArray()) {
+ return predicateMatcherFactory.makeLongProcessor(selector);
+ }
+ return super.makeLongProcessor(selector);
+ }
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/query/filter/DimFilter.java
b/processing/src/main/java/org/apache/druid/query/filter/DimFilter.java
index 95c7b78862b..286c0288f1f 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/DimFilter.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/DimFilter.java
@@ -53,7 +53,8 @@ import java.util.Set;
@JsonSubTypes.Type(name = "equals", value = EqualityFilter.class),
@JsonSubTypes.Type(name = "range", value = RangeFilter.class),
@JsonSubTypes.Type(name = "isfalse", value = IsFalseDimFilter.class),
- @JsonSubTypes.Type(name = "istrue", value = IsTrueDimFilter.class)
+ @JsonSubTypes.Type(name = "istrue", value = IsTrueDimFilter.class),
+ @JsonSubTypes.Type(name = "arrayContainsElement", value =
ArrayContainsElementFilter.class)
})
public interface DimFilter extends Cacheable
{
diff --git
a/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
b/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
index 090ab26d5e2..b60ac9572ca 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/DimFilterUtils.java
@@ -60,8 +60,8 @@ public class DimFilterUtils
static final byte NULL_CACHE_ID = 0x12;
static final byte EQUALS_CACHE_ID = 0x13;
static final byte RANGE_CACHE_ID = 0x14;
-
static final byte IS_FILTER_BOOLEAN_FILTER_CACHE_ID = 0x15;
+ static final byte ARRAY_CONTAINS_CACHE_ID = 0x16;
public static final byte STRING_SEPARATOR = (byte) 0xFF;
diff --git
a/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java
b/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java
index 636f95fecf5..ae7a5eaa134 100644
--- a/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java
+++ b/processing/src/main/java/org/apache/druid/query/filter/EqualityFilter.java
@@ -169,7 +169,11 @@ public class EqualityFilter extends
AbstractOptimizableDimFilter implements Filt
DimFilter.DimFilterToStringBuilder bob =
new DimFilter.DimFilterToStringBuilder().appendDimension(column, null)
.append(" = ")
-
.append(matchValueEval.value());
+ .append(
+ matchValueEval.isArray()
+ ?
Arrays.deepToString(matchValueEval.asArray())
+ : matchValueEval.value()
+ );
if (!ColumnType.STRING.equals(matchValueType)) {
bob.append(" (" + matchValueType.asTypeString() + ")");
@@ -231,28 +235,7 @@ public class EqualityFilter extends
AbstractOptimizableDimFilter implements Filt
if (!Filters.checkFilterTuningUseIndex(column, selector, filterTuning)) {
return null;
}
-
- final ColumnIndexSupplier indexSupplier =
selector.getIndexSupplier(column);
- if (indexSupplier == null) {
- return new AllUnknownBitmapColumnIndex(selector);
- }
-
- final ValueIndexes valueIndexes = indexSupplier.as(ValueIndexes.class);
- if (valueIndexes != null) {
- // matchValueEval.value() cannot be null here due to check in the
constructor
- //noinspection DataFlowIssue
- return valueIndexes.forValue(matchValueEval.value(), matchValueType);
- }
-
- if (matchValueType.isPrimitive()) {
- final StringValueSetIndexes stringValueSetIndexes =
indexSupplier.as(StringValueSetIndexes.class);
- if (stringValueSetIndexes != null) {
-
- return stringValueSetIndexes.forValue(matchValueEval.asString());
- }
- }
- // column exists, but has no indexes we can use
- return null;
+ return getEqualityIndex(column, matchValueEval, matchValueType, selector);
}
@Override
@@ -329,7 +312,37 @@ public class EqualityFilter extends
AbstractOptimizableDimFilter implements Filt
);
}
- private static class EqualityPredicateFactory implements
DruidPredicateFactory
+ public static BitmapColumnIndex getEqualityIndex(
+ String column,
+ ExprEval<?> matchValueEval,
+ ColumnType matchValueType,
+ ColumnIndexSelector selector
+ )
+ {
+ final ColumnIndexSupplier indexSupplier =
selector.getIndexSupplier(column);
+ if (indexSupplier == null) {
+ return new AllUnknownBitmapColumnIndex(selector);
+ }
+
+ final ValueIndexes valueIndexes = indexSupplier.as(ValueIndexes.class);
+ if (valueIndexes != null) {
+ // matchValueEval.value() cannot be null here due to check in the
constructor
+ //noinspection DataFlowIssue
+ return valueIndexes.forValue(matchValueEval.value(), matchValueType);
+ }
+
+ if (matchValueType.isPrimitive()) {
+ final StringValueSetIndexes stringValueSetIndexes =
indexSupplier.as(StringValueSetIndexes.class);
+ if (stringValueSetIndexes != null) {
+
+ return stringValueSetIndexes.forValue(matchValueEval.asString());
+ }
+ }
+ // column exists, but has no indexes we can use
+ return null;
+ }
+
+ public static class EqualityPredicateFactory implements DruidPredicateFactory
{
private final ExprEval<?> matchValue;
private final Supplier<Predicate<String>> stringPredicateSupplier;
@@ -472,6 +485,7 @@ public class EqualityFilter extends
AbstractOptimizableDimFilter implements Filt
return arrayComparator.compare(input, matchArray) == 0;
});
}
+
private Predicate<Object[]>
makeArrayPredicateInternal(TypeSignature<ValueType> arrayType)
{
final ExpressionType expressionType =
ExpressionType.fromColumnTypeStrict(arrayType);
@@ -512,10 +526,10 @@ public class EqualityFilter extends
AbstractOptimizableDimFilter implements Filt
}
}
- private static class TypedConstantValueMatcherFactory implements
ColumnProcessorFactory<ValueMatcher>
+ public static class TypedConstantValueMatcherFactory implements
ColumnProcessorFactory<ValueMatcher>
{
- private final ExprEval<?> matchValue;
- private final PredicateValueMatcherFactory predicateMatcherFactory;
+ protected final ExprEval<?> matchValue;
+ protected final PredicateValueMatcherFactory predicateMatcherFactory;
public TypedConstantValueMatcherFactory(
ExprEval<?> matchValue,
diff --git
a/processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java
b/processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java
index 31578093e77..f078e8dbac5 100644
---
a/processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java
+++
b/processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java
@@ -24,6 +24,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.druid.data.input.InputRow;
import org.apache.druid.data.input.MapBasedInputRow;
+import org.apache.druid.data.input.impl.TimestampSpec;
import org.apache.druid.java.util.common.logger.Logger;
import org.apache.druid.segment.incremental.IncrementalIndex;
import org.apache.druid.segment.incremental.IndexSizeExceededException;
@@ -102,6 +103,21 @@ public class DataGenerator
return new MapBasedInputRow(nextTimestamp(), dimensionNames, event);
}
+ public Map<String, Object> nextRaw()
+ {
+ return nextRaw(TimestampSpec.DEFAULT_COLUMN);
+ }
+
+ public Map<String, Object> nextRaw(String timestampColumn)
+ {
+ Map<String, Object> event = new HashMap<>();
+ for (ColumnValueGenerator generator : columnGenerators) {
+ event.put(generator.getSchema().getName(), generator.generateRowValue());
+ }
+ event.put(timestampColumn, nextTimestamp());
+ return event;
+ }
+
/**
* Reset this generator to start from the begining of the interval with a
new seed.
*
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/VariantColumnAndIndexSupplier.java
b/processing/src/main/java/org/apache/druid/segment/nested/VariantColumnAndIndexSupplier.java
index 15d74e940bd..fcd2e4dca08 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/VariantColumnAndIndexSupplier.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/VariantColumnAndIndexSupplier.java
@@ -384,19 +384,19 @@ public class VariantColumnAndIndexSupplier implements
Supplier<NestedCommonForma
@Override
public <T> T computeBitmapResult(BitmapResultFactory<T>
bitmapResultFactory, boolean includeUnknown)
{
- final int id = dictionary.indexOf(ids) + arrayOffset;
+ final int localId = dictionary.indexOf(ids);
if (includeUnknown) {
- if (id < 0) {
+ if (localId < 0) {
return bitmapResultFactory.wrapDimensionValue(nullValueBitmap);
}
return bitmapResultFactory.unionDimensionValueBitmaps(
- ImmutableList.of(getBitmap(id), nullValueBitmap)
+ ImmutableList.of(getBitmap(localId + arrayOffset),
nullValueBitmap)
);
}
- if (id < 0) {
+ if (localId < 0) {
return
bitmapResultFactory.wrapDimensionValue(bitmapFactory.makeEmptyImmutableBitmap());
}
- return bitmapResultFactory.wrapDimensionValue(getBitmap(id));
+ return bitmapResultFactory.wrapDimensionValue(getBitmap(localId +
arrayOffset));
}
};
}
@@ -404,20 +404,24 @@ public class VariantColumnAndIndexSupplier implements
Supplier<NestedCommonForma
private class VariantArrayElementIndexes implements ArrayElementIndexes
{
-
@Nullable
@Override
public BitmapColumnIndex containsValue(@Nullable Object value,
TypeSignature<ValueType> elementValueType)
{
+ // this column doesn't store nested arrays, bail out if checking if we
contain an array
+ if (elementValueType.isArray()) {
+ return new AllFalseBitmapColumnIndex(bitmapFactory, nullValueBitmap);
+ }
final ExprEval<?> eval =
ExprEval.ofType(ExpressionType.fromColumnTypeStrict(elementValueType), value);
+
final ExprEval<?> castForComparison = ExprEval.castForEqualityComparison(
eval,
- ExpressionType.fromColumnTypeStrict(logicalType.getElementType())
+ ExpressionType.fromColumnTypeStrict(logicalType.isArray() ?
logicalType.getElementType() : logicalType)
);
if (castForComparison == null) {
return new AllFalseBitmapColumnIndex(bitmapFactory, nullValueBitmap);
}
- Indexed elements;
+ final Indexed elements;
final int elementOffset;
switch (logicalType.getElementType().getType()) {
case STRING:
diff --git
a/processing/src/main/java/org/apache/druid/segment/serde/NestedCommonFormatColumnPartSerde.java
b/processing/src/main/java/org/apache/druid/segment/serde/NestedCommonFormatColumnPartSerde.java
index 5e3d74d0d95..8353fd07ceb 100644
---
a/processing/src/main/java/org/apache/druid/segment/serde/NestedCommonFormatColumnPartSerde.java
+++
b/processing/src/main/java/org/apache/druid/segment/serde/NestedCommonFormatColumnPartSerde.java
@@ -277,6 +277,7 @@ public class NestedCommonFormatColumnPartSerde implements
ColumnPartSerde
}
builder.setType(logicalType);
builder.setNestedCommonFormatColumnSupplier(supplier);
+ builder.setIndexSupplier(supplier, true, false);
builder.setColumnFormat(new NestedCommonFormatColumn.Format(
logicalType,
capabilitiesBuilder.hasNulls().isTrue(),
diff --git
a/processing/src/test/java/org/apache/druid/math/expr/FunctionTest.java
b/processing/src/test/java/org/apache/druid/math/expr/FunctionTest.java
index d975ec069d5..b359ec27c27 100644
--- a/processing/src/test/java/org/apache/druid/math/expr/FunctionTest.java
+++ b/processing/src/test/java/org/apache/druid/math/expr/FunctionTest.java
@@ -309,6 +309,13 @@ public class FunctionTest extends
InitializedNullHandlingTest
assertArrayExpr("array(1, 2, 3, 'bar')", new Long[]{1L, 2L, 3L, null});
assertArrayExpr("array(1.0)", new Double[]{1.0});
assertArrayExpr("array('foo', 'bar')", new String[]{"foo", "bar"});
+ assertArrayExpr(
+ "array(a, b)",
+ new Object[]{
+ new Object[]{"foo", "bar", "baz", "foobar"},
+ new Object[]{"1", "2", "3", "4", "5"}
+ }
+ );
}
@Test
diff --git
a/processing/src/test/java/org/apache/druid/math/expr/OutputTypeTest.java
b/processing/src/test/java/org/apache/druid/math/expr/OutputTypeTest.java
index 9d214978f8a..f8d663abc70 100644
--- a/processing/src/test/java/org/apache/druid/math/expr/OutputTypeTest.java
+++ b/processing/src/test/java/org/apache/druid/math/expr/OutputTypeTest.java
@@ -329,6 +329,11 @@ public class OutputTypeTest extends
InitializedNullHandlingTest
{
assertOutputType("array(1, 2, 3)", inspector, ExpressionType.LONG_ARRAY);
assertOutputType("array(1, 2, 3.0)", inspector,
ExpressionType.DOUBLE_ARRAY);
+ assertOutputType(
+ "array(a, b)",
+ inspector,
+
ExpressionTypeFactory.getInstance().ofArray(ExpressionType.STRING_ARRAY)
+ );
assertOutputType("array_length(a)", inspector, ExpressionType.LONG);
assertOutputType("array_length(b)", inspector, ExpressionType.LONG);
diff --git
a/processing/src/test/java/org/apache/druid/segment/filter/ArrayContainsElementFilterTests.java
b/processing/src/test/java/org/apache/druid/segment/filter/ArrayContainsElementFilterTests.java
new file mode 100644
index 00000000000..9c2d2de1bc2
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/segment/filter/ArrayContainsElementFilterTests.java
@@ -0,0 +1,732 @@
+/*
+ * 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.filter;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.apache.druid.common.config.NullHandling;
+import org.apache.druid.error.DruidException;
+import org.apache.druid.guice.NestedDataModule;
+import org.apache.druid.jackson.DefaultObjectMapper;
+import org.apache.druid.java.util.common.Pair;
+import org.apache.druid.query.filter.ArrayContainsElementFilter;
+import org.apache.druid.query.filter.Filter;
+import org.apache.druid.query.filter.FilterTuning;
+import org.apache.druid.query.filter.NotDimFilter;
+import org.apache.druid.segment.IndexBuilder;
+import org.apache.druid.segment.StorageAdapter;
+import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.testing.InitializedNullHandlingTest;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.Closeable;
+import java.util.Arrays;
+
+@RunWith(Enclosed.class)
+public class ArrayContainsElementFilterTests
+{
+ @RunWith(Parameterized.class)
+ public static class ArrayContainsElementFilterTest extends BaseFilterTest
+ {
+ public ArrayContainsElementFilterTest(
+ String testName,
+ IndexBuilder indexBuilder,
+ Function<IndexBuilder, Pair<StorageAdapter, Closeable>> finisher,
+ boolean cnf,
+ boolean optimize
+ )
+ {
+ super(testName, DEFAULT_ROWS, indexBuilder, finisher, cnf, optimize);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception
+ {
+ BaseFilterTest.tearDown(ArrayContainsElementFilterTest.class.getName());
+ }
+
+ @Test
+ public void testArrayStringColumn()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ /*
+ dim0 .. arrayString
+ "0", .. ["a", "b", "c"]
+ "1", .. []
+ "2", .. null
+ "3", .. ["a", "b", "c"]
+ "4", .. ["c", "d"]
+ "5", .. [null]
+ */
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING,
+ "a",
+ null
+ ),
+ ImmutableList.of("0", "3")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING,
+ "a",
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "4", "5")
+ : ImmutableList.of("1", "2", "4", "5")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING,
+ "c",
+ null
+ ),
+ ImmutableList.of("0", "3", "4")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING,
+ "c",
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "5")
+ : ImmutableList.of("1", "2", "5")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING,
+ null,
+ null
+ ),
+ ImmutableList.of("5")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING,
+ null,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "3", "4")
+ : ImmutableList.of("0", "1", "2", "3", "4")
+ );
+ }
+
+ @Test
+ public void testArrayLongColumn()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ /*
+ dim0 .. arrayLong
+ "0", .. [1L, 2L, 3L]
+ "1", .. []
+ "2", .. [1L, 2L, 3L]
+ "3", .. null
+ "4", .. [null]
+ "5", .. [123L, 345L]
+ */
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.LONG,
+ 2L,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.LONG,
+ 2L,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "4", "5")
+ : ImmutableList.of("1", "3", "4", "5")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.LONG,
+ null,
+ null
+ ),
+ ImmutableList.of("4")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.LONG,
+ null,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "2", "5")
+ : ImmutableList.of("0", "1", "2", "3", "5")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.DOUBLE,
+ 2.0,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.STRING,
+ "2",
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ }
+
+ @Test
+ public void testArrayDoubleColumn()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ /*
+ dim0 .. arrayDouble
+ "0", .. [1.1, 2.2, 3.3]
+ "1", .. [1.1, 2.2, 3.3]
+ "2", .. [null]
+ "3", .. []
+ "4", .. [-1.1, -333.3]
+ "5", .. null
+ */
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE,
+ 2.2,
+ null
+ ),
+ ImmutableList.of("0", "1")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE,
+ 2.2,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("2", "3", "4")
+ : ImmutableList.of("2", "3", "4", "5")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayDouble",
+ ColumnType.STRING,
+ "2.2",
+ null
+ ),
+ ImmutableList.of("0", "1")
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE,
+ null,
+ null
+ ),
+ ImmutableList.of("2")
+ );
+ }
+
+ @Test
+ public void testArrayStringColumnContainsArrays()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ // these are not nested arrays, expect no matches
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of("a", "b", "c"),
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of("a", "b", "c"),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "3", "4", "5")
+ : ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
+ }
+
+ @Test
+ public void testArrayLongColumnContainsArrays()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+
+ // these are not nested arrays, expect no matches
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ ImmutableList.of(1L, 2L, 3L),
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ ImmutableList.of(1L, 2L, 3L),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "2", "4", "5")
+ : ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
+ }
+
+ @Test
+ public void testArrayDoubleColumnContainsArrays()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ // these are not nested arrays, expect no matches
+ assertFilterMatches(
+ new ArrayContainsElementFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(1.1, 2.2, 3.3),
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new ArrayContainsElementFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(1.1, 2.2, 3.3),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "2", "3", "4")
+ : ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
+ }
+
+ @Test
+ public void testScalarColumnContains()
+ {
+ assertFilterMatches(
+ new ArrayContainsElementFilter("s0", ColumnType.STRING, "a", null),
+ ImmutableList.of("1", "5")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("s0", ColumnType.STRING, "b", null),
+ ImmutableList.of("2")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("s0", ColumnType.STRING, "c", null),
+ ImmutableList.of("4")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("s0", ColumnType.STRING, "noexist",
null),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("s0", ColumnType.STRING_ARRAY,
ImmutableList.of("c"), null),
+ ImmutableList.of("4")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("s0", ColumnType.STRING_ARRAY,
ImmutableList.of("a", "c"), null),
+ ImmutableList.of()
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter("d0", ColumnType.DOUBLE, 10.1, null),
+ ImmutableList.of("1")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("d0", ColumnType.DOUBLE, 120.0245,
null),
+ ImmutableList.of("3")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("d0", ColumnType.DOUBLE, 765.432,
null),
+ ImmutableList.of("5")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("d0", ColumnType.DOUBLE, 765.431,
null),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("d0", ColumnType.DOUBLE_ARRAY, new
Object[]{10.1}, null),
+ ImmutableList.of("1")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("d0", ColumnType.DOUBLE_ARRAY, new
Object[]{10.1, 120.0245}, null),
+ ImmutableList.of()
+ );
+
+ assertFilterMatches(
+ new ArrayContainsElementFilter("l0", ColumnType.LONG, 100L, null),
+ ImmutableList.of("1")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("l0", ColumnType.LONG, 40L, null),
+ ImmutableList.of("2")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("l0", ColumnType.LONG, 9001L, null),
+ ImmutableList.of("4")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("l0", ColumnType.LONG, 9000L, null),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("l0", ColumnType.LONG_ARRAY,
ImmutableList.of(9001L), null),
+ ImmutableList.of("4")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("l0", ColumnType.LONG_ARRAY,
ImmutableList.of(40L, 9001L), null),
+ ImmutableList.of()
+ );
+ }
+
+ @Test
+ public void testArrayContainsNestedArray()
+ {
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ assertFilterMatchesSkipVectorize(
+ new ArrayContainsElementFilter("nestedArrayLong",
ColumnType.LONG_ARRAY, new Object[]{1L, 2L, 3L}, null),
+ ImmutableList.of("0", "2")
+ );
+
+ assertFilterMatchesSkipVectorize(
+ new ArrayContainsElementFilter("nestedArrayLong",
ColumnType.LONG_ARRAY, new Object[]{1L, 2L}, null),
+ ImmutableList.of()
+ );
+ }
+
+ @Test
+ public void testArrayContainsMvd()
+ {
+ assertFilterMatches(
+ new ArrayContainsElementFilter("dim2", ColumnType.STRING, "a", null),
+ ImmutableList.of("0", "3")
+ );
+ if (isAutoSchema()) {
+ assertFilterMatches(
+ NotDimFilter.of(new ArrayContainsElementFilter("dim2",
ColumnType.STRING, "a", null)),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "2", "4")
+ : ImmutableList.of("1", "2", "4", "5")
+ );
+ // [""] becomes [null] in default value mode
+ assertFilterMatches(
+ new ArrayContainsElementFilter("dim2", ColumnType.STRING, null,
null),
+ NullHandling.sqlCompatible() ? ImmutableList.of() :
ImmutableList.of("2")
+ );
+ } else {
+ // multi-value dimension treats [] as null, so in sql compatible mode
row 1 ends up as not matching
+ assertFilterMatches(
+ NotDimFilter.of(new ArrayContainsElementFilter("dim2",
ColumnType.STRING, "a", null)),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("2", "4")
+ : ImmutableList.of("1", "2", "4", "5")
+ );
+ assertFilterMatches(
+ new ArrayContainsElementFilter("dim2", ColumnType.STRING, null,
null),
+ ImmutableList.of()
+ );
+ }
+
+
+ }
+ }
+
+ public static class ArrayContainsElementFilterNonParameterizedTests extends
InitializedNullHandlingTest
+ {
+ @Test
+ public void testSerde() throws JsonProcessingException
+ {
+ ObjectMapper mapper = new DefaultObjectMapper();
+ ArrayContainsElementFilter filter = new ArrayContainsElementFilter("x",
ColumnType.STRING, "hello", null);
+ String s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.LONG, 1L, null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.LONG, 1, null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.DOUBLE, 111.111,
null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.FLOAT, 1234.0f,
null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.STRING_ARRAY,
new Object[]{"a", "b", null, "c"}, null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.STRING_ARRAY,
Arrays.asList("a", "b", null, "c"), null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.LONG_ARRAY, new
Object[]{1L, null, 2L, 3L}, null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.LONG_ARRAY,
Arrays.asList(1L, null, 2L, 3L), null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.DOUBLE_ARRAY,
new Object[]{1.1, 2.1, null, 3.1}, null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter("x", ColumnType.DOUBLE_ARRAY,
Arrays.asList(1.1, 2.1, null, 3.1), null);
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+
+ filter = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.NESTED_DATA,
+ ImmutableMap.of("x", ImmutableList.of(1, 2, 3)),
+ null
+ );
+ s = mapper.writeValueAsString(filter);
+ Assert.assertEquals(filter, mapper.readValue(s,
ArrayContainsElementFilter.class));
+ }
+
+ @Test
+ public void testRewrite()
+ {
+ ArrayContainsElementFilter filter = new ArrayContainsElementFilter("x",
ColumnType.STRING, "hello", null);
+ Filter rewrite = filter.rewriteRequiredColumns(ImmutableMap.of("x",
"y"));
+ ArrayContainsElementFilter expected = new
ArrayContainsElementFilter("y", ColumnType.STRING, "hello", null);
+ Assert.assertEquals(expected, rewrite);
+ }
+
+ @Test
+ public void testGetCacheKey()
+ {
+ ArrayContainsElementFilter f1 = new ArrayContainsElementFilter("x",
ColumnType.STRING, "hello", null);
+ ArrayContainsElementFilter f1_2 = new ArrayContainsElementFilter("x",
ColumnType.STRING, "hello", null);
+ ArrayContainsElementFilter f2 = new ArrayContainsElementFilter("x",
ColumnType.STRING, "world", null);
+ ArrayContainsElementFilter f3 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.STRING,
+ "hello",
+ new FilterTuning(true, null, null)
+ );
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ f1 = new ArrayContainsElementFilter("x", ColumnType.LONG, 1L, null);
+ f1_2 = new ArrayContainsElementFilter("x", ColumnType.LONG, 1, null);
+ f2 = new ArrayContainsElementFilter("x", ColumnType.LONG, 2L, null);
+ f3 = new ArrayContainsElementFilter("x", ColumnType.LONG, 1L, new
FilterTuning(true, null, null));
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ f1 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE, 1.1, null);
+ f1_2 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE, 1.1, null);
+ f2 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE, 2.2, null);
+ f3 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE, 1.1, new
FilterTuning(true, null, null));
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ f1 = new ArrayContainsElementFilter("x", ColumnType.FLOAT, 1.1f, null);
+ f1_2 = new ArrayContainsElementFilter("x", ColumnType.FLOAT, 1.1f, null);
+ f2 = new ArrayContainsElementFilter("x", ColumnType.FLOAT, 2.2f, null);
+ f3 = new ArrayContainsElementFilter("x", ColumnType.FLOAT, 1.1f, new
FilterTuning(true, null, null));
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ f1 = new ArrayContainsElementFilter("x", ColumnType.STRING_ARRAY, new
Object[]{"a", "b", null, "c"}, null);
+ f1_2 = new ArrayContainsElementFilter("x", ColumnType.STRING_ARRAY,
Arrays.asList("a", "b", null, "c"), null);
+ f2 = new ArrayContainsElementFilter("x", ColumnType.STRING_ARRAY, new
Object[]{"a", "b", "c"}, null);
+ f3 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"a", "b", null, "c"},
+ new FilterTuning(true, null, null)
+ );
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ f1 = new ArrayContainsElementFilter("x", ColumnType.LONG_ARRAY, new
Object[]{100L, 200L, null, 300L}, null);
+ f1_2 = new ArrayContainsElementFilter("x", ColumnType.LONG_ARRAY,
Arrays.asList(100L, 200L, null, 300L), null);
+ f2 = new ArrayContainsElementFilter("x", ColumnType.LONG_ARRAY, new
Object[]{100L, null, 200L, 300L}, null);
+ f3 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.LONG_ARRAY,
+ new Object[]{100L, 200L, null, 300L},
+ new FilterTuning(true, null, null)
+ );
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ f1 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE_ARRAY, new
Object[]{1.001, null, 20.0002, 300.0003}, null);
+ f1_2 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE_ARRAY,
Arrays.asList(1.001, null, 20.0002, 300.0003), null);
+ f2 = new ArrayContainsElementFilter("x", ColumnType.DOUBLE_ARRAY, new
Object[]{1.001, 20.0002, 300.0003, null}, null);
+ f3 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.001, null, 20.0002, 300.0003},
+ new FilterTuning(true, null, null)
+ );
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+
+ NestedDataModule.registerHandlersAndSerde();
+ f1 = new ArrayContainsElementFilter("x", ColumnType.NESTED_DATA,
ImmutableMap.of("x", ImmutableList.of(1, 2, 3)), null);
+ f1_2 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.NESTED_DATA,
+ ImmutableMap.of("x", ImmutableList.of(1, 2, 3)),
+ null
+ );
+ f2 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.NESTED_DATA,
+ ImmutableMap.of("x", ImmutableList.of(1, 2, 3, 4)),
+ null
+ );
+ f3 = new ArrayContainsElementFilter(
+ "x",
+ ColumnType.NESTED_DATA,
+ ImmutableMap.of("x", ImmutableList.of(1, 2, 3)),
+ new FilterTuning(true, null, null)
+ );
+ Assert.assertArrayEquals(f1.getCacheKey(), f1_2.getCacheKey());
+ Assert.assertFalse(Arrays.equals(f1.getCacheKey(), f2.getCacheKey()));
+ Assert.assertArrayEquals(f1.getCacheKey(), f3.getCacheKey());
+ }
+
+ @Test
+ public void testInvalidParameters()
+ {
+ Throwable t = Assert.assertThrows(
+ DruidException.class,
+ () -> new ArrayContainsElementFilter(null, ColumnType.STRING, null,
null)
+ );
+ Assert.assertEquals("Invalid array_contains filter, column cannot be
null", t.getMessage());
+ t = Assert.assertThrows(
+ DruidException.class,
+ () -> new ArrayContainsElementFilter("dim0", null, null, null)
+ );
+ Assert.assertEquals(
+ "Invalid array_contains filter on column [dim0],
elementMatchValueType cannot be null",
+ t.getMessage()
+ );
+ }
+
+
+ @Test
+ public void test_equals()
+ {
+ EqualsVerifier.forClass(ArrayContainsElementFilter.class).usingGetClass()
+ .withNonnullFields(
+ "column",
+ "elementMatchValueType",
+ "elementMatchValueEval",
+ "elementMatchValue",
+ "predicateFactory",
+ "cachedOptimizedFilter"
+ )
+ .withPrefabValues(ColumnType.class, ColumnType.STRING,
ColumnType.DOUBLE)
+ .withIgnoredFields("predicateFactory",
"cachedOptimizedFilter", "elementMatchValue")
+ .verify();
+ }
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
b/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
index a4a45c2355d..69b6cefde48 100644
---
a/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
+++
b/processing/src/test/java/org/apache/druid/segment/filter/BaseFilterTest.java
@@ -141,6 +141,7 @@ public abstract class BaseFilterTest extends
InitializedNullHandlingTest
new ExpressionVirtualColumn("vd0", "d0", ColumnType.DOUBLE,
TestExprMacroTable.INSTANCE),
new ExpressionVirtualColumn("vf0", "f0", ColumnType.FLOAT,
TestExprMacroTable.INSTANCE),
new ExpressionVirtualColumn("vl0", "l0", ColumnType.LONG,
TestExprMacroTable.INSTANCE),
+ new ExpressionVirtualColumn("nestedArrayLong", "array(arrayLong)",
ColumnType.ofArray(ColumnType.LONG_ARRAY), TestExprMacroTable.INSTANCE),
new ListFilteredVirtualColumn("allow-dim0",
DefaultDimensionSpec.of("dim0"), ImmutableSet.of("3", "4"), true),
new ListFilteredVirtualColumn("deny-dim0",
DefaultDimensionSpec.of("dim0"), ImmutableSet.of("3", "4"), false),
new ListFilteredVirtualColumn("allow-dim2",
DefaultDimensionSpec.of("dim2"), ImmutableSet.of("a"), true),
diff --git
a/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
b/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
index 06ca5ab3fd4..5a4431e0056 100644
---
a/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
+++
b/processing/src/test/java/org/apache/druid/segment/filter/EqualityFilterTests.java
@@ -46,6 +46,7 @@ import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.testing.InitializedNullHandlingTest;
import org.junit.AfterClass;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
@@ -722,265 +723,264 @@ public class EqualityFilterTests
@Test
public void testArrays()
{
- if (isAutoSchema()) {
- // only auto schema supports array columns... skip other segment types
- /*
- dim0 .. arrayString arrayLong arrayDouble
- "0", .. ["a", "b", "c"], [1L, 2L, 3L], [1.1, 2.2,
3.3]
- "1", .. [], [], [1.1, 2.2,
3.3]
- "2", .. null, [1L, 2L, 3L], [null]
- "3", .. ["a", "b", "c"], null, []
- "4", .. ["c", "d"], [null], [-1.1,
-333.3]
- "5", .. [null], [123L, 345L], null
- */
+ // only auto schema supports array columns... skip other segment types
+ Assume.assumeTrue(isAutoSchema());
+ /*
+ dim0 .. arrayString arrayLong arrayDouble
+ "0", .. ["a", "b", "c"], [1L, 2L, 3L], [1.1, 2.2,
3.3]
+ "1", .. [], [], [1.1, 2.2,
3.3]
+ "2", .. null, [1L, 2L, 3L], [null]
+ "3", .. ["a", "b", "c"], null, []
+ "4", .. ["c", "d"], [null], [-1.1,
-333.3]
+ "5", .. [null], [123L, 345L], null
+ */
- assertFilterMatches(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- ImmutableList.of("a", "b", "c"),
- null
- ),
- ImmutableList.of("0", "3")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- ImmutableList.of("a", "b", "c"),
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("1", "4", "5")
- : ImmutableList.of("1", "2", "4", "5")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{"a", "b", "c"},
- null
- ),
- ImmutableList.of("0", "3")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- ImmutableList.of(),
- null
- ),
- ImmutableList.of("1")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{null},
- null
- ),
- ImmutableList.of("5")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{null, null},
- null
- ),
- ImmutableList.of()
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{null, null},
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("0", "1", "3", "4", "5")
- : ImmutableList.of("0", "1", "2", "3", "4", "5")
- );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of("a", "b", "c"),
+ null
+ ),
+ ImmutableList.of("0", "3")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of("a", "b", "c"),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "4", "5")
+ : ImmutableList.of("1", "2", "4", "5")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"a", "b", "c"},
+ null
+ ),
+ ImmutableList.of("0", "3")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of(),
+ null
+ ),
+ ImmutableList.of("1")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{null},
+ null
+ ),
+ ImmutableList.of("5")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{null, null},
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{null, null},
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "3", "4", "5")
+ : ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- ImmutableList.of(1L, 2L, 3L),
- null
- ),
- ImmutableList.of("0", "2")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- ImmutableList.of(1L, 2L, 3L),
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("1", "4", "5")
- : ImmutableList.of("1", "3", "4", "5")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{1L, 2L, 3L},
- null
- ),
- ImmutableList.of("0", "2")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- ImmutableList.of(),
- null
- ),
- ImmutableList.of("1")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{null},
- null
- ),
- ImmutableList.of("4")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{null, null},
- null
- ),
- ImmutableList.of()
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{null, null},
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("0", "1", "2", "4", "5")
- : ImmutableList.of("0", "1", "2", "3", "4", "5")
- );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ ImmutableList.of(1L, 2L, 3L),
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ ImmutableList.of(1L, 2L, 3L),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "4", "5")
+ : ImmutableList.of("1", "3", "4", "5")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{1L, 2L, 3L},
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ ImmutableList.of(),
+ null
+ ),
+ ImmutableList.of("1")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{null},
+ null
+ ),
+ ImmutableList.of("4")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{null, null},
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{null, null},
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "2", "4", "5")
+ : ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
- // test loss of precision matching long arrays with double array match
values
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.0, 2.0, 3.0},
- null
- ),
- ImmutableList.of("0", "2")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.1, 2.2, 3.3},
- null
- ),
- ImmutableList.of()
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{null},
- null
- ),
- ImmutableList.of("4")
- );
+ // test loss of precision matching long arrays with double array match
values
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.0, 2.0, 3.0},
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.1, 2.2, 3.3},
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{null},
+ null
+ ),
+ ImmutableList.of("4")
+ );
- assertFilterMatches(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- ImmutableList.of(1.1, 2.2, 3.3),
- null
- ),
- ImmutableList.of("0", "1")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- ImmutableList.of(1.1, 2.2, 3.3),
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("2", "3", "4")
- : ImmutableList.of("2", "3", "4", "5")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.1, 2.2, 3.3},
- null
- ),
- ImmutableList.of("0", "1")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- ImmutableList.of(),
- null
- ),
- ImmutableList.of("3")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{null},
- null
- ),
- ImmutableList.of("2")
- );
- assertFilterMatches(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- ImmutableList.of(1.1, 2.2, 3.4),
- null
- ),
- ImmutableList.of()
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- ImmutableList.of(1.1, 2.2, 3.4),
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("0", "1", "2", "3", "4")
- : ImmutableList.of("0", "1", "2", "3", "4", "5")
- );
- }
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(1.1, 2.2, 3.3),
+ null
+ ),
+ ImmutableList.of("0", "1")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(1.1, 2.2, 3.3),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("2", "3", "4")
+ : ImmutableList.of("2", "3", "4", "5")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.1, 2.2, 3.3},
+ null
+ ),
+ ImmutableList.of("0", "1")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(),
+ null
+ ),
+ ImmutableList.of("3")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{null},
+ null
+ ),
+ ImmutableList.of("2")
+ );
+ assertFilterMatches(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(1.1, 2.2, 3.4),
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ ImmutableList.of(1.1, 2.2, 3.4),
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "2", "3", "4")
+ : ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
}
@Test
@@ -996,68 +996,67 @@ public class EqualityFilterTests
"5", .. [100, 200, 300]
*/
- if (isAutoSchema()) {
- assertFilterMatches(
- new EqualityFilter(
- "variant",
- ColumnType.STRING_ARRAY,
- ImmutableList.of("a", "b", "c"),
- null
- ),
- ImmutableList.of()
- );
- assertFilterMatches(
- NotDimFilter.of(
- new EqualityFilter(
- "variant",
- ColumnType.STRING_ARRAY,
- ImmutableList.of("a", "b", "c"),
- null
- )
- ),
- ImmutableList.of("0", "1", "2", "3", "4", "5")
- );
+ Assume.assumeTrue(isAutoSchema());
+ assertFilterMatches(
+ new EqualityFilter(
+ "variant",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of("a", "b", "c"),
+ null
+ ),
+ ImmutableList.of()
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new EqualityFilter(
+ "variant",
+ ColumnType.STRING_ARRAY,
+ ImmutableList.of("a", "b", "c"),
+ null
+ )
+ ),
+ ImmutableList.of("0", "1", "2", "3", "4", "5")
+ );
- assertFilterMatches(
- new EqualityFilter(
- "variant",
- ColumnType.STRING,
- "abc",
- null
- ),
- ImmutableList.of("0")
- );
+ assertFilterMatches(
+ new EqualityFilter(
+ "variant",
+ ColumnType.STRING,
+ "abc",
+ null
+ ),
+ ImmutableList.of("0")
+ );
- assertFilterMatches(
- new EqualityFilter(
- "variant",
- ColumnType.LONG,
- 100L,
- null
- ),
- ImmutableList.of("1", "2")
- );
+ assertFilterMatches(
+ new EqualityFilter(
+ "variant",
+ ColumnType.LONG,
+ 100L,
+ null
+ ),
+ ImmutableList.of("1", "2")
+ );
- assertFilterMatches(
- new EqualityFilter(
- "variant",
- ColumnType.STRING,
- "100",
- null
- ),
- ImmutableList.of("1", "2")
- );
+ assertFilterMatches(
+ new EqualityFilter(
+ "variant",
+ ColumnType.STRING,
+ "100",
+ null
+ ),
+ ImmutableList.of("1", "2")
+ );
- assertFilterMatches(
- new EqualityFilter(
- "variant",
- ColumnType.LONG_ARRAY,
- Arrays.asList(100, 200, 300),
- null
- ),
- ImmutableList.of("5")
- );
- }
+ assertFilterMatches(
+ new EqualityFilter(
+ "variant",
+ ColumnType.LONG_ARRAY,
+ Arrays.asList(100, 200, 300),
+ null
+ ),
+ ImmutableList.of("5")
+ );
}
}
diff --git
a/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
b/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
index 132d6af64db..1f7759397c5 100644
---
a/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
+++
b/processing/src/test/java/org/apache/druid/segment/filter/RangeFilterTests.java
@@ -47,6 +47,7 @@ import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.testing.InitializedNullHandlingTest;
import org.junit.AfterClass;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
@@ -946,10 +947,8 @@ public class RangeFilterTests
ImmutableList.of("0", "1", "2", "5", "6")
);
- if (isAutoSchema()) {
- // bail out, auto ingests arrays instead of mvds and this virtual
column is for mvd stuff
- return;
- }
+ // bail out, auto ingests arrays instead of mvds and this virtual column
is for mvd stuff
+ Assume.assumeFalse(isAutoSchema());
assertFilterMatchesSkipVectorize(
new RangeFilter("allow-dim2", ColumnType.STRING, "a", "c", false,
false, null),
@@ -983,9 +982,9 @@ public class RangeFilterTests
@Test
public void testArrayRanges()
{
- if (isAutoSchema()) {
- // only auto schema supports array columns currently, this means the
match value will need to be coerceable to
- // the column value type...
+ // only auto schema supports array columns currently, this means the
match value will need to be coerceable to
+ // the column value type...
+ Assume.assumeTrue(isAutoSchema());
/* dim0 .. arrayString arrayLong arrayDouble
"0", .. ["a", "b", "c"], [1L, 2L, 3L], [1.1, 2.2,
3.3]
@@ -997,349 +996,348 @@ public class RangeFilterTests
"6", .. ["x", "y"], [100, 200], [1.1, null,
3.3]
"7", .. [null, "hello", "world"], [1234, 3456L, null], [1.23, 4.56,
6.78]
*/
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{"a", "b", "c"},
- new Object[]{"a", "b", "c"},
- false,
- false,
- null
- ),
- ImmutableList.of("0", "3")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{"a", "b", "c"},
- new Object[]{"a", "b", "c"},
- false,
- false,
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("1", "4", "5", "6", "7")
- : ImmutableList.of("1", "2", "4", "5", "6", "7")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- null,
- new Object[]{"a", "b", "c"},
- false,
- false,
- null
- ),
- ImmutableList.of("0", "1", "3", "5", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"a", "b", "c"},
+ new Object[]{"a", "b", "c"},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "3")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"a", "b", "c"},
+ new Object[]{"a", "b", "c"},
+ false,
+ false,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("1", "4", "5", "6", "7")
+ : ImmutableList.of("1", "2", "4", "5", "6", "7")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ null,
+ new Object[]{"a", "b", "c"},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "1", "3", "5", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{"a", "b", "c"},
- null,
- true,
- false,
- null
- ),
- ImmutableList.of("4", "6")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"a", "b", "c"},
+ null,
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("4", "6")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- null,
- new Object[]{"a", "b", "c"},
- false,
- true,
- null
- ),
- ImmutableList.of("1", "5", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ null,
+ new Object[]{"a", "b", "c"},
+ false,
+ true,
+ null
+ ),
+ ImmutableList.of("1", "5", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{"a", "b"},
- new Object[]{"a", "b", "c", "d"},
- true,
- true,
- null
- ),
- ImmutableList.of("0", "3")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"a", "b"},
+ new Object[]{"a", "b", "c", "d"},
+ true,
+ true,
+ null
+ ),
+ ImmutableList.of("0", "3")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- new Object[]{"c", "d"},
- new Object[]{"c", "d", "e"},
- false,
- true,
- null
- ),
- ImmutableList.of("4")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ new Object[]{"c", "d"},
+ new Object[]{"c", "d", "e"},
+ false,
+ true,
+ null
+ ),
+ ImmutableList.of("4")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- null,
- new Object[]{},
- false,
- false,
- null
- ),
- ImmutableList.of("1")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ null,
+ new Object[]{},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayString",
- ColumnType.STRING_ARRAY,
- null,
- new Object[]{null},
- false,
- false,
- null
- ),
- ImmutableList.of("1", "5")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayString",
+ ColumnType.STRING_ARRAY,
+ null,
+ new Object[]{null},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1", "5")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- null,
- new Object[]{},
- false,
- false,
- null
- ),
- ImmutableList.of("1")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- null,
- new Object[]{},
- false,
- false,
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("0", "2", "4", "5", "6", "7")
- : ImmutableList.of("0", "2", "3", "4", "5", "6", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ null,
+ new Object[]{},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ null,
+ new Object[]{},
+ false,
+ false,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "2", "4", "5", "6", "7")
+ : ImmutableList.of("0", "2", "3", "4", "5", "6", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{},
- null,
- true,
- false,
- null
- ),
- ImmutableList.of("0", "2", "4", "5", "6", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{},
+ null,
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2", "4", "5", "6", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- null,
- new Object[]{null},
- false,
- false,
- null
- ),
- ImmutableList.of("1", "4")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ null,
+ new Object[]{null},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1", "4")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{1L, 2L, 3L},
- new Object[]{1L, 2L, 3L},
- false,
- false,
- null
- ),
- ImmutableList.of("0", "2")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{1L, 2L, 3L},
+ new Object[]{1L, 2L, 3L},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- null,
- new Object[]{1L, 2L, 3L},
- false,
- true,
- null
- ),
- ImmutableList.of("1", "4")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ null,
+ new Object[]{1L, 2L, 3L},
+ false,
+ true,
+ null
+ ),
+ ImmutableList.of("1", "4")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- new Object[]{1L, 2L, 3L},
- null,
- true,
- false,
- null
- ),
- ImmutableList.of("5", "6", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ new Object[]{1L, 2L, 3L},
+ null,
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("5", "6", "7")
+ );
- // empties and nulls still sort before numbers
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.LONG_ARRAY,
- null,
- new Object[]{-1L},
- false,
- false,
- null
- ),
- ImmutableList.of("1", "4")
- );
+ // empties and nulls still sort before numbers
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.LONG_ARRAY,
+ null,
+ new Object[]{-1L},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1", "4")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{},
- false,
- false,
- null
- ),
- ImmutableList.of("3")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{},
- false,
- false,
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("0", "1", "2", "4", "6", "7")
- : ImmutableList.of("0", "1", "2", "4", "5", "6", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("3")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{},
+ false,
+ false,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "1", "2", "4", "6", "7")
+ : ImmutableList.of("0", "1", "2", "4", "5", "6", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{},
- null,
- true,
- false,
- null
- ),
- ImmutableList.of("0", "1", "2", "4", "6", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{},
+ null,
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "1", "2", "4", "6", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{null},
- false,
- false,
- null
- ),
- ImmutableList.of("2", "3")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{null},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("2", "3")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.1, 2.2, 3.3},
- new Object[]{1.1, 2.2, 3.3},
- false,
- false,
- null
- ),
- ImmutableList.of("0", "1")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.1, 2.2, 3.3},
- null,
- true,
- false,
- null
- ),
- ImmutableList.of("7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.1, 2.2, 3.3},
+ new Object[]{1.1, 2.2, 3.3},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "1")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.1, 2.2, 3.3},
+ null,
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{1.1, 2.2, 3.3},
- true,
- false,
- null
- ),
- ImmutableList.of("0", "1", "2", "3", "4", "6")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{1.1, 2.2, 3.3},
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "1", "2", "3", "4", "6")
+ );
- // empties and nulls sort before numbers
- assertFilterMatches(
- new RangeFilter(
- "arrayDouble",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{0.0},
- true,
- false,
- null
- ),
- ImmutableList.of("2", "3", "4")
- );
- }
+ // empties and nulls sort before numbers
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayDouble",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{0.0},
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("2", "3", "4")
+ );
}
@Test
public void testArrayRangesPrecisionLoss()
{
- if (isAutoSchema()) {
- // only auto schema supports array columns currently, this means the
match value will need to be coerceable to
- // the column value type...
+ // only auto schema supports array columns currently, this means the
match value will need to be coerceable to
+ // the column value type...
+ Assume.assumeTrue(isAutoSchema());
/* dim0 .. arrayLong
"0", .. [1L, 2L, 3L],
@@ -1352,165 +1350,164 @@ public class RangeFilterTests
"7", .. [1234, 3456L, null]
*/
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{1.0, 2.0, 3.0},
- true,
- false,
- null
- ),
- ImmutableList.of("0", "1", "2", "4")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{1.0, 2.0, 3.0},
- true,
- true,
- null
- ),
- ImmutableList.of("1", "4")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- null,
- new Object[]{1.1, 2.1, 3.1},
- true,
- true,
- null
- ),
- ImmutableList.of("0", "1", "2", "4")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{1.0, 2.0, 3.0},
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "1", "2", "4")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{1.0, 2.0, 3.0},
+ true,
+ true,
+ null
+ ),
+ ImmutableList.of("1", "4")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ null,
+ new Object[]{1.1, 2.1, 3.1},
+ true,
+ true,
+ null
+ ),
+ ImmutableList.of("0", "1", "2", "4")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.0, 2.0, 3.0},
- null,
- false,
- false,
- null
- ),
- ImmutableList.of("0", "2", "5", "6", "7")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{0.8, 1.8, 2.8},
- null,
- false,
- false,
- null
- ),
- ImmutableList.of("0", "2", "5", "6", "7")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{0.8, 1.8, 2.8},
- null,
- true,
- false,
- null
- ),
- ImmutableList.of("0", "2", "5", "6", "7")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.0, 2.0, 3.0},
- null,
- true,
- true,
- null
- ),
- ImmutableList.of("5", "6", "7")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.1, 2.1, 3.1},
- null,
- false,
- true,
- null
- ),
- ImmutableList.of("5", "6", "7")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{1.1, 2.1, 3.1},
- null,
- true,
- true,
- null
- ),
- ImmutableList.of("5", "6", "7")
- );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.0, 2.0, 3.0},
+ null,
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2", "5", "6", "7")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{0.8, 1.8, 2.8},
+ null,
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2", "5", "6", "7")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{0.8, 1.8, 2.8},
+ null,
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2", "5", "6", "7")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.0, 2.0, 3.0},
+ null,
+ true,
+ true,
+ null
+ ),
+ ImmutableList.of("5", "6", "7")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.1, 2.1, 3.1},
+ null,
+ false,
+ true,
+ null
+ ),
+ ImmutableList.of("5", "6", "7")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{1.1, 2.1, 3.1},
+ null,
+ true,
+ true,
+ null
+ ),
+ ImmutableList.of("5", "6", "7")
+ );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{0.8, 1.8, 2.8},
- new Object[]{1.1, 2.1, 3.1},
- true,
- true,
- null
- ),
- ImmutableList.of("0", "2")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{0.8, 1.8, 2.8},
- new Object[]{1.1, 2.1, 3.1},
- false,
- true,
- null
- ),
- ImmutableList.of("0", "2")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{0.8, 1.8, 2.8},
- new Object[]{1.1, 2.1, 3.1},
- true,
- false,
- null
- ),
- ImmutableList.of("0", "2")
- );
- assertFilterMatches(
- new RangeFilter(
- "arrayLong",
- ColumnType.DOUBLE_ARRAY,
- new Object[]{0.8, 1.8, 2.8},
- new Object[]{1.1, 2.1, 3.1},
- false,
- false,
- null
- ),
- ImmutableList.of("0", "2")
- );
- }
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{0.8, 1.8, 2.8},
+ new Object[]{1.1, 2.1, 3.1},
+ true,
+ true,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{0.8, 1.8, 2.8},
+ new Object[]{1.1, 2.1, 3.1},
+ false,
+ true,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{0.8, 1.8, 2.8},
+ new Object[]{1.1, 2.1, 3.1},
+ true,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "arrayLong",
+ ColumnType.DOUBLE_ARRAY,
+ new Object[]{0.8, 1.8, 2.8},
+ new Object[]{1.1, 2.1, 3.1},
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "2")
+ );
}
@Test
@@ -1527,61 +1524,60 @@ public class RangeFilterTests
"6", .. null
"7", .. null
*/
- if (isAutoSchema()) {
- assertFilterMatches(
- new RangeFilter(
- "variant",
- ColumnType.LONG,
- 100L,
- null,
- false,
- false,
- null
- ),
- ImmutableList.of("1", "2", "5")
- );
- assertFilterMatches(
- NotDimFilter.of(
- new RangeFilter(
- "variant",
- ColumnType.LONG,
- 100L,
- null,
- false,
- false,
- null
- )
- ),
- NullHandling.sqlCompatible()
- ? ImmutableList.of("0", "3", "4")
- : ImmutableList.of("0", "3", "4", "6", "7")
- );
- // lexicographical comparison
- assertFilterMatches(
- new RangeFilter(
- "variant",
- ColumnType.STRING,
- "100",
- null,
- false,
- false,
- null
- ),
- ImmutableList.of("0", "1", "2", "4", "5")
- );
- assertFilterMatches(
- new RangeFilter(
- "variant",
- ColumnType.LONG_ARRAY,
- Collections.singletonList(100L),
- Arrays.asList(100L, 200L, 300L),
- false,
- false,
- null
- ),
- ImmutableList.of("1", "2", "5")
- );
- }
+ Assume.assumeTrue(isAutoSchema());
+ assertFilterMatches(
+ new RangeFilter(
+ "variant",
+ ColumnType.LONG,
+ 100L,
+ null,
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1", "2", "5")
+ );
+ assertFilterMatches(
+ NotDimFilter.of(
+ new RangeFilter(
+ "variant",
+ ColumnType.LONG,
+ 100L,
+ null,
+ false,
+ false,
+ null
+ )
+ ),
+ NullHandling.sqlCompatible()
+ ? ImmutableList.of("0", "3", "4")
+ : ImmutableList.of("0", "3", "4", "6", "7")
+ );
+ // lexicographical comparison
+ assertFilterMatches(
+ new RangeFilter(
+ "variant",
+ ColumnType.STRING,
+ "100",
+ null,
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("0", "1", "2", "4", "5")
+ );
+ assertFilterMatches(
+ new RangeFilter(
+ "variant",
+ ColumnType.LONG_ARRAY,
+ Collections.singletonList(100L),
+ Arrays.asList(100L, 200L, 300L),
+ false,
+ false,
+ null
+ ),
+ ImmutableList.of("1", "2", "5")
+ );
}
}
diff --git
a/processing/src/test/java/org/apache/druid/segment/generator/SegmentGenerator.java
b/processing/src/test/java/org/apache/druid/segment/generator/SegmentGenerator.java
index 8f1246c7255..7f44d8db746 100644
---
a/processing/src/test/java/org/apache/druid/segment/generator/SegmentGenerator.java
+++
b/processing/src/test/java/org/apache/druid/segment/generator/SegmentGenerator.java
@@ -22,8 +22,10 @@ package org.apache.druid.segment.generator;
import com.google.common.hash.Hashing;
import org.apache.druid.common.config.NullHandling;
import org.apache.druid.data.input.InputRow;
-import org.apache.druid.data.input.MapBasedInputRow;
+import org.apache.druid.data.input.InputRowSchema;
import org.apache.druid.data.input.impl.DimensionsSpec;
+import org.apache.druid.data.input.impl.MapInputRowParser;
+import org.apache.druid.data.input.impl.TimestampSpec;
import org.apache.druid.guice.NestedDataModule;
import org.apache.druid.java.util.common.FileUtils;
import org.apache.druid.java.util.common.ISE;
@@ -52,7 +54,6 @@ import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -183,14 +184,16 @@ public class SegmentGenerator implements Closeable
final List<QueryableIndex> indexes = new ArrayList<>();
Transformer transformer = transformSpec.toTransformer();
+ InputRowSchema rowSchema = new InputRowSchema(
+ new TimestampSpec(null, null, null),
+ dimensionsSpec,
+ null
+ );
for (int i = 0; i < numRows; i++) {
- final InputRow row = transformer.transform(dataGenerator.nextRow());
- Map<String, Object> evaluated = new HashMap<>();
- for (String dimension : dimensionsSpec.getDimensionNames()) {
- evaluated.put(dimension, row.getRaw(dimension));
- }
- MapBasedInputRow transformedRow = new
MapBasedInputRow(row.getTimestamp(), dimensionsSpec.getDimensionNames(),
evaluated);
+ Map<String, Object> raw = dataGenerator.nextRaw();
+ InputRow inputRow = MapInputRowParser.parse(rowSchema, raw);
+ InputRow transformedRow = transformer.transform(inputRow);
rows.add(transformedRow);
if ((i + 1) % 20000 == 0) {
diff --git
a/processing/src/test/java/org/apache/druid/segment/nested/VariantColumnSupplierTest.java
b/processing/src/test/java/org/apache/druid/segment/nested/VariantColumnSupplierTest.java
index b1eb65ea4ae..7910e49652f 100644
---
a/processing/src/test/java/org/apache/druid/segment/nested/VariantColumnSupplierTest.java
+++
b/processing/src/test/java/org/apache/druid/segment/nested/VariantColumnSupplierTest.java
@@ -461,9 +461,7 @@ public class VariantColumnSupplierTest extends
InitializedNullHandlingTest
}
Assert.assertTrue(nullValueIndex.get().computeBitmapResult(resultFactory,
false).get(i));
if (expectedType.getSingleType() != null) {
- Assert.assertFalse(arrayElementIndexes.containsValue(null,
expectedType.getSingleType()).computeBitmapResult(resultFactory,
-
false
- ).get(i));
+ Assert.assertFalse(arrayElementIndexes.containsValue(null,
expectedType.getSingleType()).computeBitmapResult(resultFactory, false).get(i));
}
}
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayContainsOperatorConversion.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayContainsOperatorConversion.java
index cffee832535..c961ed04016 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayContainsOperatorConversion.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayContainsOperatorConversion.java
@@ -31,8 +31,10 @@ import org.apache.druid.math.expr.ExprEval;
import org.apache.druid.math.expr.ExpressionType;
import org.apache.druid.math.expr.InputBindings;
import org.apache.druid.query.filter.AndDimFilter;
+import org.apache.druid.query.filter.ArrayContainsElementFilter;
import org.apache.druid.query.filter.DimFilter;
import org.apache.druid.query.filter.EqualityFilter;
+import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.segment.column.RowSignature;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.Expressions;
@@ -139,6 +141,43 @@ public class ArrayContainsOperatorConversion extends
BaseExpressionDimFilterOper
}
}
}
+ // if the input is a direct array column, we can use sweet array filter
+ if (leftExpr.isDirectColumnAccess() && isArray(leftExpr)) {
+ Expr expr = plannerContext.parseExpression(rightExpr.getExpression());
+ // To convert this expression filter into an And of ArrayContainsElement
filters, we need to extract all array
+ // elements. For now, we can optimize only when rightExpr is a literal
because there is no way to extract the
+ // array elements by traversing the Expr. Note that all implementations
of Expr are defined as package-private
+ // classes in a different package.
+ if (expr.isLiteral()) {
+ // Evaluate the expression to get out the array elements.
+ // We can safely pass a nil ObjectBinding if the expression is literal.
+ ExprEval<?> exprEval = expr.eval(InputBindings.nilBindings());
+ if (exprEval.isArray()) {
+ final Object[] arrayElements = exprEval.asArray();
+ final List<DimFilter> filters = new
ArrayList<>(arrayElements.length);
+ final ColumnType elementType =
ExpressionType.toColumnType(ExpressionType.elementType(exprEval.type()));
+ for (final Object val : arrayElements) {
+ filters.add(
+ new ArrayContainsElementFilter(
+ leftExpr.getSimpleExtraction().getColumn(),
+ elementType,
+ val,
+ null
+ )
+ );
+ }
+
+ return filters.size() == 1 ? filters.get(0) : new
AndDimFilter(filters);
+ } else {
+ return new ArrayContainsElementFilter(
+ leftExpr.getSimpleExtraction().getColumn(),
+ ExpressionType.toColumnType(exprEval.type()),
+ exprEval.valueOrDefault(),
+ null
+ );
+ }
+ }
+ }
return toExpressionFilter(plannerContext, getDruidFunctionName(),
druidExpressions);
}
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
index 4a266773eee..0f6e86edb18 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java
@@ -53,6 +53,7 @@ import
org.apache.druid.query.aggregation.LongSumAggregatorFactory;
import org.apache.druid.query.dimension.DefaultDimensionSpec;
import org.apache.druid.query.expression.TestExprMacroTable;
import org.apache.druid.query.extraction.SubstringDimExtractionFn;
+import org.apache.druid.query.filter.ArrayContainsElementFilter;
import org.apache.druid.query.filter.ExpressionDimFilter;
import org.apache.druid.query.filter.InDimFilter;
import org.apache.druid.query.filter.LikeDimFilter;
@@ -1091,7 +1092,11 @@ public class CalciteArraysQueryTest extends
BaseCalciteQueryTest
.dataSource(DATA_SOURCE_ARRAYS)
.intervals(querySegmentSpec(Filtration.eternity()))
.filters(
-
expressionFilter("array_contains(\"arrayStringNulls\",array('a','b'))")
+ and(
+ new ArrayContainsElementFilter("arrayStringNulls",
ColumnType.STRING, "a", null),
+ new ArrayContainsElementFilter("arrayStringNulls",
ColumnType.STRING, "b", null)
+ )
+
)
.columns("arrayStringNulls")
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
@@ -1117,7 +1122,10 @@ public class CalciteArraysQueryTest extends
BaseCalciteQueryTest
.dataSource(DATA_SOURCE_ARRAYS)
.intervals(querySegmentSpec(Filtration.eternity()))
.filters(
-
expressionFilter("array_contains(\"arrayLongNulls\",array(1,null))")
+ and(
+ new ArrayContainsElementFilter("arrayLongNulls",
ColumnType.LONG, 1L, null),
+ new ArrayContainsElementFilter("arrayLongNulls",
ColumnType.LONG, null, null)
+ )
)
.columns("arrayLongNulls")
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
@@ -1142,7 +1150,10 @@ public class CalciteArraysQueryTest extends
BaseCalciteQueryTest
.dataSource(DATA_SOURCE_ARRAYS)
.intervals(querySegmentSpec(Filtration.eternity()))
.filters(
-
expressionFilter("array_contains(\"arrayDoubleNulls\",array(1.1,null))")
+ and(
+ new ArrayContainsElementFilter("arrayDoubleNulls",
ColumnType.DOUBLE, 1.1, null),
+ new ArrayContainsElementFilter("arrayDoubleNulls",
ColumnType.DOUBLE, null, null)
+ )
)
.columns("arrayDoubleNulls")
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
@@ -6980,7 +6991,7 @@ public class CalciteArraysQueryTest extends
BaseCalciteQueryTest
)
.intervals(querySegmentSpec(Filtration.eternity()))
.filters(
-
expressionFilter("array_contains(\"arrayLongNulls\",array(2))")
+ new ArrayContainsElementFilter("arrayLongNulls",
ColumnType.LONG, 2L, null)
)
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
.legacy(false)
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
index 7a2a9809930..9c0677a0f15 100644
---
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
+++
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
@@ -49,6 +49,7 @@ import
org.apache.druid.query.aggregation.ExpressionLambdaAggregatorFactory;
import org.apache.druid.query.aggregation.FilteredAggregatorFactory;
import org.apache.druid.query.aggregation.LongSumAggregatorFactory;
import org.apache.druid.query.dimension.DefaultDimensionSpec;
+import org.apache.druid.query.filter.ArrayContainsElementFilter;
import org.apache.druid.query.filter.EqualityFilter;
import org.apache.druid.query.filter.ExpressionDimFilter;
import org.apache.druid.query.filter.InDimFilter;
@@ -1506,10 +1507,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
)
)
.setDimFilter(
- new ExpressionDimFilter(
- "array_contains(\"arrayLongNulls\",1)",
- queryFramework().macroTable()
- )
+ new
ArrayContainsElementFilter("arrayLongNulls", ColumnType.LONG, 1, null)
)
.setAggregatorSpecs(
aggregators(
@@ -1564,7 +1562,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
)
.setDimFilter(
or(
-
expressionFilter("array_contains(\"arrayLongNulls\",1)"),
+ new
ArrayContainsElementFilter("arrayLongNulls", ColumnType.LONG, 1L, null),
expressionFilter("array_overlap(\"arrayLongNulls\",array(2,3))")
)
)
@@ -1775,10 +1773,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
)
)
.setDimFilter(
- new ExpressionDimFilter(
- "array_contains(\"arrayStringNulls\",'b')",
- queryFramework().macroTable()
- )
+ new
ArrayContainsElementFilter("arrayStringNulls", ColumnType.STRING, "b", null)
)
.setAggregatorSpecs(
aggregators(
@@ -1994,10 +1989,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
)
)
.setDimFilter(
- new ExpressionDimFilter(
- "array_contains(\"arrayDoubleNulls\",2.2)",
- queryFramework().macroTable()
- )
+ new
ArrayContainsElementFilter("arrayDoubleNulls", ColumnType.DOUBLE, 2.2, null)
)
.setAggregatorSpecs(
aggregators(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]