From db8b9096cb6de6634ad57923ae915688cde5a7a1 Mon Sep 17 00:00:00 2001 From: jimingquan Date: Tue, 30 Aug 2022 16:37:54 +0800 Subject: [PATCH] Change input vid format (#4583) * support getProp only return vid & delete _src from edge * fix prune properties * enhance edge" * fix pathBuildExpr * fix union error * fix unwind * fix bicartesionproduct * add optimize appendvertice flag * fix test * fix format * change getNeighbor's input * fix error * fix test error and fix go yield id(3276157) * fix error * fix ctest error --- src/clients/storage/StorageClient.cpp | 13 +- src/clients/storage/StorageClient.h | 4 +- src/common/expression/CaseExpression.cpp | 4 +- src/graph/context/Iterator.cpp | 5 +- src/graph/context/Result.h | 2 +- src/graph/executor/StorageAccessExecutor.cpp | 47 ++++--- src/graph/executor/StorageAccessExecutor.h | 5 +- src/graph/executor/algo/BatchShortestPath.cpp | 33 ++--- src/graph/executor/algo/ShortestPathBase.h | 4 +- .../executor/algo/ShortestPathExecutor.cpp | 8 +- .../executor/algo/SingleShortestPath.cpp | 37 ++---- .../executor/query/GetDstBySrcExecutor.cpp | 2 +- .../executor/query/GetDstBySrcExecutor.h | 2 +- .../executor/query/GetNeighborsExecutor.cpp | 14 +- .../executor/query/GetNeighborsExecutor.h | 2 +- src/graph/executor/query/TraverseExecutor.cpp | 120 +++++++----------- src/graph/executor/query/TraverseExecutor.h | 31 ++--- src/graph/executor/test/GetNeighborsTest.cpp | 13 +- src/graph/validator/GoValidator.cpp | 42 ++++-- src/interface/storage.thrift | 2 +- src/storage/query/GetNeighborsProcessor.cpp | 18 ++- src/storage/query/GetNeighborsProcessor.h | 2 +- src/storage/test/QueryTestUtils.h | 4 +- src/tools/storage-perf/StoragePerfTool.cpp | 17 +-- .../features/match/SingleShorestPath.feature | 36 ++---- 25 files changed, 206 insertions(+), 261 deletions(-) diff --git a/src/clients/storage/StorageClient.cpp b/src/clients/storage/StorageClient.cpp index faee39c02a2..e40385b30ba 100644 --- a/src/clients/storage/StorageClient.cpp +++ b/src/clients/storage/StorageClient.cpp @@ -40,7 +40,7 @@ cpp2::RequestCommon StorageClient::CommonRequestParam::toReqCommon() const { StorageRpcRespFuture StorageClient::getNeighbors( const CommonRequestParam& param, std::vector colNames, - const std::vector& vertices, + const std::vector& vids, const std::vector& edgeTypes, cpp2::EdgeDirection edgeDirection, const std::vector* statProps, @@ -52,13 +52,12 @@ StorageRpcRespFuture StorageClient::getNeighbors( const std::vector& orderBy, int64_t limit, const Expression* filter) { - auto cbStatus = getIdFromRow(param.space, false); + auto cbStatus = getIdFromValue(param.space); if (!cbStatus.ok()) { return folly::makeFuture>( std::runtime_error(cbStatus.status().toString())); } - - auto status = clusterIdsToHosts(param.space, vertices, std::move(cbStatus).value()); + auto status = clusterIdsToHosts(param.space, vids, std::move(cbStatus).value()); if (!status.ok()) { return folly::makeFuture>( std::runtime_error(status.status().toString())); @@ -109,14 +108,16 @@ StorageRpcRespFuture StorageClient::getNeighbors( } StorageRpcRespFuture StorageClient::getDstBySrc( - const CommonRequestParam& param, const List& vertices, const std::vector& edgeTypes) { + const CommonRequestParam& param, + const std::vector& vertices, + const std::vector& edgeTypes) { auto cbStatus = getIdFromValue(param.space); if (!cbStatus.ok()) { return folly::makeFuture>( std::runtime_error(cbStatus.status().toString())); } - auto status = clusterIdsToHosts(param.space, vertices.values, std::move(cbStatus).value()); + auto status = clusterIdsToHosts(param.space, vertices, std::move(cbStatus).value()); if (!status.ok()) { return folly::makeFuture>( std::runtime_error(status.status().toString())); diff --git a/src/clients/storage/StorageClient.h b/src/clients/storage/StorageClient.h index 3b7f73edeca..f6e58a05b70 100644 --- a/src/clients/storage/StorageClient.h +++ b/src/clients/storage/StorageClient.h @@ -67,7 +67,7 @@ class StorageClient const CommonRequestParam& param, std::vector colNames, // The first column has to be the VertexID - const std::vector& vertices, + const std::vector& vids, const std::vector& edgeTypes, cpp2::EdgeDirection edgeDirection, const std::vector* statProps, @@ -82,7 +82,7 @@ class StorageClient StorageRpcRespFuture getDstBySrc( const CommonRequestParam& param, - const List& vertices, + const std::vector& vertices, const std::vector& edgeTypes); StorageRpcRespFuture getProps( diff --git a/src/common/expression/CaseExpression.cpp b/src/common/expression/CaseExpression.cpp index 1f45b765a94..e67fc57b869 100644 --- a/src/common/expression/CaseExpression.cpp +++ b/src/common/expression/CaseExpression.cpp @@ -54,9 +54,9 @@ bool CaseExpression::operator==(const Expression& rhs) const { } const Value& CaseExpression::eval(ExpressionContext& ctx) { - auto cond = condition_ != nullptr ? condition_->eval(ctx) : Value(); + const auto& cond = condition_ != nullptr ? condition_->eval(ctx) : Value(); for (const auto& whenThen : cases_) { - auto when = whenThen.when->eval(ctx); + const auto& when = whenThen.when->eval(ctx); if (condition_ != nullptr) { if (cond == when) { result_ = whenThen.then->eval(ctx); diff --git a/src/graph/context/Iterator.cpp b/src/graph/context/Iterator.cpp index 637caa61712..fc369192a69 100644 --- a/src/graph/context/Iterator.cpp +++ b/src/graph/context/Iterator.cpp @@ -439,11 +439,8 @@ Value GetNeighborsIter::getVertex(const std::string& name) const { if (!valid()) { return Value::kNullValue; } - auto vidVal = getColumn(0); - if (UNLIKELY(!SchemaUtil::isValidVid(vidVal))) { - return Value::kNullBadType; - } + Vertex vertex; vertex.vid = vidVal; auto& tagPropMap = currentDs_->tagPropsMap; diff --git a/src/graph/context/Result.h b/src/graph/context/Result.h index a23d701cd6f..90af2fadd54 100644 --- a/src/graph/context/Result.h +++ b/src/graph/context/Result.h @@ -52,7 +52,7 @@ class Result final { return std::move(core_.iter); } - Iterator* iterRef() { + Iterator* iterRef() const { return core_.iter.get(); } diff --git a/src/graph/executor/StorageAccessExecutor.cpp b/src/graph/executor/StorageAccessExecutor.cpp index 62aa7c53554..01958eb7a17 100644 --- a/src/graph/executor/StorageAccessExecutor.cpp +++ b/src/graph/executor/StorageAccessExecutor.cpp @@ -77,39 +77,43 @@ StatusOr buildRequestDataSet(const SpaceInfo &space, } template -StatusOr buildRequestList(const SpaceInfo &space, - QueryExpressionContext &exprCtx, - Iterator *iter, - Expression *expr, - bool dedup) { +StatusOr> buildRequestList(const SpaceInfo &space, + QueryExpressionContext &exprCtx, + Iterator *iter, + Expression *expr, + bool dedup, + bool isCypher) { DCHECK(iter && expr) << "iter=" << iter << ", expr=" << expr; - nebula::List vertices; - auto s = iter->size(); - vertices.reserve(s); + std::vector vids; + auto iterSize = iter->size(); + vids.reserve(iterSize); std::unordered_set uniqueSet; - uniqueSet.reserve(s); + uniqueSet.reserve(iterSize); - const auto &vidType = *(space.spaceDesc.vid_type_ref()); + const auto &metaVidType = *(space.spaceDesc.vid_type_ref()); + auto vidType = SchemaUtil::propTypeToValueType(metaVidType.get_type()); for (; iter->valid(); iter->next()) { auto vid = expr->eval(exprCtx(iter)); if (vid.empty()) { continue; } - if (!SchemaUtil::isValidVid(vid, vidType)) { + if (vid.type() != vidType) { + if (isCypher) { + continue; + } std::stringstream ss; - ss << "`" << vid.toString() << "', the srcs should be type of " - << apache::thrift::util::enumNameSafe(vidType.get_type()) << ", but was`" << vid.type() - << "'"; + ss << "`" << vid.toString() << "', the srcs should be type of " << vidType << ", but was`" + << vid.type() << "'"; return Status::Error(ss.str()); } if (dedup && !uniqueSet.emplace(Vid::value(vid)).second) { continue; } - vertices.emplace_back(std::move(vid)); + vids.emplace_back(std::move(vid)); } - return vertices; + return vids; } } // namespace internal @@ -131,16 +135,17 @@ StatusOr StorageAccessExecutor::buildRequestDataSetByVidType(Iterator * return internal::buildRequestDataSet(space, exprCtx, iter, expr, dedup, isCypher); } -StatusOr StorageAccessExecutor::buildRequestListByVidType(Iterator *iter, - Expression *expr, - bool dedup) { +StatusOr> StorageAccessExecutor::buildRequestListByVidType(Iterator *iter, + Expression *expr, + bool dedup, + bool isCypher) { const auto &space = qctx()->rctx()->session()->space(); QueryExpressionContext exprCtx(qctx()->ectx()); if (isIntVidType(space)) { - return internal::buildRequestList(space, exprCtx, iter, expr, dedup); + return internal::buildRequestList(space, exprCtx, iter, expr, dedup, isCypher); } - return internal::buildRequestList(space, exprCtx, iter, expr, dedup); + return internal::buildRequestList(space, exprCtx, iter, expr, dedup, isCypher); } std::string StorageAccessExecutor::getStorageDetail( diff --git a/src/graph/executor/StorageAccessExecutor.h b/src/graph/executor/StorageAccessExecutor.h index 457729b7dd2..56c97604e89 100644 --- a/src/graph/executor/StorageAccessExecutor.h +++ b/src/graph/executor/StorageAccessExecutor.h @@ -163,7 +163,10 @@ class StorageAccessExecutor : public Executor { bool dedup, bool isCypher = false); - StatusOr buildRequestListByVidType(Iterator *iter, Expression *expr, bool dedup); + StatusOr> buildRequestListByVidType(Iterator *iter, + Expression *expr, + bool dedup, + bool isCypher = false); }; } // namespace graph diff --git a/src/graph/executor/algo/BatchShortestPath.cpp b/src/graph/executor/algo/BatchShortestPath.cpp index 44e553966ee..7ad1cfa6146 100644 --- a/src/graph/executor/algo/BatchShortestPath.cpp +++ b/src/graph/executor/algo/BatchShortestPath.cpp @@ -51,19 +51,19 @@ size_t BatchShortestPath::init(const HashSet& startVids, const HashSet& endVids) resultDs_.resize(rowSize); for (auto& _startVids : batchStartVids_) { - DataSet startDs; + HashSet srcVids; PathMap leftPathMap; for (auto& startVid : _startVids) { - startDs.rows.emplace_back(Row({startVid})); + srcVids.emplace(startVid); std::vector dummy; leftPathMap[startVid].emplace(startVid, std::move(dummy)); } for (auto& _endVids : batchEndVids_) { - DataSet endDs; + HashSet dstVids; PathMap preRightPathMap; PathMap rightPathMap; for (auto& endVid : _endVids) { - endDs.rows.emplace_back(Row({endVid})); + dstVids.emplace(endVid); std::vector dummy; rightPathMap[endVid].emplace(endVid, dummy); preRightPathMap[endVid].emplace(endVid, std::move(dummy)); @@ -75,8 +75,8 @@ size_t BatchShortestPath::init(const HashSet& startVids, const HashSet& endVids) currentRightPathMaps_.emplace_back(std::move(rightPathMap)); // set vid for getNeightbor - leftVids_.emplace_back(startDs); - rightVids_.emplace_back(std::move(endDs)); + leftVids_.emplace_back(srcVids); + rightVids_.emplace_back(dstVids); // set terminateMap std::unordered_multimap> terminationMap; @@ -116,11 +116,13 @@ folly::Future BatchShortestPath::getNeighbors(size_t rowNum, size_t step qctx_->rctx()->session()->id(), qctx_->plan()->id(), qctx_->plan()->isProfileEnabled()); - auto& inputRows = reverse ? rightVids_[rowNum].rows : leftVids_[rowNum].rows; + auto& inputVids = reverse ? rightVids_[rowNum] : leftVids_[rowNum]; + std::vector vids(inputVids.begin(), inputVids.end()); + inputVids.clear(); return storageClient ->getNeighbors(param, {nebula::kVid}, - std::move(inputRows), + std::move(vids), {}, pathNode_->edgeDirection(), nullptr, @@ -271,8 +273,8 @@ folly::Future BatchShortestPath::handleResponse(size_t rowNum, size_t st if (result || stepNum * 2 >= maxStep_) { return folly::makeFuture(Status::OK()); } - auto& leftVids = leftVids_[rowNum].rows; - auto& rightVids = rightVids_[rowNum].rows; + auto& leftVids = leftVids_[rowNum]; + auto& rightVids = rightVids_[rowNum]; if (leftVids.empty() || rightVids.empty()) { return folly::makeFuture(Status::OK()); } @@ -458,17 +460,10 @@ std::vector BatchShortestPath::createPaths(const std::vector& p } void BatchShortestPath::setNextStepVid(const PathMap& paths, size_t rowNum, bool reverse) { - std::vector nextStepVids; + auto& nextStepVids = reverse ? rightVids_[rowNum] : leftVids_[rowNum]; nextStepVids.reserve(paths.size()); for (const auto& path : paths) { - Row row; - row.values.emplace_back(path.first); - nextStepVids.emplace_back(std::move(row)); - } - if (reverse) { - rightVids_[rowNum].rows.swap(nextStepVids); - } else { - leftVids_[rowNum].rows.swap(nextStepVids); + nextStepVids.emplace(path.first); } } diff --git a/src/graph/executor/algo/ShortestPathBase.h b/src/graph/executor/algo/ShortestPathBase.h index f80ebbc77c7..23e5c2195ea 100644 --- a/src/graph/executor/algo/ShortestPathBase.h +++ b/src/graph/executor/algo/ShortestPathBase.h @@ -86,8 +86,8 @@ class ShortestPathBase { folly::SpinLock statsLock_; std::vector resultDs_; - std::vector leftVids_; - std::vector rightVids_; + std::vector leftVids_; + std::vector rightVids_; }; } // namespace graph diff --git a/src/graph/executor/algo/ShortestPathExecutor.cpp b/src/graph/executor/algo/ShortestPathExecutor.cpp index b2c3626f7c1..d8269ae074f 100644 --- a/src/graph/executor/algo/ShortestPathExecutor.cpp +++ b/src/graph/executor/algo/ShortestPathExecutor.cpp @@ -38,14 +38,14 @@ folly::Future ShortestPathExecutor::execute() { size_t ShortestPathExecutor::checkInput(HashSet& startVids, HashSet& endVids) { auto iter = ectx_->getResult(pathNode_->inputVar()).iter(); - const auto& vidType = *(qctx()->rctx()->session()->space().spaceDesc.vid_type_ref()); + const auto& metaVidType = *(qctx()->rctx()->session()->space().spaceDesc.vid_type_ref()); + auto vidType = SchemaUtil::propTypeToValueType(metaVidType.get_type()); for (; iter->valid(); iter->next()) { auto start = iter->getColumn(0); auto end = iter->getColumn(1); - if (!SchemaUtil::isValidVid(start, vidType) || !SchemaUtil::isValidVid(end, vidType)) { + if (start.type() != vidType || end.type() != vidType) { LOG(ERROR) << "Mismatched shortestPath vid type. start type : " << start.type() - << ", end type: " << end.type() - << ", space vid type: " << SchemaUtil::typeToString(vidType); + << ", end type: " << end.type() << ", space vid type: " << vidType; continue; } if (start == end) { diff --git a/src/graph/executor/algo/SingleShortestPath.cpp b/src/graph/executor/algo/SingleShortestPath.cpp index a61da72f5ae..401edc8ada3 100644 --- a/src/graph/executor/algo/SingleShortestPath.cpp +++ b/src/graph/executor/algo/SingleShortestPath.cpp @@ -46,6 +46,7 @@ void SingleShortestPath::init(const HashSet& startVids, const HashSet& endVids, allRightPaths_.reserve(rowSize); resultDs_.resize(rowSize); for (const auto& startVid : startVids) { + HashSet srcVid({startVid}); for (const auto& endVid : endVids) { robin_hood::unordered_flat_map, std::hash> steps; std::vector dummy; @@ -53,11 +54,9 @@ void SingleShortestPath::init(const HashSet& startVids, const HashSet& endVids, HalfPath originRightPath({std::move(steps)}); allRightPaths_.emplace_back(std::move(originRightPath)); - DataSet startDs, endDs; - startDs.rows.emplace_back(Row({startVid})); - endDs.rows.emplace_back(Row({endVid})); - leftVids_.emplace_back(std::move(startDs)); - rightVids_.emplace_back(std::move(endDs)); + HashSet dstVid({endVid}); + leftVids_.emplace_back(srcVid); + rightVids_.emplace_back(std::move(dstVid)); } } } @@ -88,11 +87,13 @@ folly::Future SingleShortestPath::getNeighbors(size_t rowNum, qctx_->rctx()->session()->id(), qctx_->plan()->id(), qctx_->plan()->isProfileEnabled()); - auto& inputRows = reverse ? rightVids_[rowNum].rows : leftVids_[rowNum].rows; + auto& inputVids = reverse ? rightVids_[rowNum] : leftVids_[rowNum]; + std::vector vids(inputVids.begin(), inputVids.end()); + inputVids.clear(); return storageClient ->getNeighbors(param, {nebula::kVid}, - std::move(inputRows), + std::move(vids), {}, pathNode_->edgeDirection(), nullptr, @@ -137,9 +138,7 @@ Status SingleShortestPath::doBuildPath(size_t rowNum, GetNeighborsIter* iter, bo allSteps.emplace_back(); auto& currentStep = allSteps.back(); - HashSet uniqueDst; - uniqueDst.reserve(iterSize); - std::vector nextStepVids; + auto& nextStepVids = reverse ? rightVids_[rowNum] : leftVids_[rowNum]; nextStepVids.reserve(iterSize); QueryExpressionContext ctx(qctx_->ectx()); @@ -149,14 +148,12 @@ Status SingleShortestPath::doBuildPath(size_t rowNum, GetNeighborsIter* iter, bo continue; } auto& edge = edgeVal.getEdge(); - auto dst = edge.dst; + auto& dst = edge.dst; if (visitedVids.find(dst) != visitedVids.end()) { continue; } visitedVids.emplace(edge.src); - if (uniqueDst.emplace(dst).second) { - nextStepVids.emplace_back(Row({dst})); - } + nextStepVids.emplace(dst); auto vertex = iter->getVertex(); Row step; step.emplace_back(std::move(vertex)); @@ -170,13 +167,7 @@ Status SingleShortestPath::doBuildPath(size_t rowNum, GetNeighborsIter* iter, bo steps.emplace_back(std::move(step)); } } - visitedVids.insert(std::make_move_iterator(uniqueDst.begin()), - std::make_move_iterator(uniqueDst.end())); - if (reverse) { - rightVids_[rowNum].rows.swap(nextStepVids); - } else { - leftVids_[rowNum].rows.swap(nextStepVids); - } + visitedVids.insert(nextStepVids.begin(), nextStepVids.end()); return Status::OK(); } @@ -191,8 +182,8 @@ folly::Future SingleShortestPath::handleResponse(size_t rowNum, size_t s if (result || stepNum * 2 >= maxStep_) { return folly::makeFuture(Status::OK()); } - auto& leftVids = leftVids_[rowNum].rows; - auto& rightVids = rightVids_[rowNum].rows; + auto& leftVids = leftVids_[rowNum]; + auto& rightVids = rightVids_[rowNum]; if (leftVids.empty() || rightVids.empty()) { return folly::makeFuture(Status::OK()); } diff --git a/src/graph/executor/query/GetDstBySrcExecutor.cpp b/src/graph/executor/query/GetDstBySrcExecutor.cpp index 49cd9363d71..446c51a2435 100644 --- a/src/graph/executor/query/GetDstBySrcExecutor.cpp +++ b/src/graph/executor/query/GetDstBySrcExecutor.cpp @@ -13,7 +13,7 @@ using nebula::storage::cpp2::GetDstBySrcResponse; namespace nebula { namespace graph { -StatusOr GetDstBySrcExecutor::buildRequestList() { +StatusOr> GetDstBySrcExecutor::buildRequestList() { SCOPED_TIMER(&execTime_); auto inputVar = gd_->inputVar(); auto iter = ectx_->getResult(inputVar).iter(); diff --git a/src/graph/executor/query/GetDstBySrcExecutor.h b/src/graph/executor/query/GetDstBySrcExecutor.h index bf296895a6d..994f76d0caf 100644 --- a/src/graph/executor/query/GetDstBySrcExecutor.h +++ b/src/graph/executor/query/GetDstBySrcExecutor.h @@ -20,7 +20,7 @@ class GetDstBySrcExecutor final : public StorageAccessExecutor { folly::Future execute() override; - StatusOr buildRequestList(); + StatusOr> buildRequestList(); private: using RpcResponse = storage::StorageRpcResponse; diff --git a/src/graph/executor/query/GetNeighborsExecutor.cpp b/src/graph/executor/query/GetNeighborsExecutor.cpp index 3737f741088..986dcc9152e 100644 --- a/src/graph/executor/query/GetNeighborsExecutor.cpp +++ b/src/graph/executor/query/GetNeighborsExecutor.cpp @@ -13,18 +13,18 @@ using nebula::storage::cpp2::GetNeighborsResponse; namespace nebula { namespace graph { -StatusOr GetNeighborsExecutor::buildRequestDataSet() { +StatusOr> GetNeighborsExecutor::buildRequestVids() { SCOPED_TIMER(&execTime_); auto inputVar = gn_->inputVar(); auto iter = ectx_->getResult(inputVar).iter(); - return buildRequestDataSetByVidType(iter.get(), gn_->src(), gn_->dedup()); + return buildRequestListByVidType(iter.get(), gn_->src(), gn_->dedup()); } folly::Future GetNeighborsExecutor::execute() { - auto res = buildRequestDataSet(); + auto res = buildRequestVids(); NG_RETURN_IF_ERROR(res); - auto reqDs = std::move(res).value(); - if (reqDs.rows.empty()) { + auto vids = std::move(res).value(); + if (vids.empty()) { List emptyResult; return finish(ResultBuilder() .value(Value(std::move(emptyResult))) @@ -41,8 +41,8 @@ folly::Future GetNeighborsExecutor::execute() { qctx()->plan()->isProfileEnabled()); return storageClient ->getNeighbors(param, - std::move(reqDs.colNames), - std::move(reqDs.rows), + {nebula::kVid}, + std::move(vids), gn_->edgeTypes(), gn_->edgeDirection(), gn_->statProps(), diff --git a/src/graph/executor/query/GetNeighborsExecutor.h b/src/graph/executor/query/GetNeighborsExecutor.h index 7ba20c19cf5..6e311a88300 100644 --- a/src/graph/executor/query/GetNeighborsExecutor.h +++ b/src/graph/executor/query/GetNeighborsExecutor.h @@ -21,7 +21,7 @@ class GetNeighborsExecutor final : public StorageAccessExecutor { folly::Future execute() override; - StatusOr buildRequestDataSet(); + StatusOr> buildRequestVids(); private: using RpcResponse = storage::StorageRpcResponse; diff --git a/src/graph/executor/query/TraverseExecutor.cpp b/src/graph/executor/query/TraverseExecutor.cpp index c895db27666..1af3a78f1b5 100644 --- a/src/graph/executor/query/TraverseExecutor.cpp +++ b/src/graph/executor/query/TraverseExecutor.cpp @@ -17,7 +17,7 @@ namespace graph { folly::Future TraverseExecutor::execute() { range_ = traverse_->stepRange(); - auto status = buildRequestDataSet(); + auto status = buildRequestVids(); if (!status.ok()) { return error(std::move(status)); } @@ -26,52 +26,43 @@ folly::Future TraverseExecutor::execute() { Status TraverseExecutor::close() { // clear the members - reqDs_.rows.clear(); - uniqueDsts_.clear(); + vids_.clear(); return Executor::close(); } -Status TraverseExecutor::buildRequestDataSet() { +Status TraverseExecutor::buildRequestVids() { time::Duration dur; SCOPED_TIMER(&execTime_); - auto inputVar = traverse_->inputVar(); - auto& inputResult = ectx_->getResult(inputVar); - auto inputIter = inputResult.iter(); - auto iter = static_cast(inputIter.get()); + const auto& inputVar = traverse_->inputVar(); + auto inputIter = ectx_->getResult(inputVar).iterRef(); + auto iter = static_cast(inputIter); - reqDs_.colNames = {kVid}; - reqDs_.rows.reserve(iter->size()); - - std::unordered_set uniqueSet; - uniqueSet.reserve(iter->size()); - std::unordered_map prev; const auto& spaceInfo = qctx()->rctx()->session()->space(); - const auto& vidType = *(spaceInfo.spaceDesc.vid_type_ref()); + const auto& metaVidType = *(spaceInfo.spaceDesc.vid_type_ref()); + auto vidType = SchemaUtil::propTypeToValueType(metaVidType.get_type()); auto* src = traverse_->src(); + QueryExpressionContext ctx(ectx_); + HashMap prev; bool mv = movable(traverse_->inputVars().front()); for (; iter->valid(); iter->next()) { - auto vid = src->eval(ctx(iter)); - if (!SchemaUtil::isValidVid(vid, vidType)) { - LOG(ERROR) << "Mismatched vid type: " << vid.type() - << ", space vid type: " << SchemaUtil::typeToString(vidType); + const auto& vid = src->eval(ctx(iter)); + if (vid.type() != vidType) { + LOG(ERROR) << "Mismatched vid type: " << vid.type() << ", space vid type: " << vidType; continue; } // Need copy here, Argument executor may depends on this variable. auto prePath = mv ? iter->moveRow() : *iter->row(); buildPath(prev, vid, std::move(prePath)); - if (!uniqueSet.emplace(vid).second) { - continue; - } - reqDs_.emplace_back(Row({std::move(vid)})); + vids_.emplace(vid); } paths_.emplace_back(std::move(prev)); return Status::OK(); } folly::Future TraverseExecutor::traverse() { - if (reqDs_.rows.empty()) { + if (vids_.empty()) { DataSet emptyResult; return finish(ResultBuilder().value(Value(std::move(emptyResult))).build()); } @@ -87,10 +78,12 @@ folly::Future TraverseExecutor::getNeighbors() { qctx()->rctx()->session()->id(), qctx()->plan()->id(), qctx()->plan()->isProfileEnabled()); + std::vector vids(vids_.begin(), vids_.end()); + vids_.clear(); return storageClient ->getNeighbors(param, - reqDs_.colNames, - std::move(reqDs_.rows), + {nebula::kVid}, + std::move(vids), traverse_->edgeTypes(), traverse_->edgeDirection(), finalStep ? traverse_->statProps() : nullptr, @@ -173,7 +166,7 @@ folly::Future TraverseExecutor::handleResponse(RpcResponse&& resps) { return folly::makeFuture(std::move(status)); } if (!isFinalStep()) { - if (reqDs_.rows.empty()) { + if (vids_.empty()) { if (range_ != nullptr) { return folly::makeFuture(buildResult()); } else { @@ -193,7 +186,7 @@ folly::Future TraverseExecutor::handleResponse(RpcResponse&& resps) { return folly::makeFuture(std::move(status)); } if (!isFinalStep()) { - if (reqDs_.rows.empty()) { + if (vids_.empty()) { if (range_ != nullptr) { return folly::makeFuture(buildResult()); } else { @@ -210,12 +203,9 @@ folly::Future TraverseExecutor::handleResponse(RpcResponse&& resps) { } Status TraverseExecutor::buildInterimPath(GetNeighborsIter* iter) { - const auto& spaceInfo = qctx()->rctx()->session()->space(); - DataSet reqDs; - reqDs.colNames = reqDs_.colNames; size_t count = 0; - const std::unordered_map& prev = paths_.back(); + const auto& prev = paths_.back(); if (currentStep_ == 1 && zeroStep()) { paths_.emplace_back(); NG_RETURN_IF_ERROR(handleZeroStep(prev, iter->getVertices(), paths_.back(), count)); @@ -226,30 +216,30 @@ Status TraverseExecutor::buildInterimPath(GetNeighborsIter* iter) { } } paths_.emplace_back(); - std::unordered_map& current = paths_.back(); + auto& current = paths_.back(); auto* vFilter = traverse_->vFilter(); auto* eFilter = traverse_->eFilter(); QueryExpressionContext ctx(ectx_); - std::unordered_set uniqueDst; for (; iter->valid(); iter->next()) { - auto& dst = iter->getEdgeProp("*", kDst); - if (!SchemaUtil::isValidVid(dst, *(spaceInfo.spaceDesc.vid_type_ref()))) { - continue; - } if (vFilter != nullptr && currentStep_ == 1) { - auto& vFilterVal = vFilter->eval(ctx(iter)); + const auto& vFilterVal = vFilter->eval(ctx(iter)); if (!vFilterVal.isBool() || !vFilterVal.getBool()) { continue; } } if (eFilter != nullptr) { - auto& eFilterVal = eFilter->eval(ctx(iter)); + const auto& eFilterVal = eFilter->eval(ctx(iter)); if (!eFilterVal.isBool() || !eFilterVal.getBool()) { continue; } } + auto& dst = iter->getEdgeProp("*", kDst); + if (dst.type() == Value::Type::__EMPTY__) { + // no edge return Empty + continue; + } auto srcV = iter->getVertex(); auto e = iter->getEdge(); // Join on dst = src @@ -262,9 +252,8 @@ Status TraverseExecutor::buildInterimPath(GetNeighborsIter* iter) { if (hasSameEdge(prevPath, e.getEdge())) { continue; } - if (uniqueDst.emplace(dst).second) { - reqDs.rows.emplace_back(Row({std::move(dst)})); - } + vids_.emplace(dst); + if (currentStep_ == 1) { Row path; if (traverse_->trackPrevPath()) { @@ -288,14 +277,13 @@ Status TraverseExecutor::buildInterimPath(GetNeighborsIter* iter) { } // `iter' releasePrevPaths(count); - reqDs_ = std::move(reqDs); return Status::OK(); } folly::Future TraverseExecutor::buildInterimPathMultiJobs( std::unique_ptr iter) { size_t pathCnt = 0; - const std::unordered_map* prev = &paths_.back(); + const auto* prev = &paths_.back(); if (currentStep_ == 1 && zeroStep()) { paths_.emplace_back(); NG_RETURN_IF_ERROR(handleZeroStep(*prev, iter->getVertices(), paths_.back(), pathCnt)); @@ -313,9 +301,7 @@ folly::Future TraverseExecutor::buildInterimPathMultiJobs( }; auto gather = [this, pathCnt](std::vector> results) mutable -> Status { - reqDs_.clear(); - uniqueDsts_.clear(); - std::unordered_map& current = paths_.back(); + auto& current = paths_.back(); size_t mapCnt = 0; for (auto& r : results) { if (!r.ok()) { @@ -328,10 +314,9 @@ folly::Future TraverseExecutor::buildInterimPathMultiJobs( for (auto& r : results) { auto jobResult = std::move(r).value(); pathCnt += jobResult.pathCnt; - if (!jobResult.reqDs.rows.empty()) { - reqDs_.rows.insert(reqDs_.rows.end(), - std::make_move_iterator(jobResult.reqDs.rows.begin()), - std::make_move_iterator(jobResult.reqDs.rows.end())); + auto& vids = jobResult.vids; + if (!vids.empty()) { + vids_.insert(std::make_move_iterator(vids.begin()), std::make_move_iterator(vids.end())); } for (auto& kv : jobResult.newPaths) { auto& paths = current[kv.first]; @@ -350,34 +335,29 @@ folly::Future TraverseExecutor::buildInterimPathMultiJobs( StatusOr TraverseExecutor::handleJob(size_t begin, size_t end, Iterator* iter, - const std::unordered_map& prev) { + const HashMap& prev) { // Handle edges from begin to end, [begin, end) JobResult jobResult; size_t& pathCnt = jobResult.pathCnt; - DataSet& reqDs = jobResult.reqDs; - reqDs.colNames = reqDs_.colNames; + auto& vids = jobResult.vids; QueryExpressionContext ctx(ectx_); auto* vFilter = traverse_->vFilter() ? traverse_->vFilter()->clone() : nullptr; auto* eFilter = traverse_->eFilter() ? traverse_->eFilter()->clone() : nullptr; - const auto& spaceInfo = qctx()->rctx()->session()->space(); - std::unordered_map& current = jobResult.newPaths; + auto& current = jobResult.newPaths; for (; iter->valid() && begin++ < end; iter->next()) { - auto& dst = iter->getEdgeProp("*", kDst); - if (!SchemaUtil::isValidVid(dst, *(spaceInfo.spaceDesc.vid_type_ref()))) { - continue; - } if (vFilter != nullptr && currentStep_ == 1) { - auto& vFilterVal = vFilter->eval(ctx(iter)); + const auto& vFilterVal = vFilter->eval(ctx(iter)); if (!vFilterVal.isBool() || !vFilterVal.getBool()) { continue; } } if (eFilter != nullptr) { - auto& eFilterVal = eFilter->eval(ctx(iter)); + const auto& eFilterVal = eFilter->eval(ctx(iter)); if (!eFilterVal.isBool() || !eFilterVal.getBool()) { continue; } } + auto& dst = iter->getEdgeProp("*", kDst); auto srcV = iter->getVertex(); auto e = iter->getEdge(); // Join on dst = src @@ -390,9 +370,7 @@ StatusOr TraverseExecutor::handleJob(size_t begin, if (hasSameEdge(prevPath, e.getEdge())) { continue; } - if (uniqueDsts_.emplace(dst, 0).second) { - reqDs.rows.emplace_back(Row({std::move(dst)})); - } + vids.emplace(dst); if (currentStep_ == 1) { Row path; if (traverse_->trackPrevPath()) { @@ -417,9 +395,7 @@ StatusOr TraverseExecutor::handleJob(size_t begin, return jobResult; } -void TraverseExecutor::buildPath(std::unordered_map>& currentPaths, - const Value& dst, - Row&& path) { +void TraverseExecutor::buildPath(HashMap& currentPaths, const Value& dst, Row&& path) { auto pathToDstFound = currentPaths.find(dst); if (pathToDstFound == currentPaths.end()) { Paths interimPaths; @@ -482,11 +458,11 @@ void TraverseExecutor::releasePrevPaths(size_t cnt) { } } -Status TraverseExecutor::handleZeroStep(const std::unordered_map& prev, +Status TraverseExecutor::handleZeroStep(const HashMap& prev, List&& vertices, - std::unordered_map& zeroSteps, + HashMap& zeroSteps, size_t& count) { - std::unordered_set uniqueSrc; + HashSet uniqueSrc; for (auto& srcV : vertices.values) { auto src = srcV.getVertex().vid; if (!uniqueSrc.emplace(src).second) { diff --git a/src/graph/executor/query/TraverseExecutor.h b/src/graph/executor/query/TraverseExecutor.h index bdfe92c5465..7fe4e0a3b61 100644 --- a/src/graph/executor/query/TraverseExecutor.h +++ b/src/graph/executor/query/TraverseExecutor.h @@ -5,6 +5,8 @@ #ifndef EXECUTOR_QUERY_TRAVERSEEXECUTOR_H_ #define EXECUTOR_QUERY_TRAVERSEEXECUTOR_H_ +#include + #include "graph/executor/StorageAccessExecutor.h" #include "graph/planner/plan/Query.h" #include "interface/gen-cpp2/storage_types.h" @@ -35,14 +37,16 @@ namespace graph { using RpcResponse = storage::StorageRpcResponse; using Dst = Value; using Paths = std::vector; +using HashSet = robin_hood::unordered_flat_set>; +using HashMap = robin_hood::unordered_flat_map>; struct JobResult { // Newly traversed paths size size_t pathCnt{0}; - // Request dataset for next traverse - DataSet reqDs; + // Request vids for next traverse + HashSet vids; // Newly traversed paths - std::unordered_map newPaths; + HashMap newPaths; }; class TraverseExecutor final : public StorageAccessExecutor { @@ -57,7 +61,7 @@ class TraverseExecutor final : public StorageAccessExecutor { Status close() override; private: - Status buildRequestDataSet(); + Status buildRequestVids(); folly::Future traverse(); @@ -71,10 +75,7 @@ class TraverseExecutor final : public StorageAccessExecutor { folly::Future buildInterimPathMultiJobs(std::unique_ptr iter); - StatusOr handleJob(size_t begin, - size_t end, - Iterator* iter, - const std::unordered_map& prev); + StatusOr handleJob(size_t begin, size_t end, Iterator* iter, const HashMap& prev); Status buildResult(); @@ -91,26 +92,20 @@ class TraverseExecutor final : public StorageAccessExecutor { void releasePrevPaths(size_t cnt); - void buildPath(std::unordered_map>& currentPaths, - const Value& dst, - Row&& path); + void buildPath(HashMap& currentPaths, const Value& dst, Row&& path); - Status handleZeroStep(const std::unordered_map& prev, - List&& vertices, - std::unordered_map& zeroSteps, - size_t& count); + Status handleZeroStep(const HashMap& prev, List&& vertices, HashMap& zeroSteps, size_t& count); Expression* selectFilter(); private: ObjectPool objPool_; - DataSet reqDs_; + HashSet vids_; const Traverse* traverse_{nullptr}; MatchStepRange* range_{nullptr}; size_t currentStep_{0}; - std::list> paths_; + std::list paths_; size_t totalPathCnt_{0}; - folly::ConcurrentHashMap uniqueDsts_; }; } // namespace graph diff --git a/src/graph/executor/test/GetNeighborsTest.cpp b/src/graph/executor/test/GetNeighborsTest.cpp index 55c3ba5e48c..10eafa8f9c7 100644 --- a/src/graph/executor/test/GetNeighborsTest.cpp +++ b/src/graph/executor/test/GetNeighborsTest.cpp @@ -69,17 +69,14 @@ TEST_F(GetNeighborsTest, BuildRequestDataSet) { gn->setInputVar("input_gn"); auto gnExe = std::make_unique(gn, qctx_.get()); - auto res = gnExe->buildRequestDataSet(); - auto reqDs = std::move(res).value(); + auto res = gnExe->buildRequestVids(); + auto reqVids = std::move(res).value(); - DataSet expected; - expected.colNames = {kVid}; + std::vector expectedVids; for (auto i = 0; i < 10; ++i) { - Row row; - row.values.emplace_back(folly::to(i)); - expected.rows.emplace_back(std::move(row)); + expectedVids.emplace_back(folly::to(i)); } - EXPECT_EQ(reqDs, expected); + EXPECT_EQ(reqVids, expectedVids); } } // namespace graph } // namespace nebula diff --git a/src/graph/validator/GoValidator.cpp b/src/graph/validator/GoValidator.cpp index 3c0a4b8d153..0dccee80822 100644 --- a/src/graph/validator/GoValidator.cpp +++ b/src/graph/validator/GoValidator.cpp @@ -187,20 +187,34 @@ void GoValidator::extractPropExprs(const Expression* expr, Expression* GoValidator::rewriteVertexEdge2EdgeProp(const Expression* expr) { auto pool = qctx_->objPool(); - const auto& name = expr->toString(); - if (name == "id($^)" || name == "src(edge)") { - return EdgeSrcIdExpression::make(pool, "*"); - } - if (name == "id($$)" || name == "dst(edge)") { - return EdgeDstIdExpression::make(pool, "*"); - } - if (name == "rank(edge)") { - return EdgeRankExpression::make(pool, "*"); - } - if (name == "type(edge)") { - return EdgeTypeExpression::make(pool, "*"); - } - return const_cast(expr); + auto matcher = [](const Expression* e) -> bool { + if (e->kind() != Expression::Kind::kFunctionCall) { + return false; + } + auto name = e->toString(); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + std::unordered_set colNames({"id($$)", "id($^)", "rank(edge)", "typeid(edge)"}); + if (colNames.find(name) != colNames.end()) { + return true; + } + return false; + }; + + auto rewriter = [&, pool](const Expression* e) -> Expression* { + DCHECK(e->kind() == Expression::Kind::kFunctionCall); + auto name = e->toString(); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + if (name == "id($$)") { + return EdgeDstIdExpression::make(pool, "*"); + } else if (name == "id($^)") { + return EdgeSrcIdExpression::make(pool, "*"); + } else if (name == "rank(edge)") { + return EdgeRankExpression::make(pool, "*"); + } else { + return EdgeTypeExpression::make(pool, "*"); + } + }; + return RewriteVisitor::transform(expr, std::move(matcher), std::move(rewriter)); } // Rewrites the property expression to corresponding Variable/Input expression diff --git a/src/interface/storage.thrift b/src/interface/storage.thrift index 29c125977e3..daf11e02f73 100644 --- a/src/interface/storage.thrift +++ b/src/interface/storage.thrift @@ -169,7 +169,7 @@ struct GetNeighborsRequest { // Column names for input data. The first column name must be "_vid" 2: list column_names, // partId => rows - 3: map> + 3: map> (cpp.template = "std::unordered_map") parts, 4: TraverseSpec traverse_spec, 5: optional RequestCommon common, diff --git a/src/storage/query/GetNeighborsProcessor.cpp b/src/storage/query/GetNeighborsProcessor.cpp index b23ca18b791..c978697c468 100644 --- a/src/storage/query/GetNeighborsProcessor.cpp +++ b/src/storage/query/GetNeighborsProcessor.cpp @@ -82,9 +82,8 @@ void GetNeighborsProcessor::runInSingleThread(const cpp2::GetNeighborsRequest& r for (const auto& partEntry : req.get_parts()) { contexts_.front().resultStat_ = ResultStatus::NORMAL; auto partId = partEntry.first; - for (const auto& row : partEntry.second) { - CHECK_GE(row.values.size(), 1); - auto vId = row.values[0].getStr(); + for (const auto& vid : partEntry.second) { + auto vId = vid.getStr(); if (!NebulaKeyUtils::isValidVidLen(spaceVidLen_, vId)) { LOG(INFO) << "Space " << spaceId_ << ", vertex length invalid, " @@ -122,9 +121,9 @@ void GetNeighborsProcessor::runInMultipleThread(const cpp2::GetNeighborsRequest& } size_t i = 0; std::vector>> futures; - for (const auto& [partId, rows] : req.get_parts()) { + for (const auto& [partId, vids] : req.get_parts()) { futures.emplace_back( - runInExecutor(&contexts_[i], &expCtxs_[i], &results_[i], partId, rows, limit, random)); + runInExecutor(&contexts_[i], &expCtxs_[i], &results_[i], partId, vids, limit, random)); i++; } @@ -150,15 +149,14 @@ folly::Future> GetNeighborsProce StorageExpressionContext* expCtx, nebula::DataSet* result, PartitionID partId, - const std::vector& rows, + const std::vector& vids, int64_t limit, bool random) { return folly::via( - executor_, [this, context, expCtx, result, partId, input = std::move(rows), limit, random]() { + executor_, [this, context, expCtx, result, partId, input = std::move(vids), limit, random]() { auto plan = buildPlan(context, expCtx, result, limit, random); - for (const auto& row : input) { - CHECK_GE(row.values.size(), 1); - auto vId = row.values[0].getStr(); + for (const auto& vid : input) { + auto vId = vid.getStr(); if (!NebulaKeyUtils::isValidVidLen(spaceVidLen_, vId)) { LOG(INFO) << "Space " << spaceId_ << ", vertex length invalid, " diff --git a/src/storage/query/GetNeighborsProcessor.h b/src/storage/query/GetNeighborsProcessor.h index 5e634a6fcad..60d09c3a9eb 100644 --- a/src/storage/query/GetNeighborsProcessor.h +++ b/src/storage/query/GetNeighborsProcessor.h @@ -80,7 +80,7 @@ class GetNeighborsProcessor StorageExpressionContext* expCtx, nebula::DataSet* result, PartitionID partId, - const std::vector& rows, + const std::vector& vids, int64_t limit, bool random); void profilePlan(StoragePlan& plan); diff --git a/src/storage/test/QueryTestUtils.h b/src/storage/test/QueryTestUtils.h index cad9eda85e1..0f5a2ea6661 100644 --- a/src/storage/test/QueryTestUtils.h +++ b/src/storage/test/QueryTestUtils.h @@ -307,9 +307,7 @@ class QueryTestUtils { (*req.column_names_ref()).emplace_back(kVid); for (const auto& vertex : vertices) { PartitionID partId = (hash(vertex) % totalParts) + 1; - nebula::Row row; - row.values.emplace_back(vertex); - (*req.parts_ref())[partId].emplace_back(std::move(row)); + (*req.parts_ref())[partId].emplace_back(vertex); } for (const auto& edge : over) { (*traverseSpec.edge_types_ref()).emplace_back(edge); diff --git a/src/tools/storage-perf/StoragePerfTool.cpp b/src/tools/storage-perf/StoragePerfTool.cpp index f507c06237c..b576ad46d32 100644 --- a/src/tools/storage-perf/StoragePerfTool.cpp +++ b/src/tools/storage-perf/StoragePerfTool.cpp @@ -282,11 +282,9 @@ class Perf { auto* evb = threadPool_->getEventBase(); std::vector colNames; colNames.emplace_back(kVid); - std::vector vertices; + std::vector vids; for (auto& vertex : randomVertices()) { - nebula::Row row; - row.values.emplace_back(vertex); - vertices.emplace_back(row); + vids.emplace_back(vertex); } cpp2::EdgeDirection edgeDire = cpp2::EdgeDirection::BOTH; @@ -300,15 +298,8 @@ class Perf { StorageClient::CommonRequestParam param(spaceId_, 0, 0, false); storageClient_ - ->getNeighbors(param, - colNames, - vertices, - {edgeType_}, - edgeDire, - &statProps, - &vProps, - &eProps, - nullptr) + ->getNeighbors( + param, colNames, vids, {edgeType_}, edgeDire, &statProps, &vProps, &eProps, nullptr) .via(evb) .thenValue([this, start](auto&& resps) { if (!resps.succeeded()) { diff --git a/tests/tck/features/match/SingleShorestPath.feature b/tests/tck/features/match/SingleShorestPath.feature index 8a9adf506f2..a1f53e3eeac 100644 --- a/tests/tck/features/match/SingleShorestPath.feature +++ b/tests/tck/features/match/SingleShorestPath.feature @@ -66,34 +66,18 @@ Feature: single shortestPath | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})> | When executing query: """ - MATCH p = shortestPath( (a:player{age:30})-[e*..5]->(b:team) ) RETURN p + MATCH p = shortestPath( (a:player{age:30})-[e*..5]->(b:team) ) WHERE id(a) != 'Blake Griffin' RETURN p """ Then the result should be, in any order, with relax comparison: - | p | - | <("DeAndre Jordan" :player{age: 30, name: "DeAndre Jordan"})-[:serve@0 {end_year: 2019, start_year: 2019}]->("Knicks" :team{name: "Knicks"})> | - | <("DeAndre Jordan" :player{age: 30, name: "DeAndre Jordan"})-[:serve@0 {end_year: 2019, start_year: 2018}]->("Mavericks" :team{name: "Mavericks"})> | - | <("DeAndre Jordan" :player{age: 30, name: "DeAndre Jordan"})-[:serve@0 {end_year: 2018, start_year: 2008}]->("Clippers" :team{name: "Clippers"})> | - | <("Kevin Durant" :player{age: 30, name: "Kevin Durant"})-[:serve@0 {end_year: 2016, start_year: 2007}]->("Thunders" :team{name: "Thunders"})> | - | <("Kevin Durant" :player{age: 30, name: "Kevin Durant"})-[:serve@0 {end_year: 2019, start_year: 2016}]->("Warriors" :team{name: "Warriors"})> | - | <("Russell Westbrook" :player{age: 30, name: "Russell Westbrook"})-[:like@0 {likeness: 90}]->("James Harden" :player{age: 29, name: "James Harden"})-[:serve@0 {end_year: 2019, start_year: 2012}]->("Rockets" :team{name: "Rockets"})> | - | <("Russell Westbrook" :player{age: 30, name: "Russell Westbrook"})-[:serve@0 {end_year: 2019, start_year: 2008}]->("Thunders" :team{name: "Thunders"})> | - | <("Russell Westbrook" :player{age: 30, name: "Russell Westbrook"})-[:like@0 {likeness: 90}]->("Paul George" :player{age: 28, name: "Paul George"})-[:serve@0 {end_year: 2017, start_year: 2010}]->("Pacers" :team{name: "Pacers"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:serve@0 {end_year: 2014, start_year: 2010}]->("Heat" :team{name: "Heat"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:like@0 {likeness: 100}]->("Ray Allen" :player{age: 43, name: "Ray Allen"})-[:like@0 {likeness: 9}]->("Rajon Rondo" :player{age: 33, name: "Rajon Rondo"})-[:serve@0 {end_year: 2016, start_year: 2015}]->("Kings" :team{name: "Kings"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:serve@0 {end_year: 2019, start_year: 2018}]->("Lakers" :team{name: "Lakers"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("Carmelo Anthony" :player{age: 34, name: "Carmelo Anthony"})-[:serve@0 {end_year: 2017, start_year: 2011}]->("Knicks" :team{name: "Knicks"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:serve@0 {end_year: 2011, start_year: 2005}]->("Hornets" :team{name: "Hornets"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:like@0 {likeness: 100}]->("Ray Allen" :player{age: 43, name: "Ray Allen"})-[:like@0 {likeness: 9}]->("Rajon Rondo" :player{age: 33, name: "Rajon Rondo"})-[:serve@0 {end_year: 2015, start_year: 2014}]->("Mavericks" :team{name: "Mavericks"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:serve@0 {end_year: 2018, start_year: 2009}]->("Clippers" :team{name: "Clippers"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("Dwyane Wade" :player{age: 37, name: "Dwyane Wade"})-[:serve@0 {end_year: 2017, start_year: 2016}]->("Bulls" :team{name: "Bulls"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:like@0 {likeness: 100}]->("Ray Allen" :player{age: 43, name: "Ray Allen"})-[:serve@0 {end_year: 2003, start_year: 1996}]->("Bucks" :team{name: "Bucks"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("Carmelo Anthony" :player{age: 34, name: "Carmelo Anthony"})-[:serve@0 {end_year: 2011, start_year: 2003}]->("Nuggets" :team{name: "Nuggets"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:like@0 {likeness: 100}]->("Ray Allen" :player{age: 43, name: "Ray Allen"})-[:serve@0 {end_year: 2012, start_year: 2007}]->("Celtics" :team{name: "Celtics"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:like@0 {likeness: 100}]->("Ray Allen" :player{age: 43, name: "Ray Allen"})-[:like@0 {likeness: 9}]->("Rajon Rondo" :player{age: 33, name: "Rajon Rondo"})-[:serve@0 {end_year: 2018, start_year: 2017}]->("Pelicans" :team{name: "Pelicans"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:serve@0 {end_year: 2021, start_year: 2017}]->("Rockets" :team{name: "Rockets"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("Carmelo Anthony" :player{age: 34, name: "Carmelo Anthony"})-[:serve@0 {end_year: 2018, start_year: 2017}]->("Thunders" :team{name: "Thunders"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:serve@0 {end_year: 2019, start_year: 2018}]->("Pistons" :team{name: "Pistons"})> | - | <("Blake Griffin" :player{age: 30, name: "Blake Griffin"})-[:like@0 {likeness: -1}]->("Chris Paul" :player{age: 33, name: "Chris Paul"})-[:like@0 {likeness: 90}]->("LeBron James" :player{age: 34, name: "LeBron James"})-[:serve@0 {end_year: 2010, start_year: 2003}]->("Cavaliers" :team{name: "Cavaliers"})> | + | p | + | <("DeAndre Jordan" :player{age: 30, name: "DeAndre Jordan"})-[:serve@0 {end_year: 2019, start_year: 2019}]->("Knicks" :team{name: "Knicks"})> | + | <("DeAndre Jordan" :player{age: 30, name: "DeAndre Jordan"})-[:serve@0 {end_year: 2019, start_year: 2018}]->("Mavericks" :team{name: "Mavericks"})> | + | <("DeAndre Jordan" :player{age: 30, name: "DeAndre Jordan"})-[:serve@0 {end_year: 2018, start_year: 2008}]->("Clippers" :team{name: "Clippers"})> | + | <("Kevin Durant" :player{age: 30, name: "Kevin Durant"})-[:serve@0 {end_year: 2016, start_year: 2007}]->("Thunders" :team{name: "Thunders"})> | + | <("Kevin Durant" :player{age: 30, name: "Kevin Durant"})-[:serve@0 {end_year: 2019, start_year: 2016}]->("Warriors" :team{name: "Warriors"})> | + | <("Russell Westbrook" :player{age: 30, name: "Russell Westbrook"})-[:like@0 {likeness: 90}]->("James Harden" :player{age: 29, name: "James Harden"})-[:serve@0 {end_year: 2019, start_year: 2012}]->("Rockets" :team{name: "Rockets"})> | + | <("Russell Westbrook" :player{age: 30, name: "Russell Westbrook"})-[:serve@0 {end_year: 2019, start_year: 2008}]->("Thunders" :team{name: "Thunders"})> | + | <("Russell Westbrook" :player{age: 30, name: "Russell Westbrook"})-[:like@0 {likeness: 90}]->("Paul George" :player{age: 28, name: "Paul George"})-[:serve@0 {end_year: 2017, start_year: 2010}]->("Pacers" :team{name: "Pacers"})> | When executing query: """ MATCH p = shortestPath( (a:player{age:30})-[e*..5]->(b:team) )