Skip to content

Commit

Permalink
ExtractFilterExprVisitor
Browse files Browse the repository at this point in the history
  • Loading branch information
sworduo committed Dec 17, 2021
1 parent 58914a7 commit cbe4c34
Show file tree
Hide file tree
Showing 4 changed files with 579 additions and 14 deletions.
10 changes: 10 additions & 0 deletions src/common/expression/LogicalExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ class LogicalExpression final : public Expression {

bool isLogicalExpr() const override { 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.";
}
}

private:
explicit LogicalExpression(ObjectPool* pool, Kind kind) : Expression(pool, kind) {}

Expand Down
218 changes: 205 additions & 13 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 @@ -118,35 +120,78 @@ void ExtractFilterExprVisitor::visit(EdgeExpression *) { canBePushed_ = false; }

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
// 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,
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 @@ -156,6 +201,153 @@ void ExtractFilterExprVisitor::visit(LogicalExpression *expr) {
}
}

// @return: split or not
bool ExtractFilterExprVisitor::visitLogicalOr(LogicalExpression *expr) {
DCHECK_EQ(expr->kind(), Expression::Kind::kLogicalOr);
if (expr->operands().size() <= 2) {
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) {
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);
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
Expand Down
11 changes: 11 additions & 0 deletions src/graph/visitor/ExtractFilterExprVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,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

0 comments on commit cbe4c34

Please sign in to comment.