From 76c3545ce29bcb59c0efaeab73cba4a4325d34ff Mon Sep 17 00:00:00 2001 From: Sophie <84560950+Sophie-Xie@users.noreply.github.com> Date: Thu, 27 Apr 2023 22:12:51 +0800 Subject: [PATCH] optimize-all-path-go (#5528) (#2732) Co-authored-by: jimingquan --- src/graph/context/ast/QueryAstContext.h | 1 + src/graph/executor/algo/AllPathsExecutor.cpp | 143 ++++++++++++------ src/graph/executor/algo/AllPathsExecutor.h | 29 +++- src/graph/executor/query/ExpandExecutor.cpp | 3 +- src/graph/planner/ngql/GoPlanner.cpp | 30 ++++ src/graph/planner/ngql/GoPlanner.h | 2 + src/graph/validator/GoValidator.cpp | 38 +++++ src/graph/validator/GoValidator.h | 2 + tests/tck/features/go/SimpleCase.feature | 104 ++++++------- .../PushFilterDownHashLeftJoinRule.feature | 29 ++-- 10 files changed, 255 insertions(+), 126 deletions(-) diff --git a/src/graph/context/ast/QueryAstContext.h b/src/graph/context/ast/QueryAstContext.h index 59632051e05..92d07570bc3 100644 --- a/src/graph/context/ast/QueryAstContext.h +++ b/src/graph/context/ast/QueryAstContext.h @@ -95,6 +95,7 @@ struct GoContext final : AstContext { bool joinInput{false}; // true when $$.tag.prop exist bool joinDst{false}; + bool isSimple{false}; ExpressionProps exprProps; diff --git a/src/graph/executor/algo/AllPathsExecutor.cpp b/src/graph/executor/algo/AllPathsExecutor.cpp index 70d2ba0b999..51d794dd5e5 100644 --- a/src/graph/executor/algo/AllPathsExecutor.cpp +++ b/src/graph/executor/algo/AllPathsExecutor.cpp @@ -12,7 +12,7 @@ DEFINE_uint32( 100, "the number of vids to expand, when this threshold is exceeded, use heuristic expansion"); DEFINE_uint32(path_threshold_ratio, 2, "threshold for heuristics expansion"); -DEFINE_uint32(path_batch_size, 5000, "number of paths constructed by each thread"); +DEFINE_uint32(path_batch_size, 50000, "number of paths constructed by each thread"); namespace nebula { namespace graph { @@ -156,6 +156,8 @@ folly::Future AllPathsExecutor::getNeighbors(bool reverse) { } auto listVal = std::make_shared(std::move(list)); auto iter = std::make_unique(listVal); + time::Duration buildAdjTime; + auto key = folly::sformat("buildAdjTime {}step[{}]", reverse ? "reverse " : "", step); if (reverse) { rightNextStepVids_.clear(); expandFromRight(iter.get()); @@ -163,6 +165,7 @@ folly::Future AllPathsExecutor::getNeighbors(bool reverse) { leftNextStepVids_.clear(); expandFromLeft(iter.get()); } + otherStats_.emplace(key, folly::sformat("{}(us)", buildAdjTime.elapsedInUSec())); return Status::OK(); }); } @@ -261,6 +264,7 @@ folly::Future AllPathsExecutor::buildResult() { // if key exists, discard the right adjacency's key & values // because the right adjacency list may have fewer edges // a->c->o, a->b, c->f, f->o + time::Duration mergeAdjTime; for (auto& rAdj : rightAdjList_) { auto& src = rAdj.first; auto iter = leftAdjList_.find(src); @@ -292,71 +296,106 @@ folly::Future AllPathsExecutor::buildResult() { } } } + otherStats_.emplace("merge_adj_time", folly::sformat("{}(us)", mergeAdjTime.elapsedInUSec())); + time::Duration buildPathTime; auto future = buildPathMultiJobs(); - return future.via(runner()).thenValue([this](auto&& resp) { - UNUSED(resp); - if (!withProp_ || emptyPropVids_.empty()) { - finish(ResultBuilder().value(Value(std::move(result_))).build()); - return folly::makeFuture(Status::OK()); - } - return getPathProps(); - }); + return future.via(runner()) + .ensure([this, buildPathTime]() { + otherStats_.emplace("build_path_time", + folly::sformat("{}(us)", buildPathTime.elapsedInUSec())); + }) + .thenValue([this](auto&& resp) { + UNUSED(resp); + if (!withProp_ || emptyPropVids_.empty()) { + finish(ResultBuilder().value(Value(std::move(result_))).build()); + return folly::makeFuture(Status::OK()); + } + return getPathProps(); + }); } folly::Future AllPathsExecutor::buildPathMultiJobs() { - auto pathsPtr = std::make_shared>>(); + auto pathsPtr = std::make_shared>(); + if (threadLocalPtr_.get() == nullptr) { + threadLocalPtr_.reset(new std::deque()); + } for (auto& vid : leftInitVids_) { auto vidIter = leftAdjList_.find(vid); if (vidIter == leftAdjList_.end()) { continue; } - auto src = vidIter->first; + auto& src = vidIter->first; auto& adjEdges = vidIter->second; if (adjEdges.empty()) { continue; } pathsPtr->reserve(adjEdges.size() + pathsPtr->size()); for (auto& edge : adjEdges) { - pathsPtr->emplace_back(std::vector({src, edge})); + threadLocalPtr_->emplace_back(NPath(src, edge)); + pathsPtr->emplace_back(&threadLocalPtr_->back()); } } size_t step = 2; auto future = doBuildPath(step, 0, pathsPtr->size(), pathsPtr); - return future.via(runner()).thenValue([this](std::vector&& paths) { + return future.via(runner()).thenValue([this](std::vector>&& paths) { memory::MemoryCheckGuard guard; + if (!paths.empty()) { - result_.rows.swap(paths); + time::Duration convertPathTime; + for (auto& path : paths) { + result_.rows.emplace_back(convertNPath2Row(path.first, path.second)); + } + otherStats_.emplace("convert_path_time", + folly::sformat("{}(us)", convertPathTime.elapsedInUSec())); } return Status::OK(); }); } -folly::Future> AllPathsExecutor::doBuildPath( - size_t step, - size_t start, - size_t end, - std::shared_ptr>> pathsPtr) { - if (cnt_.load(std::memory_order_relaxed) >= limit_) { - return folly::makeFuture>(std::vector()); +// construct ROW[src1, [e1, v2, e2], v3] +Row AllPathsExecutor::convertNPath2Row(NPath* path, Value dst) { + std::vector list; + NPath* head = path; + while (head != nullptr) { + list.emplace_back(head->edge); + list.emplace_back(head->vertex); + head = head->p; } + Row row; + // add src; + row.values.emplace_back(list.back()); + list.pop_back(); + std::reverse(list.begin(), list.end()); + List edgeList(std::move(list)); + row.values.emplace_back(std::move(edgeList)); + row.values.emplace_back(std::move(dst)); + return row; +} +folly::Future>> +AllPathsExecutor::doBuildPath(size_t step, + size_t start, + size_t end, + std::shared_ptr> pathsPtr) { + if (cnt_.load(std::memory_order_relaxed) >= limit_) { + return folly::makeFuture>>( + std::vector>()); + } + if (threadLocalPtr_.get() == nullptr) { + threadLocalPtr_.reset(new std::deque()); + } auto& adjList = leftAdjList_; - auto currentPathPtr = std::make_unique>(); - auto newPathsPtr = std::make_shared>>(); + auto currentStepResult = std::make_unique>>(); + auto newPathsPtr = std::make_shared>(); for (auto i = start; i < end; ++i) { - auto& path = (*pathsPtr)[i]; - auto& edgeValue = path.back(); + auto path = (*pathsPtr)[i]; + auto& edgeValue = path->edge; DCHECK(edgeValue.isEdge()); auto& dst = edgeValue.getEdge().dst; auto dstIter = rightInitVids_.find(dst); if (dstIter != rightInitVids_.end()) { - Row row; - row.values.emplace_back(path.front()); - List edgeList(std::vector(path.begin() + 1, path.end())); - row.values.emplace_back(std::move(edgeList)); - row.values.emplace_back(*dstIter); - currentPathPtr->emplace_back(std::move(row)); + currentStepResult->emplace_back(std::make_pair(path, *dstIter)); ++cnt_; if (cnt_.load(std::memory_order_relaxed) >= limit_) { break; @@ -379,20 +418,17 @@ folly::Future> AllPathsExecutor::doBuildPath( continue; } } - // copy - auto newPath = path; - newPath.emplace_back(adjIter->first); - newPath.emplace_back(edge); - newPathsPtr->emplace_back(std::move(newPath)); + threadLocalPtr_->emplace_back(NPath(path, adjIter->first, edge)); + newPathsPtr->emplace_back(&threadLocalPtr_->back()); } } } auto newPathsSize = newPathsPtr->size(); if (step > maxStep_ || newPathsSize == 0) { - return folly::makeFuture>(std::move(*currentPathPtr)); + return folly::makeFuture>>(std::move(*currentStepResult)); } - std::vector>> futures; + std::vector>>> futures; if (newPathsSize < FLAGS_path_batch_size) { futures.emplace_back(folly::via(runner(), [this, step, newPathsSize, newPathsPtr]() { return doBuildPath(step + 1, 0, newPathsSize, newPathsPtr); @@ -407,9 +443,10 @@ folly::Future> AllPathsExecutor::doBuildPath( } } return folly::collect(futures).via(runner()).thenValue( - [pathPtr = std::move(currentPathPtr)](std::vector>&& paths) { + [pathPtr = std::move(currentStepResult)]( + std::vector>>&& paths) { memory::MemoryCheckGuard guard; - std::vector result = std::move(*pathPtr); + std::vector> result = std::move(*pathPtr); for (auto& path : paths) { if (path.empty()) { continue; @@ -441,19 +478,29 @@ folly::Future AllPathsExecutor::getPathProps() { }); } -bool AllPathsExecutor::hasSameVertices(const std::vector& edgeList, const Edge& edge) { +bool AllPathsExecutor::hasSameEdge(NPath* path, const Edge& edge) { + NPath* head = path; + while (head != nullptr) { + if (edge == head->edge) { + return true; + } + head = head->p; + } + return false; +} + +bool AllPathsExecutor::hasSameVertices(NPath* path, const Edge& edge) { if (edge.src == edge.dst) { return true; } auto& vid = edge.dst; - auto iter = edgeList.begin() + 1; - for (; iter != edgeList.end(); iter++) { - if (iter->isEdge()) { - auto& edgeVal = iter->getEdge(); - if (edgeVal.src == vid) { - return true; - } + NPath* head = path; + while (head != nullptr) { + auto& vertex = head->vertex; + if (vertex.getVertex().vid == vid) { + return true; } + head = head->p; } return false; } diff --git a/src/graph/executor/algo/AllPathsExecutor.h b/src/graph/executor/algo/AllPathsExecutor.h index 6c06ed1b231..695a8efaa06 100644 --- a/src/graph/executor/algo/AllPathsExecutor.h +++ b/src/graph/executor/algo/AllPathsExecutor.h @@ -5,6 +5,7 @@ #ifndef GRAPH_EXECUTOR_ALGO_ALLPATHSEXECUTOR_H_ #define GRAPH_EXECUTOR_ALGO_ALLPATHSEXECUTOR_H_ +#include "folly/ThreadLocal.h" #include "graph/executor/StorageAccessExecutor.h" // Using the two-way BFS algorithm, a heuristic algorithm is used in the expansion process @@ -26,10 +27,10 @@ // when expanding, if the vid has already been visited, do not visit again // leftAdjList_ save result of forward expansion // rightAdjList_ save result of backward expansion - namespace nebula { namespace graph { class AllPaths; +struct NPath; class AllPathsExecutor final : public StorageAccessExecutor { public: AllPathsExecutor(const PlanNode* node, QueryContext* qctx) @@ -46,6 +47,17 @@ class AllPathsExecutor final : public StorageAccessExecutor { template using VertexMap = std::unordered_map, VertexHash, VertexEqual>; + struct NPath { + NPath* p{nullptr}; + const Value& vertex; + const Value& edge; + NPath(const Value& v, const Value& e) : vertex(v), edge(e) {} + NPath(NPath* path, const Value& v, const Value& e) : p(path), vertex(v), edge(e) {} + NPath(NPath&& v) noexcept : p(v.p), vertex(std::move(v.vertex)), edge(std::move(v.edge)) {} + NPath(const NPath& v) : p(v.p), vertex(v.vertex), edge(v.edge) {} + ~NPath() {} + }; + private: void buildRequestVids(bool reverse); @@ -59,11 +71,10 @@ class AllPathsExecutor final : public StorageAccessExecutor { void expandFromRight(GetNeighborsIter* iter); - folly::Future> doBuildPath( - size_t step, - size_t start, - size_t end, - std::shared_ptr>> edgeLists); + Row convertNPath2Row(NPath* path, Value dst); + + folly::Future>> doBuildPath( + size_t step, size_t start, size_t end, std::shared_ptr> paths); folly::Future getPathProps(); @@ -71,7 +82,9 @@ class AllPathsExecutor final : public StorageAccessExecutor { folly::Future buildResult(); - bool hasSameVertices(const std::vector& edgeList, const Edge& edge); + bool hasSameEdge(NPath* path, const Edge& edge); + + bool hasSameVertices(NPath* path, const Edge& edge); private: const AllPaths* pathNode_{nullptr}; @@ -94,6 +107,8 @@ class AllPathsExecutor final : public StorageAccessExecutor { DataSet result_; std::vector emptyPropVids_; + class NewTag {}; + folly::ThreadLocalPtr, NewTag> threadLocalPtr_; }; } // namespace graph } // namespace nebula diff --git a/src/graph/executor/query/ExpandExecutor.cpp b/src/graph/executor/query/ExpandExecutor.cpp index 9cc1be79dc4..e5a1d9f295d 100644 --- a/src/graph/executor/query/ExpandExecutor.cpp +++ b/src/graph/executor/query/ExpandExecutor.cpp @@ -94,7 +94,8 @@ folly::Future ExpandExecutor::GetDstBySrc() { size = (*result.dsts_ref()).size(); } auto info = util::collectRespProfileData(result.result, hostLatency[i], size); - otherStats_.emplace(folly::sformat("resp[{}]", i), folly::toPrettyJson(info)); + otherStats_.emplace(folly::sformat("step{} resp [{}]", currentStep_, i), + folly::toPrettyJson(info)); } auto result = handleCompleteness(resps, FLAGS_accept_partial_success); if (!result.ok()) { diff --git a/src/graph/planner/ngql/GoPlanner.cpp b/src/graph/planner/ngql/GoPlanner.cpp index dab6369d4ef..220f8f0f404 100644 --- a/src/graph/planner/ngql/GoPlanner.cpp +++ b/src/graph/planner/ngql/GoPlanner.cpp @@ -158,6 +158,33 @@ PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep) { return join; } +SubPlan GoPlanner::doSimplePlan() { + auto qctx = goCtx_->qctx; + size_t step = goCtx_->steps.mSteps(); + auto* expand = Expand::make(qctx, + startNode_, + goCtx_->space.id, + false, // random + step, + buildEdgeProps(true)); + expand->setEdgeTypes(buildEdgeTypes()); + expand->setColNames({"_expand_vid"}); + expand->setInputVar(goCtx_->vidsVar); + + auto* dedup = Dedup::make(qctx, expand); + + auto pool = qctx->objPool(); + auto* newYieldExpr = pool->makeAndAdd(); + newYieldExpr->addColumn(new YieldColumn(ColumnExpression::make(pool, 0))); + auto* project = Project::make(qctx, dedup, newYieldExpr); + project->setColNames(std::move(goCtx_->colNames)); + + SubPlan subPlan; + subPlan.root = project; + subPlan.tail = expand; + return subPlan; +} + SubPlan GoPlanner::doPlan() { auto qctx = goCtx_->qctx; auto& from = goCtx_->from; @@ -275,6 +302,9 @@ StatusOr GoPlanner::transform(AstContext* astCtx) { subPlan.root = subPlan.tail = pt; return subPlan; } + if (goCtx_->isSimple) { + return doSimplePlan(); + } return doPlan(); } diff --git a/src/graph/planner/ngql/GoPlanner.h b/src/graph/planner/ngql/GoPlanner.h index 9f620778fb1..1a430529039 100644 --- a/src/graph/planner/ngql/GoPlanner.h +++ b/src/graph/planner/ngql/GoPlanner.h @@ -34,6 +34,8 @@ class GoPlanner final : public Planner { private: SubPlan doPlan(); + SubPlan doSimplePlan(); + private: std::unique_ptr buildVertexProps(const ExpressionProps::TagIDPropsMap& propsMap); diff --git a/src/graph/validator/GoValidator.cpp b/src/graph/validator/GoValidator.cpp index a16b42ebd2d..7ef45b1b9e0 100644 --- a/src/graph/validator/GoValidator.cpp +++ b/src/graph/validator/GoValidator.cpp @@ -58,6 +58,7 @@ Status GoValidator::validateImpl() { exprProps.varProps().size() > 1) { return Status::SemanticError("Only support single input in a go sentence."); } + goCtx_->isSimple = isSimpleCase(); NG_RETURN_IF_ERROR(buildColumns()); return Status::OK(); @@ -290,5 +291,42 @@ bool GoValidator::checkDstPropOrVertexExist(const Expression* expr) { return true; } +bool GoValidator::isSimpleCase() { + if (!goCtx_->limits.empty() || !goCtx_->distinct || goCtx_->filter || goCtx_->steps.isMToN() || + goCtx_->from.fromType != FromType::kInstantExpr) { + return false; + } + // Check if the yield cluase uses: + // 1. src tag props, + // 2. or edge props, except the dst id of edge. + // 3. input or var props. + auto& exprProps = goCtx_->exprProps; + if (!exprProps.srcTagProps().empty() || !exprProps.dstTagProps().empty()) { + return false; + } + if (!exprProps.edgeProps().empty()) { + for (auto& edgeProp : exprProps.edgeProps()) { + auto props = edgeProp.second; + if (props.size() != 1 && props.find(kDst) == props.end()) { + return false; + } + } + } + + bool atLeastOneDstId = false; + for (auto& col : goCtx_->yieldExpr->columns()) { + auto expr = col->expr(); + if (expr->kind() != Expression::Kind::kEdgeDst) { + return false; + } + atLeastOneDstId = true; + auto dstIdExpr = static_cast(expr); + if (dstIdExpr->sym() != "*" && goCtx_->over.edgeTypes.size() != 1) { + return false; + } + } + return atLeastOneDstId; +} + } // namespace graph } // namespace nebula diff --git a/src/graph/validator/GoValidator.h b/src/graph/validator/GoValidator.h index 241f88c4a43..5a7a42f9fb9 100644 --- a/src/graph/validator/GoValidator.h +++ b/src/graph/validator/GoValidator.h @@ -44,6 +44,8 @@ class GoValidator final : public Validator { bool checkDstPropOrVertexExist(const Expression* expr); + bool isSimpleCase(); + private: std::unique_ptr goCtx_; diff --git a/tests/tck/features/go/SimpleCase.feature b/tests/tck/features/go/SimpleCase.feature index 8a5bf1f8ba7..625670d2899 100644 --- a/tests/tck/features/go/SimpleCase.feature +++ b/tests/tck/features/go/SimpleCase.feature @@ -16,12 +16,11 @@ Feature: Simple case | 2 | And the execution plan should be: | id | name | dependencies | operator info | - | 6 | Aggregate | 5 | | - | 5 | Dedup | 4 | | + | 5 | Aggregate | 4 | | | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | When profiling query: """ GO FROM "Yao Ming" OVER like YIELD DISTINCT id($$) AS dst, $$.player.age AS age | ORDER BY $-.dst @@ -72,13 +71,12 @@ Feature: Simple case | "Manu Ginobili" | | "Tim Duncan" | And the execution plan should be: - | id | name | dependencies | operator info | - | 6 | Sort | 5 | | - | 5 | Dedup | 4 | | - | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | + | id | name | dependencies | operator info | + | 5 | Sort | 4 | | + | 4 | Project | 3 | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | When profiling query: """ GO FROM "Tony Parker" OVER like YIELD DISTINCT 2, id($$) AS a | ORDER BY $-.a @@ -107,12 +105,11 @@ Feature: Simple case | 22 | And the execution plan should be: | id | name | dependencies | operator info | - | 6 | Aggregate | 5 | | - | 5 | Dedup | 4 | | + | 5 | Aggregate | 4 | | | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | When profiling query: """ GO 3 STEPS FROM "Tony Parker" OVER serve BIDIRECT WHERE $$.team.name != "Lakers" YIELD DISTINCT id($$) | YIELD count(*) @@ -397,18 +394,17 @@ Feature: Simple case | 0 | And the execution plan should be: | id | name | dependencies | operator info | - | 13 | Aggregate | 12 | | - | 12 | Dedup | 11 | | - | 11 | Project | 10 | | - | 10 | HashInnerJoin | 5,9 | | - | 5 | Dedup | 4 | | + | 12 | Aggregate | 11 | | + | 11 | Dedup | 10 | | + | 10 | Project | 9 | | + | 9 | HashInnerJoin | 4,8 | | | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | - | 9 | ExpandAll | 8 | | - | 8 | Expand | 7 | | - | 7 | Argument | | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | + | 8 | ExpandAll | 7 | | + | 7 | Expand | 6 | | + | 6 | Argument | | | When profiling query: """ GO 1 STEP FROM "Tony Parker" OVER * YIELD distinct id($$) as id| GO 3 STEP FROM $-.id OVER * YIELD distinct id($$) | YIELD COUNT(*) @@ -418,18 +414,17 @@ Feature: Simple case | 22 | And the execution plan should be: | id | name | dependencies | operator info | - | 13 | Aggregate | 12 | | - | 12 | Dedup | 11 | | - | 11 | Project | 10 | | - | 10 | HashInnerJoin | 5,9 | | - | 5 | Dedup | 4 | | + | 12 | Aggregate | 11 | | + | 11 | Dedup | 10 | | + | 10 | Project | 9 | | + | 9 | HashInnerJoin | 4,8 | | | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | - | 9 | ExpandAll | 8 | | - | 8 | Expand | 7 | | - | 7 | Argument | | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | + | 8 | ExpandAll | 7 | | + | 7 | Expand | 6 | | + | 6 | Argument | | | Scenario: could not be optimied cases When profiling query: @@ -584,20 +579,19 @@ Feature: Simple case | "Grant Hill" | 46 | "Grant Hill" | And the execution plan should be: | id | name | dependencies | operator info | - | 18 | Sort | 17 | | - | 17 | Dedup | 16 | | - | 16 | Project | 21 | | - | 21 | HashInnerJoin | 5,20 | | - | 5 | Dedup | 4 | | + | 17 | Sort | 16 | | + | 16 | Dedup | 15 | | + | 15 | Project | 20 | | + | 20 | HashInnerJoin | 4,19 | | | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | - | 20 | Filter | 19 | | - | 19 | HashLeftJoin | 9,12 | | - | 9 | ExpandAll | 8 | | - | 8 | Expand | 7 | | - | 7 | Argument | | | - | 12 | Project | 11 | | - | 11 | GetVertices | 10 | | - | 10 | Argument | | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | + | 19 | Filter | 18 | | + | 18 | HashLeftJoin | 8,11 | | + | 8 | ExpandAll | 7 | | + | 7 | Expand | 6 | | + | 6 | Argument | | | + | 11 | Project | 10 | | + | 10 | GetVertices | 9 | | + | 9 | Argument | | | diff --git a/tests/tck/features/optimizer/PushFilterDownHashLeftJoinRule.feature b/tests/tck/features/optimizer/PushFilterDownHashLeftJoinRule.feature index c5a7ef3995d..c5a20a0a4a1 100644 --- a/tests/tck/features/optimizer/PushFilterDownHashLeftJoinRule.feature +++ b/tests/tck/features/optimizer/PushFilterDownHashLeftJoinRule.feature @@ -63,22 +63,21 @@ Feature: Push Filter down HashLeftJoin rule | "Boris Diaw" | "Suns" | ["team"] | And the execution plan should be: | id | name | dependencies | operator info | - | 20 | TopN | 17 | | - | 17 | Dedup | 16 | | - | 16 | Project | 23 | | - | 23 | HashInnerJoin | 5,26 | | - | 5 | Dedup | 4 | | + | 19 | TopN | 16 | | + | 16 | Dedup | 15 | | + | 15 | Project | 22 | | + | 22 | HashInnerJoin | 4,25 | | | 4 | Project | 3 | | - | 3 | ExpandAll | 2 | | - | 2 | Expand | 1 | | - | 1 | Start | | | - | 26 | HashLeftJoin | 27,12 | | - | 27 | ExpandAll | 8 | {"filter": "((like.likeness>80) OR like.likeness IS EMPTY)"} | - | 8 | Expand | 7 | | - | 7 | Argument | | | - | 12 | Project | 11 | | - | 11 | GetVertices | 10 | | - | 10 | Argument | | | + | 3 | Dedup | 2 | | + | 2 | Expand | 0 | | + | 0 | Start | | | + | 25 | HashLeftJoin | 26,11 | | + | 26 | ExpandAll | 7 | {"filter": "((like.likeness>80) OR like.likeness IS EMPTY)"} | + | 7 | Expand | 6 | | + | 6 | Argument | | | + | 11 | Project | 10 | | + | 10 | GetVertices | 9 | | + | 9 | Argument | | | Scenario: NOT push filter down HashLeftJoin When profiling query: