| /* |
| * 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.join.table; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import org.apache.druid.common.config.NullHandling; |
| import org.apache.druid.java.util.common.DateTimes; |
| import org.apache.druid.java.util.common.io.Closer; |
| import org.apache.druid.math.expr.ExprMacroTable; |
| import org.apache.druid.query.InlineDataSource; |
| import org.apache.druid.query.dimension.DefaultDimensionSpec; |
| import org.apache.druid.query.dimension.DimensionSpec; |
| import org.apache.druid.segment.ColumnSelectorFactory; |
| import org.apache.druid.segment.ColumnValueSelector; |
| import org.apache.druid.segment.ConstantDimensionSelector; |
| import org.apache.druid.segment.DimensionSelector; |
| import org.apache.druid.segment.column.ColumnCapabilities; |
| import org.apache.druid.segment.column.RowSignature; |
| import org.apache.druid.segment.column.ValueType; |
| import org.apache.druid.segment.join.JoinConditionAnalysis; |
| import org.apache.druid.segment.join.JoinMatcher; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import java.util.Collections; |
| import java.util.Optional; |
| import java.util.Set; |
| |
| public class IndexedTableJoinableTest |
| { |
| private static final String PREFIX = "j."; |
| private static final String KEY_COLUMN = "str"; |
| private static final String VALUE_COLUMN = "long"; |
| private static final String UNKNOWN_COLUMN = "unknown"; |
| private static final String SEARCH_KEY_NULL_VALUE = "baz"; |
| private static final String SEARCH_KEY_VALUE = "foo"; |
| private static final String SEARCH_VALUE_VALUE = "1"; |
| private static final String SEARCH_VALUE_UNKNOWN = "10"; |
| private static final long MAX_CORRELATION_SET_SIZE = 10_000L; |
| |
| static { |
| NullHandling.initializeForTests(); |
| } |
| |
| private final ColumnSelectorFactory dummyColumnSelectorFactory = new ColumnSelectorFactory() |
| { |
| @Override |
| public DimensionSelector makeDimensionSelector(DimensionSpec dimensionSpec) |
| { |
| return new ConstantDimensionSelector("dummy"); |
| } |
| |
| @Override |
| public ColumnValueSelector<?> makeColumnValueSelector(String columnName) |
| { |
| return null; |
| } |
| |
| @Override |
| public ColumnCapabilities getColumnCapabilities(String columnName) |
| { |
| return null; |
| } |
| }; |
| |
| private final InlineDataSource inlineDataSource = InlineDataSource.fromIterable( |
| ImmutableList.of( |
| new Object[]{"foo", 1L}, |
| new Object[]{"bar", 2L}, |
| new Object[]{"baz", null} |
| ), |
| RowSignature.builder() |
| .add("str", ValueType.STRING) |
| .add("long", ValueType.LONG) |
| .build() |
| ); |
| |
| private final RowBasedIndexedTable<Object[]> indexedTable = new RowBasedIndexedTable<>( |
| inlineDataSource.getRowsAsList(), |
| inlineDataSource.rowAdapter(), |
| inlineDataSource.getRowSignature(), |
| ImmutableSet.of("str"), |
| DateTimes.nowUtc().toString() |
| ); |
| |
| private IndexedTableJoinable target; |
| |
| @Before |
| public void setUp() |
| { |
| target = new IndexedTableJoinable(indexedTable); |
| } |
| |
| @Test |
| public void getAvailableColumns() |
| { |
| Assert.assertEquals(ImmutableList.of("str", "long"), target.getAvailableColumns()); |
| } |
| |
| @Test |
| public void getCardinalityForStringColumn() |
| { |
| Assert.assertEquals(indexedTable.numRows() + 1, target.getCardinality("str")); |
| } |
| |
| @Test |
| public void getCardinalityForLongColumn() |
| { |
| Assert.assertEquals(indexedTable.numRows() + 1, target.getCardinality("long")); |
| } |
| |
| @Test |
| public void getCardinalityForNonexistentColumn() |
| { |
| Assert.assertEquals(1, target.getCardinality("nonexistent")); |
| } |
| |
| @Test |
| public void getColumnCapabilitiesForStringColumn() |
| { |
| final ColumnCapabilities capabilities = target.getColumnCapabilities("str"); |
| Assert.assertEquals(ValueType.STRING, capabilities.getType()); |
| Assert.assertTrue(capabilities.isDictionaryEncoded().isTrue()); |
| Assert.assertFalse(capabilities.hasBitmapIndexes()); |
| Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); |
| Assert.assertFalse(capabilities.hasSpatialIndexes()); |
| } |
| |
| @Test |
| public void getColumnCapabilitiesForLongColumn() |
| { |
| final ColumnCapabilities capabilities = target.getColumnCapabilities("long"); |
| Assert.assertEquals(ValueType.LONG, capabilities.getType()); |
| Assert.assertFalse(capabilities.isDictionaryEncoded().isTrue()); |
| Assert.assertFalse(capabilities.hasBitmapIndexes()); |
| Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); |
| Assert.assertFalse(capabilities.hasSpatialIndexes()); |
| } |
| |
| @Test |
| public void getColumnCapabilitiesForNonexistentColumnShouldReturnNull() |
| { |
| final ColumnCapabilities capabilities = target.getColumnCapabilities("nonexistent"); |
| Assert.assertNull(capabilities); |
| } |
| |
| @Test |
| public void makeJoinMatcherWithDimensionSelectorOnString() |
| { |
| final JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression( |
| "x == \"j.str\"", |
| PREFIX, |
| ExprMacroTable.nil() |
| ); |
| final JoinMatcher joinMatcher = target.makeJoinMatcher( |
| dummyColumnSelectorFactory, |
| condition, |
| false, |
| false, |
| Closer.create() |
| ); |
| |
| final DimensionSelector selector = joinMatcher.getColumnSelectorFactory() |
| .makeDimensionSelector(DefaultDimensionSpec.of("str")); |
| |
| // getValueCardinality |
| Assert.assertEquals(4, selector.getValueCardinality()); |
| |
| // nameLookupPossibleInAdvance |
| Assert.assertTrue(selector.nameLookupPossibleInAdvance()); |
| |
| // lookupName |
| Assert.assertEquals("foo", selector.lookupName(0)); |
| Assert.assertEquals("bar", selector.lookupName(1)); |
| Assert.assertEquals("baz", selector.lookupName(2)); |
| Assert.assertNull(selector.lookupName(3)); |
| |
| // lookupId |
| Assert.assertNull(selector.idLookup()); |
| } |
| |
| @Test |
| public void getCorrelatedColummnValuesMissingSearchColumnShouldReturnEmpty() |
| { |
| Optional<Set<String>> correlatedValues = |
| target.getCorrelatedColumnValues( |
| UNKNOWN_COLUMN, |
| "foo", |
| VALUE_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| false); |
| |
| Assert.assertEquals(Optional.empty(), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColummnValuesMissingRetrievalColumnShouldReturnEmpty() |
| { |
| Optional<Set<String>> correlatedValues = |
| target.getCorrelatedColumnValues( |
| KEY_COLUMN, |
| "foo", |
| UNKNOWN_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| false); |
| |
| Assert.assertEquals(Optional.empty(), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchKeyAndRetrieveKeyColumnShouldReturnSearchValue() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| KEY_COLUMN, |
| SEARCH_KEY_VALUE, |
| KEY_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| false); |
| Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_KEY_VALUE)), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchKeyAndRetrieveKeyColumnAboveLimitShouldReturnEmpty() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| KEY_COLUMN, |
| SEARCH_KEY_VALUE, |
| KEY_COLUMN, |
| 0, |
| false); |
| Assert.assertEquals(Optional.empty(), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchKeyAndRetrieveValueColumnShouldReturnExtractedValue() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| KEY_COLUMN, |
| SEARCH_KEY_VALUE, |
| VALUE_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| false); |
| Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_VALUE_VALUE)), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchKeyMissingAndRetrieveValueColumnShouldReturnExtractedValue() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| KEY_COLUMN, |
| SEARCH_KEY_NULL_VALUE, |
| VALUE_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| false); |
| Assert.assertEquals(Optional.of(Collections.singleton(null)), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnAndNonKeyColumnSearchDisabledShouldReturnEmpty() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| VALUE_COLUMN, |
| SEARCH_VALUE_VALUE, |
| VALUE_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| false); |
| Assert.assertEquals(Optional.empty(), correlatedValues); |
| correlatedValues = target.getCorrelatedColumnValues( |
| VALUE_COLUMN, |
| SEARCH_VALUE_VALUE, |
| KEY_COLUMN, |
| 10, |
| false); |
| Assert.assertEquals(Optional.empty(), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnShouldReturnSearchValue() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| VALUE_COLUMN, |
| SEARCH_VALUE_VALUE, |
| VALUE_COLUMN, |
| MAX_CORRELATION_SET_SIZE, |
| true); |
| Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_VALUE_VALUE)), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnShouldReturnUnAppliedValue() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| VALUE_COLUMN, |
| SEARCH_VALUE_VALUE, |
| KEY_COLUMN, |
| 10, |
| true); |
| Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_KEY_VALUE)), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnWithMaxLimitSetShouldHonorMaxLimit() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| VALUE_COLUMN, |
| SEARCH_VALUE_VALUE, |
| KEY_COLUMN, |
| 0, |
| true); |
| Assert.assertEquals(Optional.empty(), correlatedValues); |
| } |
| |
| @Test |
| public void getCorrelatedColumnValuesForSearchUnknownValueAndRetrieveKeyColumnShouldReturnNoCorrelatedValues() |
| { |
| Optional<Set<String>> correlatedValues = target.getCorrelatedColumnValues( |
| VALUE_COLUMN, |
| SEARCH_VALUE_UNKNOWN, |
| KEY_COLUMN, |
| 10, |
| true); |
| Assert.assertEquals(Optional.of(ImmutableSet.of()), correlatedValues); |
| } |
| } |