Skip to content

Commit

Permalink
Fold constant for List/Set Expression (#4901)
Browse files Browse the repository at this point in the history
fix ut

small comment

small delete

small change for ut

small fix

fix lookup

fix tck

fix tck

fix lookup index selection related to logicalOr expression

fix lookup

fix lookup index selection

fix lookup

small delete

small fix
  • Loading branch information
czpmango authored Nov 18, 2022
1 parent 5bf34d7 commit 90a3107
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 38 deletions.
8 changes: 4 additions & 4 deletions src/graph/optimizer/OptimizerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ std::vector<const Expression*> collectUnusedExpr(
return unusedOperands;
}

StatusOr<IndexResult> selectLogicalExprIndex(const LogicalExpression* expr,
const IndexItem& index) {
StatusOr<IndexResult> selectLogicalAndExprIndex(const LogicalExpression* expr,
const IndexItem& index) {
if (expr->kind() != Expression::Kind::kLogicalAnd) {
return Status::Error("Invalid expression kind.");
}
Expand Down Expand Up @@ -304,8 +304,8 @@ StatusOr<IndexResult> selectIndex(const Expression* expr, const IndexItem& index
return selectRelExprIndex(static_cast<const RelationalExpression*>(expr), index);
}

if (expr->isLogicalExpr()) {
return selectLogicalExprIndex(static_cast<const LogicalExpression*>(expr), index);
if (expr->kind() == Expression::Kind::kLogicalAnd) {
return selectLogicalAndExprIndex(static_cast<const LogicalExpression*>(expr), index);
}

return Status::Error("Invalid expression kind.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ StatusOr<TransformResult> OptimizeEdgeIndexScanByFilterRule::transform(
// Stand alone IN expr with only 1 element in the list, no need to check index
if (conditionType == ExprKind::kRelIn) {
transformedExpr = graph::ExpressionUtils::rewriteInExpr(condition);
DCHECK(transformedExpr->kind() == ExprKind::kRelEQ);
}

// case2: logical AND expr
Expand Down
37 changes: 25 additions & 12 deletions src/graph/optimizer/rule/OptimizeTagIndexScanByFilterRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,15 @@ bool OptimizeTagIndexScanByFilterRule::match(OptContext* ctx, const MatchedResul
// If the container in the IN expr has only 1 element, it will be converted to an relEQ
// expr. If more than 1 element found in the container, UnionAllIndexScanBaseRule will be
// applied.
if (relExpr->kind() == ExprKind::kRelIn && relExpr->right()->isContainerExpr()) {
auto ContainerOperands = graph::ExpressionUtils::getContainerExprOperands(relExpr->right());
return ContainerOperands.size() == 1;
auto* rhs = relExpr->right();
if (relExpr->kind() == ExprKind::kRelIn) {
if (rhs->isContainerExpr()) {
return graph::ExpressionUtils::getContainerExprOperands(relExpr->right()).size() == 1;
} else if (rhs->kind() == Expression::Kind::kConstant) {
auto constExprValue = static_cast<const ConstantExpression*>(rhs)->value();
return (constExprValue.isList() && constExprValue.getList().size() == 1) ||
(constExprValue.isSet() && constExprValue.getSet().size() == 1);
}
}
return relExpr->left()->kind() == ExprKind::kTagProperty &&
relExpr->right()->kind() == ExprKind::kConstant;
Expand Down Expand Up @@ -115,11 +121,10 @@ StatusOr<TransformResult> OptimizeTagIndexScanByFilterRule::transform(
// Stand alone IN expr with only 1 element in the list, no need to check index
if (conditionType == ExprKind::kRelIn) {
transformedExpr = graph::ExpressionUtils::rewriteInExpr(condition);
DCHECK(transformedExpr->kind() == ExprKind::kRelEQ);

} else if (conditionType == ExprKind::kStartsWith) {
// StartsWith expr is converted to a RelAnd expr with a constant value
transformedExpr = graph::ExpressionUtils::rewriteStartsWithExpr(condition);
DCHECK(transformedExpr->kind() == ExprKind::kLogicalAnd);
}

// case2: logical AND expr
Expand All @@ -130,15 +135,23 @@ StatusOr<TransformResult> OptimizeTagIndexScanByFilterRule::transform(
auto inExpr = static_cast<RelationalExpression*>(operand);
// Do not apply this rule if the IN expr has a valid index or it has more than 1 element in
// the list
if (static_cast<ListExpression*>(inExpr->right())->size() > 1) {
auto* rhs = inExpr->right();
if (rhs->isContainerExpr() &&
graph::ExpressionUtils::getContainerExprOperands(rhs).size() > 1) {
return TransformResult::noTransform();
} else {
// If the inner IN expr has only 1 element, rewrite it to an relEQ expression and there is
// no need to check whether it has a index
auto relEqExpr = graph::ExpressionUtils::rewriteInExpr(inExpr);
static_cast<LogicalExpression*>(transformedExpr)->setOperand(operandIndex, relEqExpr);
continue;
} else if (rhs->kind() == Expression::Kind::kConstant) {
auto constExprValue = static_cast<const ConstantExpression*>(rhs)->value();
if ((constExprValue.isList() && constExprValue.getList().size() > 1) ||
(constExprValue.isSet() && constExprValue.getSet().size() > 1)) {
return TransformResult::noTransform();
}
}
// If the inner IN expr has only 1 element, rewrite it to an relEQ expression and there is
// no need to check whether it has a index
auto relEqExpr = graph::ExpressionUtils::rewriteInExpr(inExpr);
static_cast<LogicalExpression*>(transformedExpr)->setOperand(operandIndex, relEqExpr);
continue;

if (OptimizerUtils::relExprHasIndex(inExpr, indexItems)) {
return TransformResult::noTransform();
}
Expand Down
12 changes: 9 additions & 3 deletions src/graph/optimizer/rule/UnionAllIndexScanBaseRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,15 @@ bool UnionAllIndexScanBaseRule::match(OptContext* ctx, const MatchedResult& matc
// relEQ expr by the OptimizeTagIndexScanByFilterRule.
if (condition->isRelExpr()) {
auto relExpr = static_cast<const RelationalExpression*>(condition);
if (relExpr->kind() == ExprKind::kRelIn && relExpr->right()->isContainerExpr()) {
auto operandsVec = graph::ExpressionUtils::getContainerExprOperands(relExpr->right());
return operandsVec.size() > 1;
auto* rhs = relExpr->right();
if (relExpr->kind() == ExprKind::kRelIn) {
if (rhs->isContainerExpr()) {
return graph::ExpressionUtils::getContainerExprOperands(rhs).size() > 1;
} else if (rhs->kind() == Expression::Kind::kConstant) {
auto constExprValue = static_cast<const ConstantExpression*>(rhs)->value();
return (constExprValue.isList() && constExprValue.getList().size() > 1) ||
(constExprValue.isSet() && constExprValue.getSet().size() > 1);
}
}
}

Expand Down
49 changes: 36 additions & 13 deletions src/graph/util/ExpressionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,22 +320,45 @@ Expression *ExpressionUtils::rewriteInExpr(const Expression *expr) {
DCHECK(expr->kind() == Expression::Kind::kRelIn);
auto pool = expr->getObjPool();
auto inExpr = static_cast<RelationalExpression *>(expr->clone());
auto containerOperands = getContainerExprOperands(inExpr->right());
auto orExpr = LogicalExpression::makeOr(pool);
if (inExpr->right()->isContainerExpr()) {
auto containerOperands = getContainerExprOperands(inExpr->right());

auto operandSize = containerOperands.size();
// container has only 1 element, no need to transform to logical expression
if (operandSize == 1) {
return RelationalExpression::makeEQ(pool, inExpr->left(), containerOperands[0]);
}
auto operandSize = containerOperands.size();
// container has only 1 element, no need to transform to logical expression
if (operandSize == 1) {
return RelationalExpression::makeEQ(pool, inExpr->left(), containerOperands[0]);
}

std::vector<Expression *> orExprOperands;
orExprOperands.reserve(operandSize);
// A in [B, C, D] => (A == B) or (A == C) or (A == D)
for (auto *operand : containerOperands) {
orExprOperands.emplace_back(RelationalExpression::makeEQ(pool, inExpr->left(), operand));
std::vector<Expression *> orExprOperands;
orExprOperands.reserve(operandSize);
// A in [B, C, D] => (A == B) or (A == C) or (A == D)
for (auto *operand : containerOperands) {
orExprOperands.emplace_back(RelationalExpression::makeEQ(pool, inExpr->left(), operand));
}
orExpr->setOperands(orExprOperands);
} else if (inExpr->right()->kind() == Expression::Kind::kConstant) {
auto constExprValue = static_cast<ConstantExpression *>(inExpr->right())->value();
std::vector<Value> values;
if (constExprValue.isList()) {
values = constExprValue.getList().values;
} else if (constExprValue.isSet()) {
auto setValues = constExprValue.getSet().values;
values = std::vector<Value>{std::make_move_iterator(setValues.begin()),
std::make_move_iterator(setValues.end())};
} else {
return const_cast<Expression *>(expr);
}
std::vector<Expression *> operands;
for (const auto &v : values) {
operands.emplace_back(
RelationalExpression::makeEQ(pool, inExpr->left(), ConstantExpression::make(pool, v)));
}
if (operands.size() == 1) {
return operands[0];
}
orExpr->setOperands(operands);
}
auto orExpr = LogicalExpression::makeOr(pool);
orExpr->setOperands(orExprOperands);

return orExpr;
}
Expand Down
31 changes: 28 additions & 3 deletions src/graph/validator/LookupValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,11 @@ StatusOr<Expression*> LookupValidator::checkConstExpr(Expression* expr,
// TODO(Aiee) extract the type cast logic as a method if we decide to support
// more cross-type comparisons.

auto propType = SchemaUtil::propTypeToValueType(type);
// Allow different numeric type to compare
if (graph::SchemaUtil::propTypeToValueType(type) == Value::Type::FLOAT && v.isInt()) {
if (propType == Value::Type::FLOAT && v.isInt()) {
return ConstantExpression::make(pool, v.toFloat());
} else if (graph::SchemaUtil::propTypeToValueType(type) == Value::Type::INT && v.isFloat()) {
} else if (propType == Value::Type::INT && v.isFloat()) {
// col1 < 10.5 range: [min, 11), col1 < 10 range: [min, 10)
double f = v.getFloat();
int iCeil = ceil(f);
Expand All @@ -462,10 +463,34 @@ StatusOr<Expression*> LookupValidator::checkConstExpr(Expression* expr,
return ConstantExpression::make(pool, iCeil);
}
return ConstantExpression::make(pool, iFloor);
} else if (kind == ExprKind::kRelIn || kind == ExprKind::kRelNotIn) {
if (v.isList()) {
auto items = v.getList().values;
for (const auto& item : items) {
if (item.type() != propType) {
std::stringstream ss;
ss << "Column type of " << prop.c_str() << " mismatched. Expected " << propType
<< " but was " << item.type();
return Status::SemanticError(ss.str());
}
}
return ConstantExpression::make(pool, std::move(v));
} else if (v.isSet()) {
auto items = v.getSet().values;
for (const auto& item : items) {
if (item.type() != propType) {
std::stringstream ss;
ss << "Column type of " << prop.c_str() << " mismatched. Expected " << propType
<< " but was " << item.type();
return Status::SemanticError(ss.str());
}
}
return ConstantExpression::make(pool, std::move(v));
}
}

// Check prop type
if (v.type() != SchemaUtil::propTypeToValueType(type)) {
if (v.type() != propType) {
// allow different types in the IN expression, such as "abc" IN ["abc"]
if (!expr->isContainerExpr()) {
return Status::SemanticError("Column type error : %s", prop.c_str());
Expand Down
24 changes: 22 additions & 2 deletions src/graph/visitor/FoldConstantExprVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,27 @@ void FoldConstantExprVisitor::visit(ArithmeticExpression *expr) {

void FoldConstantExprVisitor::visit(RelationalExpression *expr) {
visitBinaryExpr(expr);
if (expr->kind() == Expression::Kind::kRelIn) {
auto rhs = static_cast<RelationalExpression *>(expr)->right();
if (rhs->kind() == Expression::Kind::kConstant) {
auto ce = static_cast<ConstantExpression *>(rhs);
auto v = ce->value();
// Convert to more efficient C++ data structure based on the size of the container expression
if (v.isList()) {
auto &list = v.getList().values;
if (list.size() >= 16) {
ce->setValue(Set(std::unordered_set<Value>{std::make_move_iterator(list.begin()),
std::make_move_iterator(list.end())}));
}
} else if (v.isSet()) {
auto &set = v.getSet().values;
if (set.size() < 16) {
ce->setValue(List(std::vector<Value>{std::make_move_iterator(set.begin()),
std::make_move_iterator(set.end())}));
}
}
}
}
}

void FoldConstantExprVisitor::visit(SubscriptExpression *expr) {
Expand Down Expand Up @@ -348,8 +369,7 @@ void FoldConstantExprVisitor::visitBinaryExpr(BinaryExpression *expr) {
}

Expression *FoldConstantExprVisitor::fold(Expression *expr) {
// Container expression should remain the same type after being folded
if (expr->isContainerExpr() || !status_.ok()) {
if (!status_.ok()) {
return expr;
}

Expand Down
54 changes: 54 additions & 0 deletions src/graph/visitor/test/FoldConstantExprVisitorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,5 +215,59 @@ TEST_F(FoldConstantExprVisitorTest, TestPathBuild) {
expected->add(ConstantExpression::make(pool, "TOM"));
ASSERT_EQ(*expr, *expected);
}

TEST_F(FoldConstantExprVisitorTest, TestRelIn) {
auto items = ExpressionList::make(pool);
items->add(ArithmeticExpression::makeAdd(
pool, ConstantExpression::make(pool, 1), ConstantExpression::make(pool, 2)));
items->add(ConstantExpression::make(pool, 4));
items->add(ConstantExpression::make(pool, false));
items->add(ConstantExpression::make(pool, "3"));
items->add(ConstantExpression::make(pool, 5));
items->add(ConstantExpression::make(pool, 3));
auto lhs = ConstantExpression::make(pool, 3);
auto listExpr = ListExpression::make(pool, items);
auto inListExpr = RelationalExpression::makeIn(pool, lhs, listExpr);
FoldConstantExprVisitor foldVisitor(pool);
inListExpr->accept(&foldVisitor);

auto expectInListExpr = RelationalExpression::makeIn(
pool, lhs, ConstantExpression::make(pool, List(std::vector<Value>{3, 4, false, "3", 5, 3})));
EXPECT_EQ(*inListExpr, *expectInListExpr);
auto setExpr = SetExpression::make(pool, items);
auto inSetExpr = RelationalExpression::makeIn(pool, lhs, setExpr);
inSetExpr->accept(&foldVisitor);
auto expectInSetExpr = RelationalExpression::makeIn(
pool, lhs, ConstantExpression::make(pool, List(std::vector<Value>{5, "3", false, 4, 3})));
EXPECT_EQ(*inSetExpr, *expectInSetExpr);

for (int i = 10u; i < 20; ++i) {
items->add(ConstantExpression::make(pool, i));
}

auto bigListExpr = ListExpression::make(pool, items);
auto inBigListExpr = RelationalExpression::makeIn(pool, lhs, bigListExpr);

inBigListExpr->accept(&foldVisitor);

auto expectInBigListExpr = RelationalExpression::makeIn(
pool,
lhs,
ConstantExpression::make(pool,
Set(std::unordered_set<Value>{
19, 18, 3, 16, 4, 17, false, 13, "3", 5, 10, 11, 12, 14, 15})));
EXPECT_EQ(*inBigListExpr, *expectInBigListExpr);
auto bigSetExpr = SetExpression::make(pool, items);
auto inBigSetExpr = RelationalExpression::makeIn(pool, lhs, bigSetExpr);
inBigSetExpr->accept(&foldVisitor);
auto expectInBigSetExpr = RelationalExpression::makeIn(
pool,
lhs,
ConstantExpression::make(
pool,
List(std::vector<Value>{19, 18, 16, 15, 14, 12, 11, 10, 5, 13, "3", 17, false, 4, 3})));
EXPECT_EQ(*inBigSetExpr, *expectInBigSetExpr);
}

} // namespace graph
} // namespace nebula

0 comments on commit 90a3107

Please sign in to comment.