Skip to content

Commit 0a060ff

Browse files
caton-hpgczpmangoSophie-Xie
authored
add PushFilterDownInnerJoinRule (#4599)
* add PushFilterDownInnerJoinRule * update tck Co-authored-by: kyle.cao <kyle.cao@vesoft.com> Co-authored-by: Sophie <84560950+Sophie-Xie@users.noreply.github.com>
1 parent fc5735a commit 0a060ff

5 files changed

+243
-11
lines changed

src/graph/optimizer/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ nebula_add_library(
2727
rule/PushFilterDownAggregateRule.cpp
2828
rule/PushFilterDownProjectRule.cpp
2929
rule/PushFilterDownLeftJoinRule.cpp
30+
rule/PushFilterDownInnerJoinRule.cpp
3031
rule/PushFilterDownNodeRule.cpp
3132
rule/PushFilterDownScanVerticesRule.cpp
3233
rule/PushVFilterDownScanVerticesRule.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/* Copyright (c) 2022 vesoft inc. All rights reserved.
2+
*
3+
* This source code is licensed under Apache 2.0 License.
4+
*/
5+
6+
#include "graph/optimizer/rule/PushFilterDownInnerJoinRule.h"
7+
8+
#include "graph/optimizer/OptContext.h"
9+
#include "graph/optimizer/OptGroup.h"
10+
#include "graph/planner/plan/PlanNode.h"
11+
#include "graph/planner/plan/Query.h"
12+
#include "graph/util/ExpressionUtils.h"
13+
14+
using nebula::graph::PlanNode;
15+
using nebula::graph::QueryContext;
16+
17+
namespace nebula {
18+
namespace opt {
19+
20+
std::unique_ptr<OptRule> PushFilterDownInnerJoinRule::kInstance =
21+
std::unique_ptr<PushFilterDownInnerJoinRule>(new PushFilterDownInnerJoinRule());
22+
23+
PushFilterDownInnerJoinRule::PushFilterDownInnerJoinRule() {
24+
RuleSet::QueryRules().addRule(this);
25+
}
26+
27+
const Pattern& PushFilterDownInnerJoinRule::pattern() const {
28+
static Pattern pattern = Pattern::create(graph::PlanNode::Kind::kFilter,
29+
{Pattern::create(graph::PlanNode::Kind::kInnerJoin)});
30+
return pattern;
31+
}
32+
33+
StatusOr<OptRule::TransformResult> PushFilterDownInnerJoinRule::transform(
34+
OptContext* octx, const MatchedResult& matched) const {
35+
auto* filterGroupNode = matched.node;
36+
auto* oldFilterNode = filterGroupNode->node();
37+
auto deps = matched.dependencies;
38+
DCHECK_EQ(deps.size(), 1);
39+
auto innerJoinGroupNode = deps.front().node;
40+
auto* innerJoinNode = innerJoinGroupNode->node();
41+
DCHECK_EQ(oldFilterNode->kind(), PlanNode::Kind::kFilter);
42+
DCHECK_EQ(innerJoinNode->kind(), PlanNode::Kind::kInnerJoin);
43+
auto* oldInnerJoinNode = static_cast<graph::InnerJoin*>(innerJoinNode);
44+
const auto* condition = static_cast<graph::Filter*>(oldFilterNode)->condition();
45+
DCHECK(condition);
46+
const std::pair<std::string, int64_t>& leftVar = oldInnerJoinNode->leftVar();
47+
auto symTable = octx->qctx()->symTable();
48+
std::vector<std::string> leftVarColNames = symTable->getVar(leftVar.first)->colNames;
49+
50+
// split the `condition` based on whether the varPropExpr comes from the left
51+
// child
52+
auto picker = [&leftVarColNames](const Expression* e) -> bool {
53+
auto varProps = graph::ExpressionUtils::collectAll(e, {Expression::Kind::kVarProperty});
54+
if (varProps.empty()) {
55+
return false;
56+
}
57+
std::vector<std::string> propNames;
58+
for (auto* expr : varProps) {
59+
DCHECK(expr->kind() == Expression::Kind::kVarProperty);
60+
propNames.emplace_back(static_cast<const VariablePropertyExpression*>(expr)->prop());
61+
}
62+
for (auto prop : propNames) {
63+
auto iter = std::find_if(leftVarColNames.begin(),
64+
leftVarColNames.end(),
65+
[&prop](std::string item) { return !item.compare(prop); });
66+
if (iter == leftVarColNames.end()) {
67+
return false;
68+
}
69+
}
70+
return true;
71+
};
72+
Expression* filterPicked = nullptr;
73+
Expression* filterUnpicked = nullptr;
74+
graph::ExpressionUtils::splitFilter(condition, picker, &filterPicked, &filterUnpicked);
75+
76+
if (!filterPicked) {
77+
return TransformResult::noTransform();
78+
}
79+
80+
// produce new left Filter node
81+
auto* newLeftFilterNode =
82+
graph::Filter::make(octx->qctx(),
83+
const_cast<graph::PlanNode*>(oldInnerJoinNode->dep()),
84+
graph::ExpressionUtils::rewriteInnerVar(filterPicked, leftVar.first));
85+
newLeftFilterNode->setInputVar(leftVar.first);
86+
newLeftFilterNode->setColNames(leftVarColNames);
87+
auto newFilterGroup = OptGroup::create(octx);
88+
auto newFilterGroupNode = newFilterGroup->makeGroupNode(newLeftFilterNode);
89+
for (auto dep : innerJoinGroupNode->dependencies()) {
90+
newFilterGroupNode->dependsOn(dep);
91+
}
92+
auto newLeftFilterOutputVar = newLeftFilterNode->outputVar();
93+
94+
// produce new InnerJoin node
95+
auto* newInnerJoinNode = static_cast<graph::InnerJoin*>(oldInnerJoinNode->clone());
96+
newInnerJoinNode->setLeftVar({newLeftFilterOutputVar, 0});
97+
const std::vector<Expression*>& hashKeys = oldInnerJoinNode->hashKeys();
98+
std::vector<Expression*> newHashKeys;
99+
for (auto* k : hashKeys) {
100+
newHashKeys.emplace_back(graph::ExpressionUtils::rewriteInnerVar(k, newLeftFilterOutputVar));
101+
}
102+
newInnerJoinNode->setHashKeys(newHashKeys);
103+
104+
TransformResult result;
105+
result.eraseAll = true;
106+
if (filterUnpicked) {
107+
auto* newAboveFilterNode = graph::Filter::make(octx->qctx(), newInnerJoinNode);
108+
newAboveFilterNode->setOutputVar(oldFilterNode->outputVar());
109+
newAboveFilterNode->setCondition(filterUnpicked);
110+
auto newAboveFilterGroupNode =
111+
OptGroupNode::create(octx, newAboveFilterNode, filterGroupNode->group());
112+
113+
auto newInnerJoinGroup = OptGroup::create(octx);
114+
auto newInnerJoinGroupNode = newInnerJoinGroup->makeGroupNode(newInnerJoinNode);
115+
newAboveFilterGroupNode->setDeps({newInnerJoinGroup});
116+
newInnerJoinGroupNode->setDeps({newFilterGroup});
117+
result.newGroupNodes.emplace_back(newAboveFilterGroupNode);
118+
} else {
119+
newInnerJoinNode->setOutputVar(oldFilterNode->outputVar());
120+
newInnerJoinNode->setColNames(oldInnerJoinNode->colNames());
121+
auto newInnerJoinGroupNode =
122+
OptGroupNode::create(octx, newInnerJoinNode, filterGroupNode->group());
123+
newInnerJoinGroupNode->setDeps({newFilterGroup});
124+
result.newGroupNodes.emplace_back(newInnerJoinGroupNode);
125+
}
126+
return result;
127+
}
128+
129+
std::string PushFilterDownInnerJoinRule::toString() const {
130+
return "PushFilterDownInnerJoinRule";
131+
}
132+
133+
} // namespace opt
134+
} // namespace nebula
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* Copyright (c) 2022 vesoft inc. All rights reserved.
2+
*
3+
* This source code is licensed under Apache 2.0 License.
4+
*/
5+
6+
#ifndef GRAPH_OPTIMIZER_RULE_PUSHFILTERDOWNINNERJOINRULE_H_
7+
#define GRAPH_OPTIMIZER_RULE_PUSHFILTERDOWNINNERJOINRULE_H_
8+
9+
#include "graph/optimizer/OptRule.h"
10+
11+
namespace nebula {
12+
namespace opt {
13+
14+
// Push down the filter items from the left subplan of [[InnerJoin]]
15+
// Required conditions:
16+
// 1. Match the pattern
17+
// Benefits:
18+
// 1. Filter data early to optimize performance
19+
//
20+
// Tranformation:
21+
// Before:
22+
//
23+
// +-----------+-----------+
24+
// | Filter |
25+
// | ($left>3 and $right<4)|
26+
// +-----------+-----------+
27+
// |
28+
// +------+------+
29+
// | InnerJoin |
30+
// +------+------+
31+
//
32+
// After:
33+
//
34+
// +------+------+
35+
// | Filter |
36+
// | ($right<4) |
37+
// +------+------+
38+
// |
39+
// +------+------+
40+
// | InnerJoin |
41+
// +------+------+
42+
// /
43+
// +------+------+
44+
// | Filter |
45+
// | ($left>3) |
46+
// +------+------+
47+
48+
class PushFilterDownInnerJoinRule final : public OptRule {
49+
public:
50+
const Pattern &pattern() const override;
51+
52+
StatusOr<OptRule::TransformResult> transform(OptContext *qctx,
53+
const MatchedResult &matched) const override;
54+
55+
std::string toString() const override;
56+
57+
private:
58+
PushFilterDownInnerJoinRule();
59+
60+
static std::unique_ptr<OptRule> kInstance;
61+
};
62+
63+
} // namespace opt
64+
} // namespace nebula
65+
66+
#endif // GRAPH_OPTIMIZER_RULE_PUSHFILTERDOWNINNERJOINRULE_H_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) 2021 vesoft inc. All rights reserved.
2+
#
3+
# This source code is licensed under Apache 2.0 License.
4+
Feature: Push Filter down InnerJoin rule
5+
6+
Background:
7+
Given a graph with space named "nba"
8+
9+
Scenario: push filter down InnerJoin
10+
When profiling query:
11+
"""
12+
LOOKUP ON player WHERE player.name == "Tony Parker"
13+
YIELD id(vertex) as id |
14+
GO FROM $-.id OVER like
15+
WHERE (like.likeness - 1) >= 0
16+
YIELD like._src AS src_id, like._dst AS dst_id, like.likeness AS likeness
17+
"""
18+
Then the result should be, in any order:
19+
| src_id | dst_id | likeness |
20+
| "Tony Parker" | "LaMarcus Aldridge" | 90 |
21+
| "Tony Parker" | "Manu Ginobili" | 95 |
22+
| "Tony Parker" | "Tim Duncan" | 95 |
23+
And the execution plan should be:
24+
| id | name | dependencies | operator info |
25+
| 10 | Project | 15 | |
26+
| 15 | InnerJoin | 17 | |
27+
| 17 | Project | 18 | |
28+
| 18 | GetNeighbors | 3 | |
29+
| 3 | Project | 11 | |
30+
| 11 | TagIndexPrefixScan | 0 | |
31+
| 0 | Start | | |

tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature

+11-11
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,23 @@ Feature: Push Filter down LeftJoin rule
2222
| "Tim Duncan" |
2323
And the execution plan should be:
2424
| id | name | dependencies | operator info |
25-
| 24 | Project | 23 | |
26-
| 23 | Filter | 22 | |
27-
| 22 | InnerJoin | 21 | |
25+
| 24 | Project | 34 | |
26+
| 34 | InnerJoin | 33 | |
27+
| 33 | Filter | 21 | |
2828
| 21 | LeftJoin | 20 | |
2929
| 20 | Project | 19 | |
3030
| 19 | GetVertices | 18 | |
31-
| 18 | Project | 31 | |
32-
| 31 | GetNeighbors | 14 | |
33-
| 14 | Project | 13 | |
34-
| 13 | Filter | 12 | |
35-
| 12 | InnerJoin | 11 | |
31+
| 18 | Project | 30 | |
32+
| 30 | GetNeighbors | 14 | |
33+
| 14 | Project | 32 | |
34+
| 32 | InnerJoin | 31 | |
35+
| 31 | Filter | 11 | |
3636
| 11 | LeftJoin | 10 | |
3737
| 10 | Project | 9 | |
3838
| 9 | GetVertices | 8 | |
39-
| 8 | Project | 30 | |
40-
| 30 | GetNeighbors | 27 | |
41-
| 27 | Project | 25 | |
39+
| 8 | Project | 29 | |
40+
| 29 | GetNeighbors | 26 | |
41+
| 26 | Project | 25 | |
4242
| 25 | TagIndexPrefixScan | 0 | |
4343
| 0 | Start | | |
4444
When profiling query:

0 commit comments

Comments
 (0)