Skip to content
This repository has been archived by the owner on Dec 1, 2022. It is now read-only.

Commit

Permalink
combine projects (#1282)
Browse files Browse the repository at this point in the history
  • Loading branch information
czpmango authored Jul 28, 2021
1 parent 1dc1749 commit c6f702d
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/optimizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ nebula_add_library(
rule/PushFilterDownGetNbrsRule.cpp
rule/RemoveNoopProjectRule.cpp
rule/CombineFilterRule.cpp
rule/CollapseProjectRule.cpp
rule/MergeGetVerticesAndDedupRule.cpp
rule/MergeGetVerticesAndProjectRule.cpp
rule/MergeGetNbrsAndDedupRule.cpp
Expand Down
121 changes: 121 additions & 0 deletions src/optimizer/rule/CollapseProjectRule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* Copyright (c) 2021 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/

#include "optimizer/rule/CollapseProjectRule.h"

#include "optimizer/OptContext.h"
#include "optimizer/OptGroup.h"
#include "planner/plan/PlanNode.h"
#include "planner/plan/Query.h"

using nebula::graph::PlanNode;
using nebula::graph::QueryContext;

namespace nebula {
namespace opt {

std::unique_ptr<OptRule> CollapseProjectRule::kInstance =
std::unique_ptr<CollapseProjectRule>(new CollapseProjectRule());

CollapseProjectRule::CollapseProjectRule() {
RuleSet::QueryRules().addRule(this);
}

const Pattern& CollapseProjectRule::pattern() const {
static Pattern pattern = Pattern::create(graph::PlanNode::Kind::kProject,
{Pattern::create(graph::PlanNode::Kind::kProject)});
return pattern;
}

StatusOr<OptRule::TransformResult> CollapseProjectRule::transform(
OptContext* octx,
const MatchedResult& matched) const {
const auto* groupNodeAbove = matched.node;
const auto* projAbove = groupNodeAbove->node();
DCHECK_EQ(projAbove->kind(), PlanNode::Kind::kProject);
const auto& deps = matched.dependencies;
DCHECK_EQ(deps.size(), 1);
const auto* groupNodeBelow = deps[0].node;
const auto* projBelow = groupNodeBelow->node();
DCHECK_EQ(projBelow->kind(), PlanNode::Kind::kProject);
std::vector<YieldColumn*> colsBelow =
static_cast<const graph::Project*>(projBelow)->columns()->columns();
const auto* projGroup = groupNodeAbove->group();

auto* newProj = static_cast<graph::Project*>(projAbove->clone());
std::vector<YieldColumn*> colsAbove = newProj->columns()->columns();

// 1. collect all property reference
std::vector<std::string> allPropRefNames;
for (auto col : colsAbove) {
std::vector<const Expression*> propRefs = graph::ExpressionUtils::collectAll(
col->expr(), {Expression::Kind::kVarProperty, Expression::Kind::kInputProperty});
for (auto* expr : propRefs) {
DCHECK(expr->kind() == Expression::Kind::kVarProperty ||
expr->kind() == Expression::Kind::kInputProperty);
allPropRefNames.emplace_back(static_cast<const PropertyExpression*>(expr)->prop());
}
}

// disable this case to avoid the expression in ProjBelow being eval multiple times
std::unordered_set<std::string> uniquePropRefNames;
for (auto p : allPropRefNames) {
if (!uniquePropRefNames.insert(p).second) {
return TransformResult::noTransform();
}
}

// 2. find link according to propRefNames and colNames in ProjBelow
std::unordered_map<std::string, Expression*> rewriteMap;
auto colNames = projBelow->colNames();
for (size_t i = 0; i < colNames.size(); ++i) {
if (uniquePropRefNames.count(colNames[i])) {
rewriteMap[colNames[i]] = colsBelow[i]->expr();
}
}

// 3. rewrite YieldColumns
auto matcher = [&rewriteMap](const Expression* e) -> bool {
if (e->kind() != Expression::Kind::kVarProperty &&
e->kind() != Expression::Kind::kInputProperty) {
return false;
}
auto& propName = static_cast<const PropertyExpression*>(e)->prop();
return rewriteMap.find(propName) != rewriteMap.end();
};
auto rewriter = [&rewriteMap](const Expression* e) -> Expression* {
DCHECK(e->kind() == Expression::Kind::kVarProperty ||
e->kind() == Expression::Kind::kInputProperty);
auto& propName = static_cast<const PropertyExpression*>(e)->prop();
return rewriteMap[propName]->clone();
};
for (auto col : colsAbove) {
auto* newColExpr = graph::RewriteVisitor::transform(col->expr(), matcher, rewriter);
col->setExpr(newColExpr);
}

// 4. rebuild OptGroupNode
newProj->setInputVar(projBelow->inputVar());
auto* newGroupNode = OptGroupNode::create(octx, newProj, projGroup);
newGroupNode->setDeps(groupNodeBelow->dependencies());

TransformResult result;
result.eraseAll = true;
result.newGroupNodes.emplace_back(newGroupNode);

return result;
}

bool CollapseProjectRule::match(OptContext* octx, const MatchedResult& matched) const {
return OptRule::match(octx, matched);
}

std::string CollapseProjectRule::toString() const {
return "CollapseProjectRule";
}

} // namespace opt
} // namespace nebula
47 changes: 47 additions & 0 deletions src/optimizer/rule/CollapseProjectRule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Copyright (c) 2021 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/

#ifndef OPTIMIZER_RULE_COLLAPSEPROJECTRULE_H_
#define OPTIMIZER_RULE_COLLAPSEPROJECTRULE_H_

#include <memory>

#include "optimizer/OptRule.h"

namespace nebula {
namespace opt {

/**
* Combines two [[Project]] nodes into one
* Required conditions:
* 1. Match the pattern
* 2. Expressions between nodes cannot be referenced more than once
* Benefits:
* 1. reduce the copy of memory between nodes
* 2. reduces expression overhead in some cases(similar to column pruning)
*/

class CollapseProjectRule final : public OptRule {
public:
const Pattern &pattern() const override;

StatusOr<TransformResult> transform(OptContext *ctx,
const MatchedResult &matched) const override;

bool match(OptContext *ctx, const MatchedResult &matched) const override;

std::string toString() const override;

private:
CollapseProjectRule();

static std::unique_ptr<OptRule> kInstance;
};

} // namespace opt
} // namespace nebula

#endif // OPTIMIZER_RULE_COLLAPSEPROJECTRULE_H_
2 changes: 1 addition & 1 deletion src/optimizer/rule/PushFilterDownAggregateRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ StatusOr<OptRule::TransformResult> PushFilterDownAggregateRule::transform(
return false;
}
auto& propName = static_cast<const VariablePropertyExpression*>(e)->prop();
return rewriteMap[propName];
return rewriteMap.find(propName) != rewriteMap.end();
};
auto rewriter = [&rewriteMap](const Expression* e) -> Expression* {
DCHECK_EQ(e->kind(), Expression::Kind::kVarProperty);
Expand Down
54 changes: 54 additions & 0 deletions tests/tck/features/optimizer/CollapseProjectRule.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (c) 2021 vesoft inc. All rights reserved.
#
# This source code is licensed under Apache 2.0 License,
# attached with Common Clause Condition 1.0, found in the LICENSES directory.
Feature: Collapse Project Rule

Background:
Given a graph with space named "nba"

Scenario: Collapse Project
When profiling query:
"""
MATCH (v:player)
WHERE v.age > 38
WITH v, v.age AS age, v.age/10 AS iage, v.age%10 AS mage,v.name AS name
RETURN iage
"""
Then the result should be, in any order:
| iage |
| 4 |
| 4 |
| 4 |
| 4 |
| 4 |
| 4 |
| 4 |
| 4 |
| 4 |
| 4 |
| 3 |
And the execution plan should be:
| id | name | dependencies | operator info |
| 16 | Project | 14 | |
| 14 | Filter | 7 | |
| 7 | Project | 6 | |
| 6 | Project | 5 | |
| 5 | Filter | 18 | |
| 18 | GetVertices | 12 | |
| 12 | IndexScan | 0 | |
| 0 | Start | | |
When profiling query:
"""
LOOKUP ON player
WHERE player.name=='Tim Duncan'
| YIELD $-.VertexID AS vid
"""
Then the result should be, in any order:
| vid |
| "Tim Duncan" |
And the execution plan should be:
| id | name | dependencies | operator info |
| 6 | Project | 5 | |
| 5 | TagIndexPrefixScan | 0 | |
| 0 | Start | | |
37 changes: 18 additions & 19 deletions tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,24 @@ Feature: Push Filter down LeftJoin rule
| "Tim Duncan" |
And the execution plan should be:
| id | name | dependencies | operator info |
| 22 | Project | 21 | |
| 21 | Filter | 20 | |
| 20 | InnerJoin | 19 | |
| 19 | LeftJoin | 18 | |
| 18 | Project | 17 | |
| 17 | GetVertices | 16 | |
| 16 | Project | 28 | |
| 28 | GetNeighbors | 12 | |
| 12 | Project | 11 | |
| 11 | Filter | 10 | |
| 10 | InnerJoin | 9 | |
| 9 | LeftJoin | 8 | |
| 8 | Project | 7 | |
| 7 | GetVertices | 6 | |
| 6 | Project | 27 | |
| 27 | GetNeighbors | 2 | |
| 2 | Project | 3 | |
| 3 | Project | 30 | |
| 30 | TagIndexPrefixScan | 0 | |
| 24 | Project | 23 | |
| 23 | Filter | 22 | |
| 22 | InnerJoin | 21 | |
| 21 | LeftJoin | 20 | |
| 20 | Project | 19 | |
| 19 | GetVertices | 18 | |
| 18 | Project | 31 | |
| 31 | GetNeighbors | 14 | |
| 14 | Project | 13 | |
| 13 | Filter | 12 | |
| 12 | InnerJoin | 11 | |
| 11 | LeftJoin | 10 | |
| 10 | Project | 9 | |
| 9 | GetVertices | 8 | |
| 8 | Project | 30 | |
| 30 | GetNeighbors | 27 | |
| 27 | Project | 25 | |
| 25 | TagIndexPrefixScan | 0 | |
| 0 | Start | | |
When profiling query:
"""
Expand Down

0 comments on commit c6f702d

Please sign in to comment.