mirror of https://github.com/apache/druid.git
Optimize JoinCondition matching (#9200)
* Optimize JoinCondition matching The LookupJoinMatcher needs to check if a condition is always true or false multiple times. This can be pre-computed to speed up the match checking This change reduces the time it takes to perform a for joining on a long key from ~ 36 ms/op to 23 ms/ op * Rename variables * fix typo
This commit is contained in:
parent
f511af1306
commit
a2939bbd1a
|
@ -28,6 +28,7 @@ import org.apache.druid.math.expr.Parser;
|
|||
import org.apache.druid.query.expression.ExprUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
@ -50,6 +51,9 @@ public class JoinConditionAnalysis
|
|||
private final String originalExpression;
|
||||
private final List<Equality> equiConditions;
|
||||
private final List<Expr> nonEquiConditions;
|
||||
private final boolean isAlwaysFalse;
|
||||
private final boolean isAlwaysTrue;
|
||||
private final boolean canHashJoin;
|
||||
|
||||
private JoinConditionAnalysis(
|
||||
final String originalExpression,
|
||||
|
@ -58,8 +62,15 @@ public class JoinConditionAnalysis
|
|||
)
|
||||
{
|
||||
this.originalExpression = Preconditions.checkNotNull(originalExpression, "originalExpression");
|
||||
this.equiConditions = equiConditions;
|
||||
this.nonEquiConditions = nonEquiConditions;
|
||||
this.equiConditions = Collections.unmodifiableList(equiConditions);
|
||||
this.nonEquiConditions = Collections.unmodifiableList(nonEquiConditions);
|
||||
// if any nonEquiCondition is an expression and it evaluates to false
|
||||
isAlwaysFalse = nonEquiConditions.stream()
|
||||
.anyMatch(expr -> expr.isLiteral() && !expr.eval(ExprUtils.nilBindings()).asBoolean());
|
||||
// if there are no equiConditions and all nonEquiConditions are literals and the evaluate to true
|
||||
isAlwaysTrue = equiConditions.isEmpty() && nonEquiConditions.stream()
|
||||
.allMatch(expr -> expr.isLiteral() && expr.eval(ExprUtils.nilBindings()).asBoolean());
|
||||
canHashJoin = nonEquiConditions.stream().allMatch(Expr::isLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,8 +152,7 @@ public class JoinConditionAnalysis
|
|||
*/
|
||||
public boolean isAlwaysFalse()
|
||||
{
|
||||
return nonEquiConditions.stream()
|
||||
.anyMatch(expr -> expr.isLiteral() && !expr.eval(ExprUtils.nilBindings()).asBoolean());
|
||||
return isAlwaysFalse;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,9 +160,7 @@ public class JoinConditionAnalysis
|
|||
*/
|
||||
public boolean isAlwaysTrue()
|
||||
{
|
||||
return equiConditions.isEmpty() &&
|
||||
nonEquiConditions.stream()
|
||||
.allMatch(expr -> expr.isLiteral() && expr.eval(ExprUtils.nilBindings()).asBoolean());
|
||||
return isAlwaysTrue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +168,7 @@ public class JoinConditionAnalysis
|
|||
*/
|
||||
public boolean canHashJoin()
|
||||
{
|
||||
return nonEquiConditions.stream().allMatch(Expr::isLiteral);
|
||||
return canHashJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -267,7 +267,11 @@ public class JoinConditionAnalysisTest
|
|||
{
|
||||
EqualsVerifier.forClass(JoinConditionAnalysis.class)
|
||||
.usingGetClass()
|
||||
.withIgnoredFields("equiConditions", "nonEquiConditions")
|
||||
.withIgnoredFields(
|
||||
// These fields are tightly coupled with originalExpression
|
||||
"equiConditions", "nonEquiConditions",
|
||||
// These fields are calculated from nonEquiConditions
|
||||
"isAlwaysTrue", "isAlwaysFalse", "canHashJoin")
|
||||
.verify();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue