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

[OptRule] Collapse continuous Project plan node #1282

Merged
merged 2 commits into from
Jul 28, 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
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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Emmm, a pattern is always not enough to understand the details, so we better add some comment for the opt rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already added.

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 | |
Comment on lines +35 to +36
Copy link
Contributor

Choose a reason for hiding this comment

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

could these projects not be collapsed ?

Copy link
Contributor Author

@czpmango czpmango Jul 23, 2021

Choose a reason for hiding this comment

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

In order to avoid multiple calculations of the expression($-._path referenced twice in the following project node), this situation was disabled.

| 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