Skip to content
Snippets Groups Projects
Commit 951b593f authored by 周子龙's avatar 周子龙
Browse files

optimizer: add PushFilterDownProjectRule

parent ce2442de
No related merge requests found
......@@ -22,6 +22,7 @@ nebula_add_library(
rule/LimitPushDownRule.cpp
rule/TopNRule.cpp
rule/PushFilterDownAggregateRule.cpp
rule/PushFilterDownProjectRule.cpp
)
nebula_add_subdirectory(test)
/* 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/PushFilterDownProjectRule.h"
#include "optimizer/OptContext.h"
#include "optimizer/OptGroup.h"
#include "planner/plan/PlanNode.h"
#include "planner/plan/Query.h"
#include "util/ExpressionUtils.h"
using nebula::graph::PlanNode;
using nebula::graph::QueryContext;
namespace nebula {
namespace opt {
std::unique_ptr<OptRule> PushFilterDownProjectRule::kInstance =
std::unique_ptr<PushFilterDownProjectRule>(new PushFilterDownProjectRule());
PushFilterDownProjectRule::PushFilterDownProjectRule() {
RuleSet::QueryRules().addRule(this);
}
const Pattern& PushFilterDownProjectRule::pattern() const {
static Pattern pattern = Pattern::create(graph::PlanNode::Kind::kFilter,
{Pattern::create(graph::PlanNode::Kind::kProject)});
return pattern;
}
StatusOr<OptRule::TransformResult> PushFilterDownProjectRule::transform(
OptContext* octx,
const MatchedResult& matched) const {
auto filterGroupNode = matched.node;
auto oldFilterNode = filterGroupNode->node();
auto projGroupNode = matched.dependencies.front().node;
auto oldProjNode = projGroupNode->node();
auto newFilterNode = static_cast<graph::Filter*>(oldFilterNode->clone());
auto newProjNode = static_cast<graph::Project*>(oldProjNode->clone());
const auto condition = newFilterNode->condition();
auto varProps = graph::ExpressionUtils::collectAll(condition, {Expression::Kind::kVarProperty});
if (varProps.empty()) {
return TransformResult::noTransform();
}
std::vector<std::string> propNames;
for (auto expr : varProps) {
DCHECK(expr->kind() == Expression::Kind::kVarProperty);
propNames.emplace_back(*static_cast<const VariablePropertyExpression*>(expr)->prop());
}
auto projColNames = newProjNode->colNames();
auto projColumns = newProjNode->columns()->columns();
for (size_t i = 0; i < projColNames.size(); ++i) {
auto column = projColumns[i];
auto colName = projColNames[i];
auto iter = std::find_if(propNames.begin(), propNames.end(), [&colName](const auto& name) {
return !colName.compare(name);
});
if (iter == propNames.end()) continue;
if (!column->alias() && column->expr()->kind() == Expression::Kind::kVarProperty) {
continue;
} else {
// project column contains computing expression, need to rewrite
return TransformResult::noTransform();
}
}
// Exchange planNode
newProjNode->setOutputVar(oldFilterNode->outputVar());
newFilterNode->setInputVar(oldProjNode->inputVar());
newProjNode->setInputVar(oldProjNode->outputVar());
newFilterNode->setOutputVar(oldProjNode->outputVar());
// Push down filter's optGroup and embed newProjGroupNode into old filter's Group
auto newProjGroupNode = OptGroupNode::create(octx, newProjNode, filterGroupNode->group());
auto newFilterGroup = OptGroup::create(octx);
auto newFilterGroupNode = newFilterGroup->makeGroupNode(newFilterNode);
newProjGroupNode->dependsOn(newFilterGroup);
for (auto dep : projGroupNode->dependencies()) {
newFilterGroupNode->dependsOn(dep);
}
TransformResult result;
result.eraseAll = true;
result.newGroupNodes.emplace_back(newProjGroupNode);
return result;
}
std::string PushFilterDownProjectRule::toString() const {
return "PushFilterDownProjectRule";
}
} // namespace opt
} // namespace nebula
/* 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_PUSHFILTERDOWNPROJECTRULE_H_
#define OPTIMIZER_RULE_PUSHFILTERDOWNPROJECTRULE_H_
#include <memory>
#include "optimizer/OptRule.h"
namespace nebula {
namespace opt {
class PushFilterDownProjectRule final : public OptRule {
public:
const Pattern &pattern() const override;
StatusOr<OptRule::TransformResult> transform(OptContext *qctx,
const MatchedResult &matched) const override;
std::string toString() const override;
private:
PushFilterDownProjectRule();
static std::unique_ptr<OptRule> kInstance;
};
} // namespace opt
} // namespace nebula
#endif // OPTIMIZER_RULE_PUSHFILTERDOWNPROJECTRULE_H_
# 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: Push Filter down Project rule
Background:
Given a graph with space named "nba"
Scenario: push filter down Project
When profiling query:
"""
MATCH (a:player)--(b)--(c)
WITH a AS a, b AS b, c AS c
WHERE a.age < 25 AND b.age < 25 AND c.age < 25
RETURN a
"""
Then the result should be, in any order:
| a |
| ("Kristaps Porzingis" :player{age: 23, name: "Kristaps Porzingis"}) |
| ("Kristaps Porzingis" :player{age: 23, name: "Kristaps Porzingis"}) |
| ("Luka Doncic" :player{age: 20, name: "Luka Doncic"}) |
| ("Luka Doncic" :player{age: 20, name: "Luka Doncic"}) |
And the execution plan should be:
| id | name | dependencies | operator info |
| 23 | Project | 40 | |
| 40 | Project | 39 | |
| 39 | Filter | 20 | |
| 20 | Filter | 19 | |
| 19 | Project | 18 | |
| 18 | InnerJoin | 17 | |
| 17 | Project | 28 | |
| 28 | GetVertices | 13 | |
| 13 | InnerJoin | 12 | |
| 12 | Filter | 11 | |
| 11 | Project | 32 | |
| 32 | GetNeighbors | 7 | |
| 7 | Filter | 6 | |
| 6 | Project | 5 | |
| 5 | Filter | 31 | |
| 31 | GetNeighbors | 24 | |
| 24 | IndexScan | 0 | |
| 0 | Start | | |
When profiling query:
"""
MATCH (a:player)--(b)--(c)
WITH a, b, c.age+1 AS cage
WHERE a.name == 'Tim Duncan' AND b.age > 40
RETURN DISTINCT a, b, cage
"""
Then the result should be, in any order:
| a | b | cage |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"}) | 39 |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"}) | 32 |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"}) | NULL |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | NULL |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | 43 |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | 37 |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | 35 |
| ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | 30 |
And the execution plan should be:
| id | name | dependencies | operator info |
| 25 | DataCollect | 24 | |
| 24 | Dedup | 41 | |
| 41 | Project | 40 | |
| 40 | Filter | 20 | |
| 20 | Filter | 19 | |
| 19 | Project | 18 | |
| 18 | InnerJoin | 17 | |
| 17 | Project | 29 | |
| 29 | GetVertices | 13 | |
| 13 | InnerJoin | 12 | |
| 12 | Filter | 11 | |
| 11 | Project | 33 | |
| 33 | GetNeighbors | 7 | |
| 7 | Filter | 6 | |
| 6 | Project | 5 | |
| 5 | Filter | 32 | |
| 32 | GetNeighbors | 26 | |
| 26 | IndexScan | 0 | |
| 0 | Start | | |
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment