-
Notifications
You must be signed in to change notification settings - Fork 132
expand logic expression for index scan #465
expand logic expression for index scan #465
Conversation
Interesting, this really looks like math.... |
Well done. After expand some expression maybe evaluate multiple times, so how do you treat the expression with side affection (or which expression not pure). |
yeah, you are right . used for index scan in storage layer. An OR logic corresponds to an IndexQueryContext. |
You mean how do make sure correct of sub expressions after expanded? |
if (ops[0]->kind() == Expression::Kind::kLogicalOr || | ||
ops[1]->kind() == Expression::Kind::kLogicalOr) { | ||
return expandExpr(target[0].get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you give an example when do we hit this path? I suppose all expression of op has been expanded in expandImplAnd
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you give an example when do we hit this path? I suppose all expression of op has been expanded in
expandImplAnd
.
t1.c1 == 1 and t1.c2 == 2 and (t1.c4 == 4 or t1.c5 == 5)
nebula::graph::ExpressionUtils::expandExpr ExpressionUtils.cpp:135
nebula::graph::ExpressionUtils::expandImplAnd ExpressionUtils.cpp:156
nebula::graph::ExpressionUtils::expandExpr ExpressionUtils.cpp:121
nebula::graph::ExpressionUtilsTest_expandExpression_Test::TestBody ExpressionUtilsTest.cpp:507
void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) 0x0000000002f58ce3
void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) 0x0000000002f5340c
testing::Test::Run() 0x0000000002f38e9c
testing::TestInfo::Run() 0x0000000002f39718
testing::TestCase::Run() 0x0000000002f39d69
testing::internal::UnitTestImpl::RunAllTests() 0x0000000002f4089a
bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) 0x0000000002f59dea
bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) 0x0000000002f541f0
testing::UnitTest::Run() 0x0000000002f3f5a0
RUN_ALL_TESTS() 0x0000000002f65fdc
main 0x0000000002f65f6a
__libc_start_main 0x00007ffff7c86f43
_start 0x0000000001e0142e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Convert AB(C + D)
to ABC + ABD)
Yes In your example t1.c1 will expand from one to two (evaluate twice). |
I understand your question,don't need to evaluate it twice,for example : for index scan, will be executed twice, don't need to expression.eval() |
Yes, if you check the t1.c1 is just a attribute expression. |
Actually, I am really confused about the logical expression unfolding implementation. I think you can implement the logic recursively like following pseudo codes: Expression *expandAnd(Expression *root) {
if (root->kind() != Expression::Kind::kAnd) {
return root->clone();
}
auto operands = root->operands();
// (A OR B) AND C => (A AND C) OR (B AND C)
if (operands[0]->kind() == Expression::Kind::kOr) {
auto orLeftChild = operands[0]->operands[0];
auto orRightChild = operands[0]->operands[1];
auto left = new LogicalExpression(Kind::kAnd, orLeftChild, operands[1]);
auto right = new LogicalExpression(Kind::kAnd, orRightChild, operands[1]);
return new LogicalExpression(Kind::kOr, expandAnd(left), expandAnd(right));
}
// A AND (B OR C) => (A AND B) OR (A AND C)
if (operands[1]->kind() == Expression::Kind::kOr) {
auto orLeftChild = operands[1]->operands[0];
auto orRightChild = operands[1]->operands[1];
auto left = new LogicalExpression(Kind::kAnd, orLeftChild, operands[0]);
auto right = new LogicalExpression(Kind::kAnd, orRightChild, operands[0]);
return new LogicalExpression(Kind::kOr, expandAnd(left), expandAnd(right));
}
return root->expr();
}
|
Interesting logic, How to deal with the following logic? |
Similar logic, only change the or Logical expression construction like this: // (A OR B OR C OR D) AND E => (A AND E) OR (B AND E) OR (C AND E) OR (D AND E)
if (operands[0]->kind() == Expression::Kind::kOr) {
auto leftChildOperands = operands[0]->operands();
auto newRoot = new LogicalExpression(Kind::kOr);
for (auto& left : leftChildOperands) {
auto operand = new LogicalExpression(Kind::kAnd, left, operands[1]);
newRoot->addOperand(expandAnd(operand));
}
return newRoot;
}
|
Actually, I don't quite understand the logic of this pseudo code, According to this pseudo code logic, the output should be: |
I have no doubt for the implementation, LGTM. I believe the problem comes from the expression may have more than two operands, this would make code not so elegant. |
I rethink the expr traversal logic from top to bottom, there's some errors for the case I have no problems about this PR. LGTM. |
Analyze complex expression and expand the expression for index scan. for example :
(t1.c1 == 1 or t1.c2 == 2) and (t1.c3 == 3 or t1.c4 == 4)
Will be converted to:
(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)