Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExtractFilterExprVisitor #3462

Merged
merged 6 commits into from
Dec 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/common/expression/LogicalExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ class LogicalExpression final : public Expression {
return true;
}

void reverseLogicalKind() {
if (kind_ == Kind::kLogicalAnd) {
kind_ = Kind::kLogicalOr;
} else if (kind_ == Kind::kLogicalOr) {
kind_ = Kind::kLogicalAnd;
} else {
LOG(FATAL) << "Should not reverse logical expression except and/or kind.";
}
}

Aiee marked this conversation as resolved.
Show resolved Hide resolved
private:
explicit LogicalExpression(ObjectPool* pool, Kind kind) : Expression(pool, kind) {}

Expand Down
220 changes: 205 additions & 15 deletions src/graph/visitor/ExtractFilterExprVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include "ExtractFilterExprVisitor.h"

#include "graph/util/ExpressionUtils.h"

namespace nebula {
namespace graph {

Expand Down Expand Up @@ -138,35 +140,78 @@ void ExtractFilterExprVisitor::visit(ColumnExpression *) {
canBePushed_ = false;
}

void ExtractFilterExprVisitor::visit(LogicalExpression *expr) {
if (expr->kind() != Expression::Kind::kLogicalAnd) {
ExprVisitorImpl::visit(expr);
return;
// @return: whether this logical expr satisfies split condition
bool ExtractFilterExprVisitor::visitLogicalAnd(LogicalExpression *expr, std::vector<bool> &flags) {
DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalAnd);
// if the size of operands greater than 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalAnd);

// then we think it has been pullAnds before
if (expr->operands().size() <= 2) {
ExpressionUtils::pullAnds(expr);
}

auto &operands = expr->operands();
std::vector<bool> flags(operands.size(), false);
auto canBePushed = false;
for (auto i = 0u; i < operands.size(); i++) {
bool allOpCanBePushed = true;
auto size = operands.size();
flags.resize(operands.size(), false);
for (auto i = 0u; i < size; i++) {
canBePushed_ = true;
operands[i]->accept(this);
if (operands[i]->kind() == Expression::Kind::kLogicalOr) {
auto lgExpr = static_cast<LogicalExpression *>(operands[i]);
bool isSplit = visitLogicalOr(lgExpr);
if (isSplit) {
// if split, the size of subOperands must be 2,
// subOperands[0] must can be pushed expr
// subOperands[1] must can not push expr
auto &subOperands = lgExpr->operands();
DCHECK_EQ(subOperands.size(), 2);
expr->setOperand(i, subOperands[0]->clone());
expr->addOperand(subOperands[1]->clone());
flags.emplace_back(false);
allOpCanBePushed = false;
}
} else {
operands[i]->accept(this);
}
flags[i] = canBePushed_;
allOpCanBePushed = allOpCanBePushed && canBePushed_;
canBePushed = canBePushed || canBePushed_;
}
canBePushed_ = canBePushed;
if (!canBePushed_) {
return;
// all operands can not be pushed
return false;
}
if (isNested_) {
// if canBePushed is true and allOpCanBePushed is false
// it means some of the operands can not be pushed and can be split
canBePushed_ = allOpCanBePushed;
return !allOpCanBePushed;
}
if (allOpCanBePushed) {
return false;
}
ExtractRemainExpr(expr, flags);
return false;
}

void ExtractFilterExprVisitor::ExtractRemainExpr(LogicalExpression *expr,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalAnd);

std::vector<bool> &flags) {
DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalAnd);
auto &operands = expr->operands();
std::vector<Expression *> remainedOperands;
auto lastUsedExprInd = 0u;
for (auto i = 0u; i < operands.size(); i++) {
if (!flags[i]) {
remainedOperands.emplace_back(operands[i]->clone());
expr->setOperand(i, ConstantExpression::make(pool_, true));
} else {
if (lastUsedExprInd < i) {
expr->setOperand(lastUsedExprInd, operands[i]);
}
lastUsedExprInd++;
}
}
if (remainedOperands.empty()) {
return;
}

operands.resize(lastUsedExprInd);
if (remainedOperands.size() > 1) {
auto remainedExpr = LogicalExpression::makeAnd(pool_);
remainedExpr->setOperands(std::move(remainedOperands));
Expand All @@ -176,9 +221,154 @@ void ExtractFilterExprVisitor::visit(LogicalExpression *expr) {
}
}

void ExtractFilterExprVisitor::visit(SubscriptRangeExpression *) {
canBePushed_ = false;
// @return: split or not
bool ExtractFilterExprVisitor::visitLogicalOr(LogicalExpression *expr) {
DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalOr);
if (expr->operands().size() <= 2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalOr);

ExpressionUtils::pullOrs(expr);
}
// For simplification,
// LogicalOr can be split,
// if and only if just one operand can not be pushed and that operand is LogicalAnd.
// splited means:
// canBePush1 or (canBePush2 and ... and canNotBePush3)
// => (canBePush1 or canBePush2) and ... and (canBePush1 or canNotBePush3)
auto &operands = expr->operands();
int canNotPushedIndex = -1;
std::vector<bool> flags;
canBePushed_ = true;
for (auto i = 0u; i < operands.size(); i++) {
if (operands[i]->kind() == Expression::Kind::kLogicalAnd) {
auto lgExpr = static_cast<LogicalExpression *>(operands[i]);
std::vector<bool> flag;
bool isNested = isNested_;
isNested_ = true;
bool canBeSplit = visitLogicalAnd(lgExpr, flag);
isNested_ = isNested;
if (!canBePushed_) {
// if the LogicalAnd can not be split, return
if (canNotPushedIndex != -1 || !canBeSplit) {
return false;
}
canNotPushedIndex = i;
flags = std::move(flag);
}
} else {
operands[i]->accept(this);
if (!canBePushed_) {
return false;
}
}
}

if (canNotPushedIndex == -1 || splitForbidden) {
canBePushed_ = (canNotPushedIndex == -1);
return false;
}
// only one operand, which is LogicalAnd expr, can not be pushed. Split it.
splitOrExpr(expr, flags, canNotPushedIndex);
return canBePushed_;
}

void ExtractFilterExprVisitor::splitOrExpr(LogicalExpression *expr,
std::vector<bool> &flags,
const unsigned int canNotPushedIndex) {
auto &operands = expr->operands();
auto andExpr = operands[canNotPushedIndex];
if (andExpr->kind() != Expression::Kind::kLogicalAnd) {
canBePushed_ = false;
return;
}
auto &andOperands = static_cast<LogicalExpression *>(andExpr)->operands();

std::vector<Expression *> canBePushExprs;
std::vector<Expression *> canNotPushExprs;

for (auto i = 0u; i < andOperands.size(); i++) {
if (flags[i]) {
canBePushExprs.emplace_back(andOperands[i]->clone());
} else {
canNotPushExprs.emplace_back(andOperands[i]->clone());
}
}

DCHECK(!canBePushExprs.empty());
DCHECK(!canNotPushExprs.empty());

hasSplit = true;
canBePushed_ = true;
std::vector<Expression *> sharedExprs;
for (auto i = 0u; i < operands.size(); i++) {
if (i != canNotPushedIndex) {
sharedExprs.emplace_back(operands[i]->clone());
}
}
std::vector<Expression *> newExprs;
newExprs.emplace_back(rewriteExpr(canBePushExprs, sharedExprs));
newExprs.emplace_back(rewriteExpr(canNotPushExprs, sharedExprs));

expr->setOperands(std::move(newExprs));
expr->reverseLogicalKind();
}

Expression *ExtractFilterExprVisitor::rewriteExpr(std::vector<Expression *> rel,
std::vector<Expression *> sharedExprs) {
Expression *newAndExpr;
DCHECK(!rel.empty());
if (rel.size() > 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DCHECK(!rel.empty())

newAndExpr = ExpressionUtils::pushAnds(pool_, rel);
ExpressionUtils::pullAnds(newAndExpr);
} else {
newAndExpr = rel[0];
}
if (sharedExprs.empty()) {
return newAndExpr;
}
sharedExprs.emplace_back(newAndExpr);
auto newOrExpr = ExpressionUtils::pushOrs(pool_, sharedExprs);
ExpressionUtils::pullOrs(newOrExpr);
return newOrExpr;
}

// It's recommended to see ExtractFilterExprVisitorTest.cpp to figure out how it works
void ExtractFilterExprVisitor::visit(LogicalExpression *expr) {
if (expr->operands().size() == 1) {
expr->operands()[0]->accept(this);
return;
}
LogicalExpression *originalExpr = static_cast<LogicalExpression *>(expr->clone());
if (expr->kind() == Expression::Kind::kLogicalAnd) {
std::vector<bool> unUsed;
visitLogicalAnd(expr, unUsed);
} else if (expr->kind() == Expression::Kind::kLogicalOr) {
bool isSplit = visitLogicalOr(expr);
if (isSplit) {
DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalAnd);
DCHECK_EQ(expr->operands().size(), 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalAnd);

remainedExpr_ = expr->operands()[1]->clone();
expr->operands().pop_back();
}
} else {
isNested_ = true;
splitForbidden = true;
ExprVisitorImpl::visit(expr);
}

if (hasSplit && !canBePushed_) {
std::vector<Expression *> operands;
for (auto &operand : originalExpr->operands()) {
operands.emplace_back(operand->clone());
}
expr->setOperands(std::move(operands));
if (expr->kind() != originalExpr->kind()) {
expr->reverseLogicalKind();
}
remainedExpr_ = nullptr;
}
return;
}

void ExtractFilterExprVisitor::visit(SubscriptRangeExpression *) { canBePushed_ = false; }

} // namespace graph
} // namespace nebula
11 changes: 11 additions & 0 deletions src/graph/visitor/ExtractFilterExprVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,20 @@ class ExtractFilterExprVisitor final : public ExprVisitorImpl {
kGetVertices, // Get/Append/Scan Vertices
kGetEdges, // Get/Append/Scan Edges
};
bool visitLogicalAnd(LogicalExpression *expr, std::vector<bool> &flags);
bool visitLogicalOr(LogicalExpression *expr);
void splitOrExpr(LogicalExpression *expr,
std::vector<bool> &flags,
const unsigned int canNotPushedIndex);
// void rewriteAndExpr(Expression *rewriteExpr);
Expression *rewriteExpr(std::vector<Expression *> rel, std::vector<Expression *> sharedExprs);
void ExtractRemainExpr(LogicalExpression *expr, std::vector<bool> &flags);

ObjectPool *pool_;
bool canBePushed_{true};
bool isNested_{false};
bool hasSplit{false};
bool splitForbidden{false};
Expression *remainedExpr_{nullptr};
PushType pushType_{PushType::kGetNeighbors};
};
Expand Down
Loading