diff --git a/src/util/ExpressionUtils.cpp b/src/util/ExpressionUtils.cpp index 6a99e2071ccc35bca96a5322356e7a5317989c1b..ebe3475cf3c5f6c90912468266ab0542b73db559 100644 --- a/src/util/ExpressionUtils.cpp +++ b/src/util/ExpressionUtils.cpp @@ -76,18 +76,125 @@ std::unique_ptr<InputPropertyExpression> ExpressionUtils::inputPropExpr(const st } std::unique_ptr<Expression> -ExpressionUtils::pushOrs(const std::vector<std::unique_ptr<RelationalExpression>>& rels) { +ExpressionUtils::pushOrs(const std::vector<std::unique_ptr<Expression>>& rels) { + return pushImpl(Expression::Kind::kLogicalOr, rels); +} + +std::unique_ptr<Expression> +ExpressionUtils::pushAnds(const std::vector<std::unique_ptr<Expression>>& rels) { + return pushImpl(Expression::Kind::kLogicalAnd, rels); +} + +std::unique_ptr<Expression> +ExpressionUtils::pushImpl(Expression::Kind kind, + const std::vector<std::unique_ptr<Expression>>& rels) { DCHECK_GT(rels.size(), 1); - auto root = std::make_unique<LogicalExpression>(Expression::Kind::kLogicalOr); + DCHECK(kind == Expression::Kind::kLogicalOr || kind == Expression::Kind::kLogicalAnd); + auto root = std::make_unique<LogicalExpression>(kind); root->addOperand(rels[0]->clone().release()); root->addOperand(rels[1]->clone().release()); for (size_t i = 2; i < rels.size(); i++) { - auto l = std::make_unique<LogicalExpression>(Expression::Kind::kLogicalOr); + auto l = std::make_unique<LogicalExpression>(kind); l->addOperand(root->clone().release()); l->addOperand(rels[i]->clone().release()); root = std::move(l); } return root; } + +std::unique_ptr<Expression> ExpressionUtils::expandExpr(const Expression *expr) { + auto kind = expr->kind(); + std::vector<std::unique_ptr<Expression>> target; + switch (kind) { + case Expression::Kind::kLogicalOr: { + const auto *logic = static_cast<const LogicalExpression*>(expr); + for (const auto& e : logic->operands()) { + if (e->kind() == Expression::Kind::kLogicalAnd) { + target.emplace_back(expandImplAnd(e.get())); + } else { + target.emplace_back(expandExpr(e.get())); + } + } + break; + } + case Expression::Kind::kLogicalAnd: { + target.emplace_back(expandImplAnd(expr)); + break; + } + default: { + return expr->clone(); + } + } + if (target.size() == 1) { + if (target[0]->kind() == Expression::Kind::kLogicalAnd) { + const auto *logic = static_cast<const LogicalExpression*>(target[0].get()); + const auto& ops = logic->operands(); + DCHECK_EQ(ops.size(), 2); + if (ops[0]->kind() == Expression::Kind::kLogicalOr || + ops[1]->kind() == Expression::Kind::kLogicalOr) { + return expandExpr(target[0].get()); + } + } + return std::move(target[0]); + } + return pushImpl(kind, target); +} + +std::unique_ptr<Expression> ExpressionUtils::expandImplAnd(const Expression *expr) { + DCHECK(expr->kind() == Expression::Kind::kLogicalAnd); + const auto *logic = static_cast<const LogicalExpression*>(expr); + DCHECK_EQ(logic->operands().size(), 2); + std::vector<std::unique_ptr<Expression>> subL; + auto& ops = logic->operands(); + if (ops[0]->kind() == Expression::Kind::kLogicalOr) { + auto target = expandImplOr(ops[0].get()); + for (const auto& e : target) { + subL.emplace_back(e->clone().release()); + } + } else { + subL.emplace_back(expandExpr(std::move(ops[0]).get())); + } + std::vector<std::unique_ptr<Expression>> subR; + if (ops[1]->kind() == Expression::Kind::kLogicalOr) { + auto target = expandImplOr(ops[1].get()); + for (const auto& e : target) { + subR.emplace_back(e->clone().release()); + } + } else { + subR.emplace_back(expandExpr(std::move(ops[1]).get())); + } + + std::vector<std::unique_ptr<Expression>> target; + for (auto& le : subL) { + for (auto& re : subR) { + auto l = std::make_unique<LogicalExpression>(Expression::Kind::kLogicalAnd); + l->addOperand(le->clone().release()); + l->addOperand(re->clone().release()); + target.emplace_back(std::move(l)); + } + } + if (target.size() == 1) { + return std::move(target[0]); + } + return pushImpl(Expression::Kind::kLogicalOr, target); +} + +std::vector<std::unique_ptr<Expression>> ExpressionUtils::expandImplOr(const Expression *expr) { + DCHECK(expr->kind() == Expression::Kind::kLogicalOr); + const auto *logic = static_cast<const LogicalExpression*>(expr); + std::vector<std::unique_ptr<Expression>> exprs; + auto& ops = logic->operands(); + for (const auto& op : ops) { + if (op->kind() == Expression::Kind::kLogicalOr) { + auto target = expandImplOr(op.get()); + for (const auto& e : target) { + exprs.emplace_back(e->clone().release()); + } + } else { + exprs.emplace_back(op->clone().release()); + } + } + return exprs; +} } // namespace graph } // namespace nebula diff --git a/src/util/ExpressionUtils.h b/src/util/ExpressionUtils.h index 982f8bcb41ce0d8cda2f9e21a01bd7775a57ea82..9e549863bd644129287c5cc7c1abb5003e0da41d 100644 --- a/src/util/ExpressionUtils.h +++ b/src/util/ExpressionUtils.h @@ -125,7 +125,19 @@ public: static std::unique_ptr<InputPropertyExpression> inputPropExpr(const std::string& prop); static std::unique_ptr<Expression> pushOrs( - const std::vector<std::unique_ptr<RelationalExpression>>& rels); + const std::vector<std::unique_ptr<Expression>>& rels); + + static std::unique_ptr<Expression> pushAnds( + const std::vector<std::unique_ptr<Expression>>& rels); + + static std::unique_ptr<Expression> pushImpl( + Expression::Kind kind, const std::vector<std::unique_ptr<Expression>>& rels); + + static std::unique_ptr<Expression> expandExpr(const Expression *expr); + + static std::unique_ptr<Expression> expandImplAnd(const Expression *expr); + + static std::vector<std::unique_ptr<Expression>> expandImplOr(const Expression *expr); }; } // namespace graph diff --git a/src/util/test/ExpressionUtilsTest.cpp b/src/util/test/ExpressionUtilsTest.cpp index 989f0d622c70511ee9118ec03f39948a3329f422..658493960fe5d6efff19f694b6c22a8f46697926 100644 --- a/src/util/test/ExpressionUtilsTest.cpp +++ b/src/util/test/ExpressionUtilsTest.cpp @@ -9,6 +9,7 @@ #include "common/expression/ConstantExpression.h" #include "common/expression/TypeCastingExpression.h" #include "util/ExpressionUtils.h" +#include "parser/GQLParser.h" namespace nebula { namespace graph { @@ -363,7 +364,7 @@ TEST_F(ExpressionUtilsTest, PullOrs) { } TEST_F(ExpressionUtilsTest, pushOrs) { - std::vector<std::unique_ptr<RelationalExpression>> rels; + std::vector<std::unique_ptr<Expression>> rels; for (int16_t i = 0; i < 5; i++) { auto r = std::make_unique<RelationalExpression>( Expression::Kind::kRelEQ, @@ -381,5 +382,176 @@ TEST_F(ExpressionUtilsTest, pushOrs) { ASSERT_EQ(expected, t->toString()); } +TEST_F(ExpressionUtilsTest, pushAnds) { + std::vector<std::unique_ptr<Expression>> rels; + for (int16_t i = 0; i < 5; i++) { + auto r = std::make_unique<RelationalExpression>( + Expression::Kind::kRelEQ, + new LabelAttributeExpression(new LabelExpression(folly::stringPrintf("tag%d", i)), + new ConstantExpression(folly::stringPrintf("col%d", i))), + new ConstantExpression(Value(folly::stringPrintf("val%d", i)))); + rels.emplace_back(std::move(r)); + } + auto t = ExpressionUtils::pushAnds(rels); + auto expected = std::string("(((((tag0.col0==val0) AND " + "(tag1.col1==val1)) AND " + "(tag2.col2==val2)) AND " + "(tag3.col3==val3)) AND " + "(tag4.col4==val4))"); + ASSERT_EQ(expected, t->toString()); +} + +std::unique_ptr<Expression> parse(const std::string& expr) { + std::string query = "LOOKUP on t1 WHERE " + expr; + GQLParser parser; + auto result = parser.parse(std::move(query)); + CHECK(result.ok()) << result.status(); + auto stmt = std::move(result).value(); + auto *seq = static_cast<SequentialSentences *>(stmt.get()); + auto *lookup = static_cast<LookupSentence *>(seq->sentences()[0]); + return lookup->whereClause()->filter()->clone(); +} + +TEST_F(ExpressionUtilsTest, expandExpression) { + { + auto filter = parse("t1.c1 == 1"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(t1.c1==1)"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 and t1.c2 == 2"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((t1.c1==1) AND (t1.c2==2))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 and t1.c2 == 2 and t1.c3 == 3"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((t1.c1==1) AND (t1.c2==2)) AND (t1.c3==3))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 or t1.c2 == 2"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((t1.c1==1) OR (t1.c2==2))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 or t1.c2 == 2 or t1.c3 == 3"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((t1.c1==1) OR (t1.c2==2)) OR (t1.c3==3))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 and t1.c2 == 2 or t1.c1 == 3"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((t1.c1==1) AND (t1.c2==2)) OR (t1.c1==3))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 or t1.c2 == 2 and t1.c1 == 3"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((t1.c1==1) OR ((t1.c2==2) AND (t1.c1==3)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and t1.c3 == 3"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((t1.c1==1) AND (t1.c3==3)) OR ((t1.c2==2) AND (t1.c3==3)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and t1.c3 == 3 or t1.c4 == 4"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((((t1.c1==1) AND (t1.c3==3)) OR " + "((t1.c2==2) AND (t1.c3==3))) OR " + "(t1.c4==4))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and (t1.c3 == 3 or t1.c4 == 4)"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((((t1.c1==1) AND (t1.c3==3)) OR " + "((t1.c1==1) AND (t1.c4==4))) OR " + "((t1.c2==2) AND (t1.c3==3))) OR " + "((t1.c2==2) AND (t1.c4==4)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("(t1.c1 == 1 or t1.c2 == 2 or t1.c3 == 3 or t1.c4 == 4) " + "and t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((((t1.c1==1) AND (t1.c5==5)) OR " + "((t1.c2==2) AND (t1.c5==5))) OR " + "((t1.c3==3) AND (t1.c5==5))) OR " + "((t1.c4==4) AND (t1.c5==5)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and t1.c4 == 4 and t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((((t1.c1==1) AND (t1.c4==4)) AND (t1.c5==5)) OR " + "(((t1.c2==2) AND (t1.c4==4)) AND (t1.c5==5)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 and (t1.c2 == 2 or t1.c4 == 4) and t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((((t1.c1==1) AND (t1.c2==2)) AND (t1.c5==5)) OR " + "(((t1.c1==1) AND (t1.c4==4)) AND (t1.c5==5)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 and t1.c2 == 2 and (t1.c4 == 4 or t1.c5 == 5)"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((((t1.c1==1) AND (t1.c2==2)) AND (t1.c4==4)) OR " + "(((t1.c1==1) AND (t1.c2==2)) AND (t1.c5==5)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and " + "(t1.c3 == 3 or t1.c4 == 4) and " + "t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((((((t1.c1==1) AND (t1.c3==3)) AND (t1.c5==5)) OR " + "(((t1.c1==1) AND (t1.c4==4)) AND (t1.c5==5))) OR " + "(((t1.c2==2) AND (t1.c3==3)) AND (t1.c5==5))) OR " + "(((t1.c2==2) AND (t1.c4==4)) AND (t1.c5==5)))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c4 == 4 or (t1.c1 == 1 and (t1.c2 == 2 or t1.c3 == 3))"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((t1.c4==4) OR " + "(((t1.c1==1) AND (t1.c2==2)) OR " + "((t1.c1==1) AND (t1.c3==3))))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c4 == 4 or " + "(t1.c1 == 1 and (t1.c2 == 2 or t1.c3 == 3)) or " + "t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((t1.c4==4) OR " + "(((t1.c1==1) AND (t1.c2==2)) OR ((t1.c1==1) AND (t1.c3==3)))) OR " + "(t1.c5==5))"; + ASSERT_EQ(expected, target->toString()); + } + { + auto filter = parse("t1.c1 == 1 and (t1.c2 == 2 or t1.c4) and t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "((((t1.c1==1) AND (t1.c2==2)) AND (t1.c5==5)) OR " + "(((t1.c1==1) AND t1.c4) AND (t1.c5==5)))"; + ASSERT_EQ(expected, target->toString()); + } + { + // Invalid expression for index. don't need to expand. + auto filter = parse("t1.c1 == 1 and (t1.c2 == 2 or t1.c4) == true and t1.c5 == 5"); + auto target = ExpressionUtils::expandExpr(filter.get()); + auto expected = "(((t1.c1==1) AND (((t1.c2==2) OR t1.c4)==true)) AND (t1.c5==5))"; + ASSERT_EQ(expected, target->toString()); + } +} } // namespace graph } // namespace nebula diff --git a/src/validator/IndexScanValidator.cpp b/src/validator/IndexScanValidator.cpp index 45c400ec8a49cc5d247149d3ed21372a59c03e80..42ea9877dae7e5822adb2a57ec00f88ae9c34c1e 100644 --- a/src/validator/IndexScanValidator.cpp +++ b/src/validator/IndexScanValidator.cpp @@ -158,7 +158,7 @@ IndexScanValidator::rewriteTSFilter(Expression* expr) { isEmptyResultSet_ = true; return Status::OK(); } - std::vector<std::unique_ptr<RelationalExpression>> rels; + std::vector<std::unique_ptr<Expression>> rels; for (const auto& row : vRet.value()) { std::unique_ptr<RelationalExpression> r; if (isEdge_) {