diff --git a/src/optimizer/CMakeLists.txt b/src/optimizer/CMakeLists.txt index c79e44f3621669bc469115b7bcb6fff9f4ac8318..0c62d94c8172b72e3a94f3fc7cb4df471b08a19e 100644 --- a/src/optimizer/CMakeLists.txt +++ b/src/optimizer/CMakeLists.txt @@ -13,6 +13,7 @@ nebula_add_library( OptContext.cpp rule/PushFilterDownGetNbrsRule.cpp rule/RemoveNoopProjectRule.cpp + rule/CombineFilterRule.cpp rule/MergeGetVerticesAndDedupRule.cpp rule/MergeGetVerticesAndProjectRule.cpp rule/MergeGetNbrsAndDedupRule.cpp diff --git a/src/optimizer/rule/CombineFilterRule.cpp b/src/optimizer/rule/CombineFilterRule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1fb69865164f05ab4338109838dfd6877e66cfc1 --- /dev/null +++ b/src/optimizer/rule/CombineFilterRule.cpp @@ -0,0 +1,75 @@ +/* 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/CombineFilterRule.h" + +#include "optimizer/OptContext.h" +#include "optimizer/OptGroup.h" +#include "planner/PlanNode.h" +#include "planner/Query.h" + +using nebula::graph::PlanNode; +using nebula::graph::QueryContext; + +namespace nebula { +namespace opt { + +std::unique_ptr<OptRule> CombineFilterRule::kInstance = + std::unique_ptr<CombineFilterRule>(new CombineFilterRule()); + +CombineFilterRule::CombineFilterRule() { + RuleSet::QueryRules().addRule(this); +} + +const Pattern& CombineFilterRule::pattern() const { + static Pattern pattern = Pattern::create(graph::PlanNode::Kind::kFilter, + {Pattern::create(graph::PlanNode::Kind::kFilter)}); + return pattern; +} + +StatusOr<OptRule::TransformResult> CombineFilterRule::transform( + OptContext* octx, + const MatchedResult& matched) const { + const auto* filterGroupNode = matched.node; + const auto* filterAbove = filterGroupNode->node(); + DCHECK_EQ(filterAbove->kind(), PlanNode::Kind::kFilter); + const auto* conditionAbove = static_cast<const graph::Filter*>(filterAbove)->condition(); + const auto& deps = matched.dependencies; + const auto* filterGroup = filterGroupNode->group(); + auto* qctx = octx->qctx(); + + TransformResult result; + result.eraseAll = true; + for (auto& dep : deps) { + const auto* groupNode = dep.node; + const auto* filterBelow = groupNode->node(); + DCHECK_EQ(filterBelow->kind(), PlanNode::Kind::kFilter); + auto* newFilter = static_cast<graph::Filter*>(filterBelow->clone()); + const auto* conditionBelow = newFilter->condition(); + auto* conditionCombine = + qctx->objPool()->add(new LogicalExpression(Expression::Kind::kLogicalAnd, + conditionAbove->clone().release(), + conditionBelow->clone().release())); + newFilter->setCondition(conditionCombine); + newFilter->setOutputVar(filterAbove->outputVar()); + auto* newGroupNode = OptGroupNode::create(octx, newFilter, filterGroup); + newGroupNode->setDeps(groupNode->dependencies()); + result.newGroupNodes.emplace_back(newGroupNode); + } + + return result; +} + +bool CombineFilterRule::match(OptContext* octx, const MatchedResult& matched) const { + return OptRule::match(octx, matched); +} + +std::string CombineFilterRule::toString() const { + return "CombineFilterRule"; +} + +} // namespace opt +} // namespace nebula diff --git a/src/optimizer/rule/CombineFilterRule.h b/src/optimizer/rule/CombineFilterRule.h new file mode 100644 index 0000000000000000000000000000000000000000..92ac49a1ba9fa499b92484f6daa622851505b772 --- /dev/null +++ b/src/optimizer/rule/CombineFilterRule.h @@ -0,0 +1,37 @@ +/* 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_COMBINEFILTERRULE_H_ +#define OPTIMIZER_RULE_COMBINEFILTERRULE_H_ + +#include <memory> + +#include "optimizer/OptRule.h" + +namespace nebula { +namespace opt { + +class CombineFilterRule 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: + CombineFilterRule(); + + static std::unique_ptr<OptRule> kInstance; +}; + +} // namespace opt +} // namespace nebula + +#endif // OPTIMIZER_RULE_COMBINEFILTERRULE_H_ diff --git a/tests/tck/features/expression/UnaryExpr.feature b/tests/tck/features/expression/UnaryExpr.feature index ee3a47e274942a1264ef1a3682fd87a52824583f..111cb8be8b12766c5995b79695ada689a09b380c 100644 --- a/tests/tck/features/expression/UnaryExpr.feature +++ b/tests/tck/features/expression/UnaryExpr.feature @@ -97,12 +97,11 @@ Feature: UnaryExpression | ("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"}) | And the execution plan should be: | id | name | dependencies | operator info | - | 10 | Project | 9 | | - | 9 | Filter | 8 | | - | 8 | Filter | 7 | | + | 10 | Project | 12 | | + | 12 | Filter | 7 | | | 7 | Project | 6 | | | 6 | Project | 5 | | - | 5 | Filter | 13 | | - | 13 | GetVertices | 11 | | + | 5 | Filter | 14 | | + | 14 | GetVertices | 11 | | | 11 | IndexScan | 0 | {"indexCtx": {"columnHints":{"scanType":"RANGE"}}} | | 0 | Start | | | diff --git a/tests/tck/features/optimizer/CombineFilterRule.feature b/tests/tck/features/optimizer/CombineFilterRule.feature new file mode 100644 index 0000000000000000000000000000000000000000..331087783266e797d9c157208e6ef96fac8896f3 --- /dev/null +++ b/tests/tck/features/optimizer/CombineFilterRule.feature @@ -0,0 +1,35 @@ +# 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: combine filters + + Background: + Given a graph with space named "nba" + + Scenario: combine filters + When profiling query: + """ + MATCH (v:player)-[:like]->(n) + WHERE v.age>40 AND n.age>42 + RETURN v, n + """ + Then the result should be, in any order: + | v | n | + | ("Steve Nash" :player{age: 45, name: "Steve Nash"}) | ("Jason Kidd" :player{age: 45, name: "Jason Kidd"}) | + | ("Vince Carter" :player{age: 42, name: "Vince Carter"}) | ("Jason Kidd" :player{age: 45, name: "Jason Kidd"}) | + | ("Jason Kidd" :player{age: 45, name: "Jason Kidd"}) | ("Steve Nash" :player{age: 45, name: "Steve Nash"}) | + And the execution plan should be: + | id | name | dependencies | operator info | + | 16 | Project | 18 | | + | 18 | Filter | 13 | {"condition" : "((($v.age>40) AND ($n.age>42)) AND !(hasSameEdgeInPath($-.__COL_0)))"} | + | 13 | Project | 12 | | + | 12 | InnerJoin | 11 | | + | 11 | Project | 20 | | + | 20 | GetVertices | 7 | | + | 7 | Filter | 6 | | + | 6 | Project | 5 | | + | 5 | Filter | 22 | | + | 22 | GetNeighbors | 17 | | + | 17 | IndexScan | 0 | | + | 0 | Start | | | diff --git a/tests/tck/features/optimizer/MergeGetVerticesDedupProjectRule.feature b/tests/tck/features/optimizer/MergeGetVerticesDedupProjectRule.feature index 9009644343e24c3f555532d5776543e13f130c47..465fb0b5e16281a5d3fd049cd6d03af7c51f829b 100644 --- a/tests/tck/features/optimizer/MergeGetVerticesDedupProjectRule.feature +++ b/tests/tck/features/optimizer/MergeGetVerticesDedupProjectRule.feature @@ -19,11 +19,10 @@ Feature: merge get vertices, dedup and project rule | "Tim Duncan" | And the execution plan should be: | id | name | dependencies | operator info | - | 0 | Project | 1 | | - | 1 | Filter | 2 | | - | 2 | Filter | 3 | | - | 3 | Project | 4 | | - | 4 | Project | 5 | | - | 5 | GetVertices | 6 | {"dedup": "true"} | - | 6 | PassThrough | 7 | | - | 7 | Start | | | + | 9 | Project | 10 | | + | 10 | Filter | 6 | | + | 6 | Project | 5 | | + | 5 | Project | 12 | | + | 12 | GetVertices | 1 | {"dedup": "true"} | + | 1 | PassThrough | 0 | | + | 0 | Start | | |