mirror of https://github.com/apache/druid.git
Add getRightEquiConditionKeys to JoinConditionAnalysis (#9287)
* Add getRightColumns to JoinConditionAnalysis This change other implementations of JoinableFactory to ask the analysis for the right key columns instead of having to calculate it themselves. * Address some review comments * more code review stuff
This commit is contained in:
parent
a1494c30e0
commit
6b44d4aa80
|
@ -32,6 +32,8 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents analysis of a join condition.
|
* Represents analysis of a join condition.
|
||||||
|
@ -55,6 +57,7 @@ public class JoinConditionAnalysis
|
||||||
private final boolean isAlwaysFalse;
|
private final boolean isAlwaysFalse;
|
||||||
private final boolean isAlwaysTrue;
|
private final boolean isAlwaysTrue;
|
||||||
private final boolean canHashJoin;
|
private final boolean canHashJoin;
|
||||||
|
private final Set<String> rightKeyColumns;
|
||||||
|
|
||||||
private JoinConditionAnalysis(
|
private JoinConditionAnalysis(
|
||||||
final String originalExpression,
|
final String originalExpression,
|
||||||
|
@ -76,6 +79,7 @@ public class JoinConditionAnalysis
|
||||||
.allMatch(expr -> expr.isLiteral() && expr.eval(
|
.allMatch(expr -> expr.isLiteral() && expr.eval(
|
||||||
ExprUtils.nilBindings()).asBoolean());
|
ExprUtils.nilBindings()).asBoolean());
|
||||||
canHashJoin = nonEquiConditions.stream().allMatch(Expr::isLiteral);
|
canHashJoin = nonEquiConditions.stream().allMatch(Expr::isLiteral);
|
||||||
|
rightKeyColumns = getEquiConditions().stream().map(Equality::getRightColumn).distinct().collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,6 +180,14 @@ public class JoinConditionAnalysis
|
||||||
return canHashJoin;
|
return canHashJoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distinct column keys from the RHS required to evaluate the equi conditions.
|
||||||
|
*/
|
||||||
|
public Set<String> getRightEquiConditionKeys()
|
||||||
|
{
|
||||||
|
return rightKeyColumns;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
|
|
|
@ -191,9 +191,9 @@ public class LookupJoinMatcher implements JoinMatcher
|
||||||
keyExprs = null;
|
keyExprs = null;
|
||||||
} else if (!condition.getNonEquiConditions().isEmpty()) {
|
} else if (!condition.getNonEquiConditions().isEmpty()) {
|
||||||
throw new IAE("Cannot join lookup with non-equi condition: %s", condition);
|
throw new IAE("Cannot join lookup with non-equi condition: %s", condition);
|
||||||
} else if (!condition.getEquiConditions()
|
} else if (!condition.getRightEquiConditionKeys()
|
||||||
.stream()
|
.stream()
|
||||||
.allMatch(eq -> eq.getRightColumn().equals(LookupColumnSelectorFactory.KEY_COLUMN))) {
|
.allMatch(LookupColumnSelectorFactory.KEY_COLUMN::equals)) {
|
||||||
throw new IAE("Cannot join lookup with condition referring to non-key column: %s", condition);
|
throw new IAE("Cannot join lookup with condition referring to non-key column: %s", condition);
|
||||||
} else {
|
} else {
|
||||||
keyExprs = condition.getEquiConditions().stream().map(Equality::getLeftExpr).collect(Collectors.toList());
|
keyExprs = condition.getEquiConditions().stream().map(Equality::getLeftExpr).collect(Collectors.toList());
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.druid.segment.column.ValueType;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface to a table where some columns (the 'key columns') have indexes that enable fast lookups.
|
* An interface to a table where some columns (the 'key columns') have indexes that enable fast lookups.
|
||||||
|
@ -36,7 +37,7 @@ public interface IndexedTable
|
||||||
/**
|
/**
|
||||||
* Returns the columns of this table that have indexes.
|
* Returns the columns of this table that have indexes.
|
||||||
*/
|
*/
|
||||||
List<String> keyColumns();
|
Set<String> keyColumns();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all columns of this table, including the key and non-key columns.
|
* Returns all columns of this table, including the key and non-key columns.
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -48,13 +49,13 @@ public class RowBasedIndexedTable<RowType> implements IndexedTable
|
||||||
private final List<String> columns;
|
private final List<String> columns;
|
||||||
private final List<ValueType> columnTypes;
|
private final List<ValueType> columnTypes;
|
||||||
private final List<Function<RowType, Object>> columnFunctions;
|
private final List<Function<RowType, Object>> columnFunctions;
|
||||||
private final List<String> keyColumns;
|
private final Set<String> keyColumns;
|
||||||
|
|
||||||
public RowBasedIndexedTable(
|
public RowBasedIndexedTable(
|
||||||
final List<RowType> table,
|
final List<RowType> table,
|
||||||
final RowAdapter<RowType> rowAdapter,
|
final RowAdapter<RowType> rowAdapter,
|
||||||
final Map<String, ValueType> rowSignature,
|
final Map<String, ValueType> rowSignature,
|
||||||
final List<String> keyColumns
|
final Set<String> keyColumns
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.table = table;
|
this.table = table;
|
||||||
|
@ -107,7 +108,7 @@ public class RowBasedIndexedTable<RowType> implements IndexedTable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> keyColumns()
|
public Set<String> keyColumns()
|
||||||
{
|
{
|
||||||
return keyColumns;
|
return keyColumns;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package org.apache.druid.segment.join;
|
package org.apache.druid.segment.join;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import nl.jqno.equalsverifier.EqualsVerifier;
|
import nl.jqno.equalsverifier.EqualsVerifier;
|
||||||
import org.apache.druid.common.config.NullHandling;
|
import org.apache.druid.common.config.NullHandling;
|
||||||
import org.apache.druid.java.util.common.Pair;
|
import org.apache.druid.java.util.common.Pair;
|
||||||
|
@ -60,6 +61,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of(),
|
ImmutableList.of(),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertEquals(analysis.getRightEquiConditionKeys(), ImmutableSet.of("y"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -80,6 +82,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of(),
|
ImmutableList.of(),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertEquals(analysis.getRightEquiConditionKeys(), ImmutableSet.of("y"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,6 +103,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of(),
|
ImmutableList.of(),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertEquals(analysis.getRightEquiConditionKeys(), ImmutableSet.of("z"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -120,6 +124,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of("(== (+ j.x j.y) z)"),
|
ImmutableList.of("(== (+ j.x j.y) z)"),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertTrue(analysis.getRightEquiConditionKeys().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -140,6 +145,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of("(== (+ x j.y) j.z)"),
|
ImmutableList.of("(== (+ x j.y) j.z)"),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertTrue(analysis.getRightEquiConditionKeys().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -160,6 +166,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of("2"),
|
ImmutableList.of("2"),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertTrue(analysis.getRightEquiConditionKeys().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -180,6 +187,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of("0"),
|
ImmutableList.of("0"),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertTrue(analysis.getRightEquiConditionKeys().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -200,6 +208,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of("(== x 1)"),
|
ImmutableList.of("(== x 1)"),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertTrue(analysis.getRightEquiConditionKeys().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -220,6 +229,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of(),
|
ImmutableList.of(),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertEquals(analysis.getRightEquiConditionKeys(), ImmutableSet.of("x"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -240,6 +250,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of(),
|
ImmutableList.of(),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertEquals(analysis.getRightEquiConditionKeys(), ImmutableSet.of("y", "z", "zz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -260,6 +271,7 @@ public class JoinConditionAnalysisTest
|
||||||
ImmutableList.of("(|| (== (+ x y) j.z) (== z j.zz))"),
|
ImmutableList.of("(|| (== (+ x y) j.z) (== z j.zz))"),
|
||||||
exprsToStrings(analysis.getNonEquiConditions())
|
exprsToStrings(analysis.getNonEquiConditions())
|
||||||
);
|
);
|
||||||
|
Assert.assertEquals(analysis.getRightEquiConditionKeys(), ImmutableSet.of("y"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -270,8 +282,8 @@ public class JoinConditionAnalysisTest
|
||||||
.withIgnoredFields(
|
.withIgnoredFields(
|
||||||
// These fields are tightly coupled with originalExpression
|
// These fields are tightly coupled with originalExpression
|
||||||
"equiConditions", "nonEquiConditions",
|
"equiConditions", "nonEquiConditions",
|
||||||
// These fields are calculated from nonEquiConditions
|
// These fields are calculated from other other fields in the class
|
||||||
"isAlwaysTrue", "isAlwaysFalse", "canHashJoin")
|
"isAlwaysTrue", "isAlwaysFalse", "canHashJoin", "rightKeyColumns")
|
||||||
.verify();
|
.verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.MappingIterator;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.apache.druid.common.config.NullHandling;
|
import org.apache.druid.common.config.NullHandling;
|
||||||
import org.apache.druid.data.input.InputRow;
|
import org.apache.druid.data.input.InputRow;
|
||||||
|
@ -252,7 +253,7 @@ public class JoinTestHelper
|
||||||
rows,
|
rows,
|
||||||
createMapRowAdapter(COUNTRIES_SIGNATURE),
|
createMapRowAdapter(COUNTRIES_SIGNATURE),
|
||||||
COUNTRIES_SIGNATURE,
|
COUNTRIES_SIGNATURE,
|
||||||
ImmutableList.of("countryNumber", "countryIsoCode")
|
ImmutableSet.of("countryNumber", "countryIsoCode")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -265,7 +266,7 @@ public class JoinTestHelper
|
||||||
rows,
|
rows,
|
||||||
createMapRowAdapter(REGIONS_SIGNATURE),
|
createMapRowAdapter(REGIONS_SIGNATURE),
|
||||||
REGIONS_SIGNATURE,
|
REGIONS_SIGNATURE,
|
||||||
ImmutableList.of("regionIsoCode", "countryIsoCode")
|
ImmutableSet.of("regionIsoCode", "countryIsoCode")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package org.apache.druid.segment.join.table;
|
package org.apache.druid.segment.join.table;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.apache.druid.math.expr.ExprMacroTable;
|
import org.apache.druid.math.expr.ExprMacroTable;
|
||||||
import org.apache.druid.query.InlineDataSource;
|
import org.apache.druid.query.InlineDataSource;
|
||||||
import org.apache.druid.query.dimension.DefaultDimensionSpec;
|
import org.apache.druid.query.dimension.DefaultDimensionSpec;
|
||||||
|
@ -72,7 +73,7 @@ public class IndexedTableJoinableTest
|
||||||
inlineDataSource.getRowsAsList(),
|
inlineDataSource.getRowsAsList(),
|
||||||
inlineDataSource.rowAdapter(),
|
inlineDataSource.rowAdapter(),
|
||||||
inlineDataSource.getRowSignature(),
|
inlineDataSource.getRowSignature(),
|
||||||
ImmutableList.of("str")
|
ImmutableSet.of("str")
|
||||||
);
|
);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.druid.segment.join.table;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.apache.druid.common.config.NullHandling;
|
import org.apache.druid.common.config.NullHandling;
|
||||||
import org.apache.druid.segment.column.ValueType;
|
import org.apache.druid.segment.column.ValueType;
|
||||||
import org.apache.druid.segment.join.JoinTestHelper;
|
import org.apache.druid.segment.join.JoinTestHelper;
|
||||||
|
@ -64,7 +65,7 @@ public class RowBasedIndexedTableTest
|
||||||
@Test
|
@Test
|
||||||
public void test_keyColumns_countries()
|
public void test_keyColumns_countries()
|
||||||
{
|
{
|
||||||
Assert.assertEquals(ImmutableList.of("countryNumber", "countryIsoCode"), countriesTable.keyColumns());
|
Assert.assertEquals(ImmutableSet.of("countryNumber", "countryIsoCode"), countriesTable.keyColumns());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -25,9 +25,8 @@ import org.apache.druid.segment.join.table.IndexedTable;
|
||||||
import org.apache.druid.segment.join.table.IndexedTableJoinable;
|
import org.apache.druid.segment.join.table.IndexedTableJoinable;
|
||||||
import org.apache.druid.segment.join.table.RowBasedIndexedTable;
|
import org.apache.druid.segment.join.table.RowBasedIndexedTable;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link JoinableFactory} for {@link InlineDataSource}. It works by building an {@link IndexedTable}.
|
* A {@link JoinableFactory} for {@link InlineDataSource}. It works by building an {@link IndexedTable}.
|
||||||
|
@ -39,8 +38,7 @@ public class InlineJoinableFactory implements JoinableFactory
|
||||||
{
|
{
|
||||||
if (condition.canHashJoin() && dataSource instanceof InlineDataSource) {
|
if (condition.canHashJoin() && dataSource instanceof InlineDataSource) {
|
||||||
final InlineDataSource inlineDataSource = (InlineDataSource) dataSource;
|
final InlineDataSource inlineDataSource = (InlineDataSource) dataSource;
|
||||||
final List<String> rightKeyColumns =
|
final Set<String> rightKeyColumns = condition.getRightEquiConditionKeys();
|
||||||
condition.getEquiConditions().stream().map(Equality::getRightColumn).distinct().collect(Collectors.toList());
|
|
||||||
|
|
||||||
return Optional.of(
|
return Optional.of(
|
||||||
new IndexedTableJoinable(
|
new IndexedTableJoinable(
|
||||||
|
|
Loading…
Reference in New Issue