diff --git a/src/context/Iterator.cpp b/src/context/Iterator.cpp index 272a56d646c69f809245416b02541e0d676930e2..03f59ae9cac498fcce92443e5774f31b224f4801 100644 --- a/src/context/Iterator.cpp +++ b/src/context/Iterator.cpp @@ -170,11 +170,9 @@ Status GetNeighborsIter::buildPropIndex(const std::string& props, std::string name = pieces[1]; if (isEdge) { // The first character of the tag/edge name is +/-. - // It's not used for now. if (UNLIKELY(name.find("+") != 0 && name.find("-") != 0)) { return Status::Error("Bad edge name: %s", name.c_str()); } - name = name.substr(1, name.size()); dsIndex->tagEdgeNameIndices.emplace(columnId, name); dsIndex->edgePropsMap.emplace(name, std::move(propIdx)); } else { @@ -231,7 +229,8 @@ const Value& GetNeighborsIter::getEdgeProp(const std::string& edge, } auto currentEdge = currentEdgeName(); - if (edge != "*" && currentEdge != edge) { + if (edge != "*" && + (currentEdge.compare(1, std::string::npos, edge) != 0)) { VLOG(1) << "Current edge: " << currentEdgeName() << " Wanted: " << edge; return Value::kNullValue; } @@ -239,6 +238,7 @@ const Value& GetNeighborsIter::getEdgeProp(const std::string& edge, auto index = dsIndices_[segment].edgePropsMap.find(currentEdge); if (index == dsIndices_[segment].edgePropsMap.end()) { VLOG(1) << "No edge found: " << edge; + VLOG(1) << "Current edge: " << currentEdge; return Value::kNullValue; } auto propIndex = index->second.propIndices.find(prop); @@ -291,7 +291,7 @@ Value GetNeighborsIter::getEdge() const { auto segment = currentSeg(); Edge edge; - auto& edgeName = currentEdgeName(); + auto edgeName = currentEdgeName().substr(1, std::string::npos); edge.name = edgeName; auto& src = getColumn(kVid); if (!src.isStr()) { @@ -313,7 +313,7 @@ Value GetNeighborsIter::getEdge() const { edge.type = 0; auto& edgePropMap = dsIndices_[segment].edgePropsMap; - auto edgeProp = edgePropMap.find(edgeName); + auto edgeProp = edgePropMap.find(currentEdgeName()); if (edgeProp == edgePropMap.end()) { return Value::kNullValue; } diff --git a/src/context/Iterator.h b/src/context/Iterator.h index f316d371d9bc017cd4ccdf97f6bfd54bddc0ada7..7fc4666108944de48c4e973deb7ee382bbf21857 100644 --- a/src/context/Iterator.h +++ b/src/context/Iterator.h @@ -510,6 +510,13 @@ public: } } + // TODO: We should build new iter for get props, the seq iter will + // not meet the requirements of match any more. + const Value& getTagProp(const std::string& tag, + const std::string& prop) const override { + return getColumn(tag+ "." + prop); + } + const Value& getEdgeProp(const std::string& edge, const std::string& prop) const override { return getColumn(edge + "." + prop); diff --git a/src/exec/query/GetNeighborsExecutor.cpp b/src/exec/query/GetNeighborsExecutor.cpp index 4f894fe50e4874c7d13630179f2a5017cf815235..8c96feacc83387c0d60169db5a5a463bd08bb771 100644 --- a/src/exec/query/GetNeighborsExecutor.cpp +++ b/src/exec/query/GetNeighborsExecutor.cpp @@ -43,14 +43,16 @@ Status GetNeighborsExecutor::buildRequestDataSet() { reqDs_.colNames = {kVid}; reqDs_.rows.reserve(iter->size()); auto* src = gn_->src(); + std::unordered_set<Value> uniqueVid; for (; iter->valid(); iter->next()) { auto val = Expression::eval(src, ctx); if (!val.isStr()) { continue; } - Row row; - row.values.emplace_back(std::move(val)); - reqDs_.rows.emplace_back(std::move(row)); + auto ret = uniqueVid.emplace(val); + if (ret.second) { + reqDs_.rows.emplace_back(Row({std::move(val)})); + } } return Status::OK(); diff --git a/src/exec/query/GetPropExecutor.h b/src/exec/query/GetPropExecutor.h index da670a64499133fe7362b01f9497b74d55b7281f..0aa6f5d86793669e4c606d59d6c334dbb1741c0f 100644 --- a/src/exec/query/GetPropExecutor.h +++ b/src/exec/query/GetPropExecutor.h @@ -39,6 +39,7 @@ protected: for (auto &colName : v.colNames) { std::replace(colName.begin(), colName.end(), ':', '.'); } + VLOG(1) << "Resp: " << v; return finish(ResultBuilder() .value(std::move(v)) .iter(Iterator::Kind::kSequential) diff --git a/src/exec/query/GetVerticesExecutor.cpp b/src/exec/query/GetVerticesExecutor.cpp index 76d56e621a440a3dd47f4edc1e3145c72296024c..7fe67a87a6bf918aa625d4b23e08c3a95709fcf2 100644 --- a/src/exec/query/GetVerticesExecutor.cpp +++ b/src/exec/query/GetVerticesExecutor.cpp @@ -29,7 +29,14 @@ folly::Future<Status> GetVerticesExecutor::getVertices() { GraphStorageClient *storageClient = qctx()->getStorageClient(); nebula::DataSet vertices({kVid}); + std::unordered_set<Value> uniqueVid; if (!gv->vertices().empty()) { + for (auto& v : gv->vertices()) { + auto ret = uniqueVid.emplace(v.values.front()); + if (ret.second) { + vertices.emplace_back(std::move(v)); + } + } vertices.rows.insert(vertices.rows.end(), std::make_move_iterator(gv->vertices().begin()), std::make_move_iterator(gv->vertices().end())); @@ -37,18 +44,27 @@ folly::Future<Status> GetVerticesExecutor::getVertices() { if (gv->src() != nullptr) { // Accept Table such as | $a | $b | $c |... as input which one column indicate src auto valueIter = ectx_->getResult(gv->inputVar()).iter(); + VLOG(1) << "GV input var: " << gv->inputVar() << " iter kind: " << valueIter->kind(); auto expCtx = QueryExpressionContext(qctx()->ectx(), valueIter.get()); for (; valueIter->valid(); valueIter->next()) { auto src = gv->src()->eval(expCtx); + VLOG(1) << "src vid: " << src; if (!src.isStr()) { - LOG(WARNING) << "Mismatched vid type."; + LOG(WARNING) << "Mismatched vid type: " << src.type(); continue; } - vertices.emplace_back(Row({ - std::move(src) - })); + auto ret = uniqueVid.emplace(src); + if (ret.second) { + vertices.emplace_back(Row({std::move(src)})); + } } } + + if (vertices.rows.empty()) { + // TODO: add test for empty input. + return finish(ResultBuilder().value(Value(DataSet())).finish()); + } + return DCHECK_NOTNULL(storageClient) ->getProps(gv->space(), std::move(vertices), diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h index 48f3430ca9cfae44e4eaec60be20976b06577b21..29fcc9c9b7684255ae24c1dfa47f280c3da0cb6e 100644 --- a/src/planner/PlanNode.h +++ b/src/planner/PlanNode.h @@ -107,7 +107,7 @@ public: void setId(int64_t id) { id_ = id; - outputVar_ = folly::stringPrintf("%s_%ld", toString(kind_), id_); + outputVar_ = folly::stringPrintf("__%s_%ld", toString(kind_), id_); } void setPlan(ExecutionPlan* plan) { diff --git a/src/validator/GoValidator.cpp b/src/validator/GoValidator.cpp index 18f89ec5aae277c14c9749cd9856567fcef6e7c2..1c689f68ac4906ff0f1eeb35a0ed02fcfbca1120 100644 --- a/src/validator/GoValidator.cpp +++ b/src/validator/GoValidator.cpp @@ -36,11 +36,6 @@ Status GoValidator::validateImpl() { NG_RETURN_IF_ERROR(buildColumns()); - if (!dstTagProps_.empty()) { - // TODO: inplement get vertex props. - return Status::Error("Not support get dst yet."); - } - if (isOverAll_) { // TODO: implement over all. return Status::Error("Not support over all yet."); @@ -85,7 +80,11 @@ Status GoValidator::validateFrom(const FromClause* from) { return Status::Error(ss.str()); } srcRef_ = src; - firstBeginningSrcVidColName_ = *(static_cast<SymbolPropertyExpression*>(src)->prop()); + auto* symPropExpr = static_cast<SymbolPropertyExpression*>(src); + if (fromType_ == kVariable) { + userDefinedVarName_ = *(symPropExpr->sym()); + } + firstBeginningSrcVidColName_ = *(symPropExpr->prop()); } } else { auto vidList = from->vidList(); @@ -204,28 +203,42 @@ Status GoValidator::oneStep(PlanNode* dependencyForGn, gn->setInputVar(inputVarNameForGN); VLOG(1) << gn->varName(); - if (!dstTagProps_.empty()) { - // TODO: inplement get vertex props. - return Status::Error("Not support get dst yet."); + PlanNode* dependencyForProjectResult = gn; + + PlanNode* projectSrcEdgeProps = nullptr; + if (!inputProps_.empty() || !varProps_.empty() || !dstTagProps_.empty()) { + projectSrcEdgeProps = buildProjectSrcEdgePropsForGN(gn); + } + + PlanNode* joinDstProps = nullptr; + if (!dstTagProps_.empty() && projectSrcEdgeProps != nullptr) { + joinDstProps = buildJoinDstProps(projectSrcEdgeProps); + } + if (joinDstProps != nullptr) { + dependencyForProjectResult = joinDstProps; } - PlanNode* inputNodeForProjectResult = gn; - auto* joinInput = ifBuildJoinPipeInput(gn, projectFromJoin); + PlanNode* joinInput = nullptr; + if (!inputProps_.empty() || !varProps_.empty()) { + joinInput = buildJoinPipeOrVariableInput( + projectFromJoin, + joinDstProps == nullptr ? projectSrcEdgeProps : joinDstProps); + } if (joinInput != nullptr) { - inputNodeForProjectResult = joinInput; + dependencyForProjectResult = joinInput; } if (filter_ != nullptr) { - auto* filterNode = Filter::make(plan, inputNodeForProjectResult, + auto* filterNode = Filter::make(plan, dependencyForProjectResult, newFilter_ != nullptr ? newFilter_ : filter_); - filterNode->setInputVar(inputNodeForProjectResult->varName()); - filterNode->setColNames(inputNodeForProjectResult->colNames()); - inputNodeForProjectResult = filterNode; + filterNode->setInputVar(dependencyForProjectResult->varName()); + filterNode->setColNames(dependencyForProjectResult->colNames()); + dependencyForProjectResult = filterNode; } auto* projectResult = - Project::make(plan, inputNodeForProjectResult, + Project::make(plan, dependencyForProjectResult, newYieldCols_ != nullptr ? newYieldCols_ : yields_); - projectResult->setInputVar(inputNodeForProjectResult->varName()); + projectResult->setInputVar(dependencyForProjectResult->varName()); projectResult->setColNames(std::vector<std::string>(colNames_)); if (distinct_) { Dedup* dedupNode = Dedup::make(plan, projectResult); @@ -253,17 +266,24 @@ Status GoValidator::buildNStepsPlan() { startVidsVar = projectStartVid->varName(); } - Project* projectLeftVarForJoin = ifBuildLeftVarForTraceJoin(projectStartVid); + Project* projectLeftVarForJoin = nullptr; + if (!inputProps_.empty() || !varProps_.empty()) { + projectLeftVarForJoin = buildLeftVarForTraceJoin(projectStartVid); + } auto* gn = GetNeighbors::make(plan, bodyStart, space_.id); gn->setSrc(src_); - gn->setEdgeProps(buildNStepLoopEdgeProps()); + gn->setEdgeProps(buildEdgeDst()); gn->setInputVar(startVidsVar); VLOG(1) << gn->varName(); Project* projectDstFromGN = projectDstVidsFromGN(gn, startVidsVar); - Project* projectFromJoin = ifTraceToStartVid(projectLeftVarForJoin, projectDstFromGN); + Project* projectFromJoin = nullptr; + if ((!inputProps_.empty() || !varProps_.empty()) && + projectLeftVarForJoin != nullptr && projectDstFromGN != nullptr) { + projectFromJoin = traceToStartVid(projectLeftVarForJoin, projectDstFromGN); + } auto* loop = Loop::make( plan, @@ -290,105 +310,167 @@ Status GoValidator::buildNStepsPlan() { return Status::OK(); } -PlanNode* GoValidator::ifBuildJoinPipeInput( - PlanNode* gn, PlanNode* projectFromJoin) { - PlanNode* inputNodeForProjectResult = nullptr; +PlanNode* GoValidator::buildProjectSrcEdgePropsForGN(PlanNode* gn) { + DCHECK(gn != nullptr); + auto* plan = qctx_->plan(); - if (!inputProps_.empty()) { + if (!inputProps_.empty() || !varProps_.empty()) { auto* srcVidCol = new YieldColumn( new VariablePropertyExpression(new std::string(gn->varName()), new std::string(kVid)), new std::string(kVid)); srcAndEdgePropCols_->addColumn(srcVidCol); - auto* project = Project::make(plan, gn, srcAndEdgePropCols_); - project->setInputVar(gn->varName()); - project->setColNames(deduceColNames(srcAndEdgePropCols_)); - VLOG(1) << project->varName(); - - PlanNode* joinInputHashKeyDependency = project; - if (steps_ > 1) { - auto* joinHashKey = new VariablePropertyExpression( - new std::string(project->varName()), new std::string(kVid)); - plan->saveObject(joinHashKey); - auto* probeKey = new VariablePropertyExpression( - new std::string(projectFromJoin->varName()), new std::string(dstVidColName_)); - plan->saveObject(probeKey); - auto* join = DataJoin::make( - plan, project, - {project->varName(), ExecutionContext::kLatestVersion}, - {projectFromJoin->varName(), ExecutionContext::kLatestVersion}, - {joinHashKey}, {probeKey}); - std::vector<std::string> colNames = project->colNames(); - for (auto& col : projectFromJoin->colNames()) { - colNames.emplace_back(col); - } - join->setColNames(std::move(colNames)); - VLOG(1) << join->varName(); - joinInputHashKeyDependency = join; - } + } - auto* joinHashKey = new VariablePropertyExpression( - new std::string(joinInputHashKeyDependency->varName()), - new std::string(steps_ > 1 ? firstBeginningSrcVidColName_ : kVid)); - plan->saveObject(joinHashKey); - auto* joinInput = - DataJoin::make(plan, joinInputHashKeyDependency, - {joinInputHashKeyDependency->varName(), - ExecutionContext::kLatestVersion}, - {inputVarName_, ExecutionContext::kLatestVersion}, - {joinHashKey}, {steps_ > 1 ? srcRef_ : src_}); - std::vector<std::string> colNames = joinInputHashKeyDependency->colNames(); - for (auto& col : outputs_) { - colNames.emplace_back(col.first); - } - joinInput->setColNames(std::move(colNames)); - VLOG(1) << joinInput->varName(); - inputNodeForProjectResult = joinInput; + VLOG(1) << "build dst cols"; + if (!dstTagProps_.empty()) { + joinDstVidColName_ = vctx_->anonColGen()->getCol(); + auto* dstVidCol = new YieldColumn( + new EdgePropertyExpression(new std::string("*"), + new std::string(kDst)), + new std::string(joinDstVidColName_)); + srcAndEdgePropCols_->addColumn(dstVidCol); } - return inputNodeForProjectResult; + auto* project = Project::make(plan, gn, srcAndEdgePropCols_); + project->setInputVar(gn->varName()); + project->setColNames(deduceColNames(srcAndEdgePropCols_)); + VLOG(1) << project->varName(); + + return project; } -Project* GoValidator::ifTraceToStartVid(Project* projectLeftVarForJoin, - Project* projectDstFromGN) { - Project* projectJoin = nullptr; +PlanNode* GoValidator::buildJoinDstProps(PlanNode* projectSrcDstProps) { + DCHECK(dstPropCols_ != nullptr); + DCHECK(projectSrcDstProps != nullptr); + auto* plan = qctx_->plan(); - if (!inputProps_.empty() && projectLeftVarForJoin != nullptr) { - auto hashKey = new VariablePropertyExpression( - new std::string(projectLeftVarForJoin->varName()), - new std::string(dstVidColName_)); - plan->saveObject(hashKey); - auto probeKey = new VariablePropertyExpression( - new std::string(projectDstFromGN->varName()), new std::string(srcVidColName_)); + + auto* vids = new VariablePropertyExpression( + new std::string(projectSrcDstProps->varName()), + new std::string(joinDstVidColName_)); + plan->saveObject(vids); + auto* getDstVertices = + GetVertices::make(plan, projectSrcDstProps, space_.id, {}, vids, + buildDstVertexProps(), {}); + getDstVertices->setInputVar(projectSrcDstProps->varName()); + + auto vidColName = vctx_->anonColGen()->getCol(); + auto* vidCol = new YieldColumn( + new VariablePropertyExpression(new std::string(getDstVertices->varName()), + new std::string(kVid)), + new std::string(vidColName)); + dstPropCols_->addColumn(vidCol); + auto* project = Project::make(plan, getDstVertices, dstPropCols_); + project->setInputVar(getDstVertices->varName()); + project->setColNames(deduceColNames(dstPropCols_)); + + auto* joinHashKey = new VariablePropertyExpression( + new std::string(projectSrcDstProps->varName()), + new std::string(joinDstVidColName_)); + plan->saveObject(joinHashKey); + auto* probeKey = new VariablePropertyExpression( + new std::string(project->varName()), new std::string(vidColName)); + plan->saveObject(probeKey); + auto joinDst = DataJoin::make(plan, project, + {projectSrcDstProps->varName(), ExecutionContext::kLatestVersion}, + {project->varName(), ExecutionContext::kLatestVersion}, + {joinHashKey}, {probeKey}); + VLOG(1) << joinDst->varName() << " hash key: " << joinHashKey->toString() + << " probe key: " << probeKey->toString(); + + return joinDst; +} + +PlanNode* GoValidator::buildJoinPipeOrVariableInput( + PlanNode* projectFromJoin, PlanNode* dependencyForJoinInput) { + auto* plan = qctx_->plan(); + + if (steps_ > 1) { + DCHECK(projectFromJoin != nullptr); + auto* joinHashKey = new VariablePropertyExpression( + new std::string(dependencyForJoinInput->varName()), new std::string(kVid)); + plan->saveObject(joinHashKey); + auto* probeKey = new VariablePropertyExpression( + new std::string(projectFromJoin->varName()), new std::string(dstVidColName_)); plan->saveObject(probeKey); auto* join = DataJoin::make( - plan, projectDstFromGN, - {projectLeftVarForJoin->varName(), - ExecutionContext::kLatestVersion}, - {projectDstFromGN->varName(), ExecutionContext::kLatestVersion}, - {hashKey}, {probeKey}); - std::vector<std::string> colNames = projectLeftVarForJoin->colNames(); - for (auto& col : projectDstFromGN->colNames()) { + plan, dependencyForJoinInput, + {dependencyForJoinInput->varName(), ExecutionContext::kLatestVersion}, + {projectFromJoin->varName(), ExecutionContext::kLatestVersion}, + {joinHashKey}, {probeKey}); + std::vector<std::string> colNames = dependencyForJoinInput->colNames(); + for (auto& col : projectFromJoin->colNames()) { colNames.emplace_back(col); } join->setColNames(std::move(colNames)); VLOG(1) << join->varName(); + dependencyForJoinInput = join; + } + + DCHECK(dependencyForJoinInput != nullptr); + auto* joinHashKey = new VariablePropertyExpression( + new std::string(dependencyForJoinInput->varName()), + new std::string(steps_ > 1 ? firstBeginningSrcVidColName_ : kVid)); + plan->saveObject(joinHashKey); + auto* joinInput = + DataJoin::make(plan, dependencyForJoinInput, + {dependencyForJoinInput->varName(), + ExecutionContext::kLatestVersion}, + {fromType_ == kPipe ? inputVarName_ : userDefinedVarName_, + ExecutionContext::kLatestVersion}, + {joinHashKey}, {steps_ > 1 ? srcRef_ : src_}); + std::vector<std::string> colNames = dependencyForJoinInput->colNames(); + for (auto& col : outputs_) { + colNames.emplace_back(col.first); + } + joinInput->setColNames(std::move(colNames)); + VLOG(1) << joinInput->varName(); + + return joinInput; +} + +Project* GoValidator::traceToStartVid(Project* projectLeftVarForJoin, + Project* projectDstFromGN) { + DCHECK(projectLeftVarForJoin != nullptr); + DCHECK(projectDstFromGN != nullptr); + + auto* plan = qctx_->plan(); + auto hashKey = new VariablePropertyExpression( + new std::string(projectLeftVarForJoin->varName()), + new std::string(dstVidColName_)); + plan->saveObject(hashKey); + auto probeKey = new VariablePropertyExpression( + new std::string(projectDstFromGN->varName()), new std::string(srcVidColName_)); + plan->saveObject(probeKey); + auto* join = DataJoin::make( + plan, projectDstFromGN, + {projectLeftVarForJoin->varName(), + ExecutionContext::kLatestVersion}, + {projectDstFromGN->varName(), ExecutionContext::kLatestVersion}, + {hashKey}, {probeKey}); + std::vector<std::string> colNames = projectLeftVarForJoin->colNames(); + for (auto& col : projectDstFromGN->colNames()) { + colNames.emplace_back(col); + } + join->setColNames(std::move(colNames)); + VLOG(1) << join->varName(); + + auto* columns = new YieldColumns(); + auto* column = + new YieldColumn(new InputPropertyExpression( + new std::string(firstBeginningSrcVidColName_)), + new std::string(firstBeginningSrcVidColName_)); + columns->addColumn(column); + column = + new YieldColumn(new InputPropertyExpression(new std::string(kVid)), + new std::string(dstVidColName_)); + columns->addColumn(column); + auto* projectJoin = Project::make(plan, join, plan->saveObject(columns)); + projectJoin->setInputVar(join->varName()); + projectJoin->setColNames(deduceColNames(columns)); + VLOG(1) << projectJoin->varName(); - auto* columns = new YieldColumns(); - auto* column = - new YieldColumn(new InputPropertyExpression( - new std::string(firstBeginningSrcVidColName_)), - new std::string(firstBeginningSrcVidColName_)); - columns->addColumn(column); - column = - new YieldColumn(new InputPropertyExpression(new std::string(kVid)), - new std::string(dstVidColName_)); - columns->addColumn(column); - projectJoin = Project::make(plan, join, plan->saveObject(columns)); - projectJoin->setInputVar(join->varName()); - projectJoin->setColNames(deduceColNames(columns)); - VLOG(1) << projectJoin->varName(); - } return projectJoin; } @@ -402,7 +484,7 @@ Project* GoValidator::projectDstVidsFromGN(PlanNode* gn, const std::string& outp columns->addColumn(column); srcVidColName_ = vctx_->anonColGen()->getCol(); - if (!inputProps_.empty()) { + if (!inputProps_.empty() || !varProps_.empty()) { column = new YieldColumn(new InputPropertyExpression(new std::string(kVid)), new std::string(srcVidColName_)); @@ -418,35 +500,39 @@ Project* GoValidator::projectDstVidsFromGN(PlanNode* gn, const std::string& outp return project; } -Project* GoValidator::ifBuildLeftVarForTraceJoin(PlanNode* projectStartVid) { - Project* projectLeftVarForJoin = nullptr; +Project* GoValidator::buildLeftVarForTraceJoin(PlanNode* projectStartVid) { auto* plan = qctx_->plan(); dstVidColName_ = vctx_->anonColGen()->getCol(); - if (!inputProps_.empty()) { - auto* columns = new YieldColumns(); - auto* column = - new YieldColumn(Expression::decode(srcRef_->encode()).release(), - new std::string(firstBeginningSrcVidColName_)); - columns->addColumn(column); - column = - new YieldColumn(Expression::decode(srcRef_->encode()).release(), - new std::string(dstVidColName_)); - columns->addColumn(column); - plan->saveObject(columns); - projectLeftVarForJoin = Project::make(plan, projectStartVid, columns); - projectLeftVarForJoin->setInputVar(inputVarName_); - projectLeftVarForJoin->setColNames(deduceColNames(columns)); - } + auto* columns = new YieldColumns(); + auto* column = + new YieldColumn(Expression::decode(srcRef_->encode()).release(), + new std::string(firstBeginningSrcVidColName_)); + columns->addColumn(column); + column = + new YieldColumn(Expression::decode(srcRef_->encode()).release(), + new std::string(dstVidColName_)); + columns->addColumn(column); + plan->saveObject(columns); + // projectStartVid could be nullptr, that means no input for this project. + auto* projectLeftVarForJoin = Project::make(plan, projectStartVid, columns); + projectLeftVarForJoin->setInputVar( + fromType_ == kPipe ? inputVarName_ : userDefinedVarName_); + projectLeftVarForJoin->setColNames(deduceColNames(columns)); return projectLeftVarForJoin; } Status GoValidator::buildOneStepPlan() { - std::string inputVarNameForGN = inputVarName_; + std::string inputVarNameForGN; if (!starts_.empty() && srcRef_ == nullptr) { inputVarNameForGN = buildConstantInput(); } else { src_ = srcRef_; + if (fromType_ == kVariable) { + inputVarNameForGN = userDefinedVarName_; + } else { + inputVarNameForGN = inputVarName_; + } } auto status = oneStep(nullptr, inputVarNameForGN, nullptr); @@ -484,7 +570,11 @@ PlanNode* GoValidator::buildRuntimeInput() { columns->addColumn(column); auto plan = qctx_->plan(); auto* project = Project::make(plan, nullptr, plan->saveObject(columns)); + if (fromType_ == kVariable) { + project->setInputVar(userDefinedVarName_); + } project->setColNames({ kVid }); + VLOG(1) << project->varName() << " input: " << project->inputVar(); src_ = plan->saveObject(new InputPropertyExpression(new std::string(kVid))); return project; } @@ -505,13 +595,11 @@ GetNeighbors::VertexProps GoValidator::buildSrcVertexProps() { return vertexProps; } -GetNeighbors::VertexProps GoValidator::buildDstVertexProps() { - GetNeighbors::VertexProps vertexProps; +std::vector<storage::cpp2::VertexProp> GoValidator::buildDstVertexProps() { + std::vector<storage::cpp2::VertexProp> vertexProps(dstTagProps_.size()); if (!dstTagProps_.empty()) { - vertexProps = std::make_unique<std::vector<storage::cpp2::VertexProp>>( - dstTagProps_.size()); std::transform(dstTagProps_.begin(), dstTagProps_.end(), - vertexProps->begin(), [](auto& tag) { + vertexProps.begin(), [](auto& tag) { storage::cpp2::VertexProp vp; vp.tag = tag.first; vp.props = std::move(tag.second); @@ -525,55 +613,109 @@ GetNeighbors::EdgeProps GoValidator::buildEdgeProps() { GetNeighbors::EdgeProps edgeProps; if (!edgeProps_.empty()) { if (direction_ == storage::cpp2::EdgeDirection::IN_EDGE) { - edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(edgeProps_.size()); - std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin(), - [] (auto& edge) { - storage::cpp2::EdgeProp ep; - ep.type = -edge.first; - ep.props = std::move(edge.second); - return ep; - }); + edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>( + edgeProps_.size()); + std::transform(edgeProps_.begin(), edgeProps_.end(), + edgeProps->begin(), [this](auto& edge) { + storage::cpp2::EdgeProp ep; + ep.type = -edge.first; + ep.props = std::move(edge.second); + if (!dstTagProps_.empty()) { + ep.props.emplace_back(kDst); + } + return ep; + }); } else if (direction_ == storage::cpp2::EdgeDirection::BOTH) { auto size = edgeProps_.size(); - edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(size * 2); - std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin(), - [] (auto& edge) { - storage::cpp2::EdgeProp ep; - ep.type = edge.first; - ep.props = std::move(edge.second); - return ep; - }); - std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin() + size, - [] (auto& edge) { - storage::cpp2::EdgeProp ep; - ep.type = -edge.first; - ep.props = std::move(edge.second); - return ep; - }); + edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>( + size * 2); + std::transform(edgeProps_.begin(), edgeProps_.end(), + edgeProps->begin(), [this](auto& edge) { + storage::cpp2::EdgeProp ep; + ep.type = edge.first; + ep.props = edge.second; + if (!dstTagProps_.empty()) { + ep.props.emplace_back(kDst); + } + return ep; + }); + std::transform(edgeProps_.begin(), edgeProps_.end(), + edgeProps->begin() + size, [this](auto& edge) { + storage::cpp2::EdgeProp ep; + ep.type = -edge.first; + ep.props = std::move(edge.second); + if (!dstTagProps_.empty()) { + ep.props.emplace_back(kDst); + } + return ep; + }); } else { - edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(edgeProps_.size()); - std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin(), - [] (auto& edge) { - storage::cpp2::EdgeProp ep; - ep.type = edge.first; - ep.props = std::move(edge.second); - return ep; - }); + edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>( + edgeProps_.size()); + std::transform(edgeProps_.begin(), edgeProps_.end(), + edgeProps->begin(), [this](auto& edge) { + storage::cpp2::EdgeProp ep; + ep.type = edge.first; + ep.props = std::move(edge.second); + if (!dstTagProps_.empty()) { + ep.props.emplace_back(kDst); + } + return ep; + }); } + } else if (!dstTagProps_.empty()) { + return buildEdgeDst(); } + return edgeProps; } -GetNeighbors::EdgeProps GoValidator::buildNStepLoopEdgeProps() { +GetNeighbors::EdgeProps GoValidator::buildEdgeDst() { GetNeighbors::EdgeProps edgeProps; - if (!edgeProps_.empty()) { - edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(edgeTypes_.size()); - std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), [] (auto& type) { - storage::cpp2::EdgeProp ep; - ep.type = type; - ep.props = {kDst}; - return ep; - }); + if (!edgeProps_.empty() || !dstTagProps_.empty()) { + if (direction_ == storage::cpp2::EdgeDirection::IN_EDGE) { + edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>( + edgeTypes_.size()); + std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), + [](auto& type) { + storage::cpp2::EdgeProp ep; + ep.type = -type; + VLOG(1) << "edge type: " << ep.type; + ep.props = {kDst}; + return ep; + }); + } else if (direction_ == storage::cpp2::EdgeDirection::BOTH) { + edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>( + edgeTypes_.size() * 2); + std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), + [](auto& type) { + storage::cpp2::EdgeProp ep; + ep.type = type; + VLOG(1) << "edge type: " << ep.type; + ep.props = {kDst}; + return ep; + }); + std::transform(edgeTypes_.begin(), edgeTypes_.end(), + edgeProps->begin() + edgeTypes_.size(), + [](auto& type) { + storage::cpp2::EdgeProp ep; + ep.type = -type; + VLOG(1) << "edge type: " << ep.type; + ep.props = {kDst}; + return ep; + }); + } else { + edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>( + edgeTypes_.size()); + std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), + [](auto& type) { + storage::cpp2::EdgeProp ep; + ep.type = type; + VLOG(1) << "edge type: " << ep.type; + ep.props = {kDst}; + return ep; + }); + } } return edgeProps; } @@ -781,7 +923,7 @@ Status GoValidator::buildColumns() { return Status::OK(); } - if (!srcTagProps_.empty() || !edgeProps_.empty()) { + if (!srcTagProps_.empty() || !edgeProps_.empty() || !dstTagProps_.empty()) { srcAndEdgePropCols_ = qctx_->plan()->saveObject(new YieldColumns()); } diff --git a/src/validator/GoValidator.h b/src/validator/GoValidator.h index 4361a53ce711451d8eb28d5dff4865cfdd569cc2..9006ad6b1c5ef2c7995a77c63f9386798f92602c 100644 --- a/src/validator/GoValidator.h +++ b/src/validator/GoValidator.h @@ -52,23 +52,27 @@ private: GetNeighbors::VertexProps buildSrcVertexProps(); - GetNeighbors::VertexProps buildDstVertexProps(); + std::vector<storage::cpp2::VertexProp> buildDstVertexProps(); GetNeighbors::EdgeProps buildEdgeProps(); - GetNeighbors::EdgeProps buildNStepLoopEdgeProps(); + GetNeighbors::EdgeProps buildEdgeDst(); Expression* buildNStepLoopCondition(int64_t steps) const; - Project* ifBuildLeftVarForTraceJoin(PlanNode* projectStartVid); + Project* buildLeftVarForTraceJoin(PlanNode* projectStartVid); Project* projectDstVidsFromGN(PlanNode* gn, const std::string& outputVar); - Project* ifTraceToStartVid(Project* projectLeftVarForJoin, - Project* projectDstFromGN); + Project* traceToStartVid(Project* projectLeftVarForJoin, + Project* projectDstFromGN); - PlanNode* ifBuildJoinPipeInput(PlanNode* gn, - PlanNode* projectFromJoin); + PlanNode* buildJoinPipeOrVariableInput(PlanNode* gn, + PlanNode* projectFromJoin); + + PlanNode* buildProjectSrcEdgePropsForGN(PlanNode* gn); + + PlanNode* buildJoinDstProps(PlanNode* projectSrcDstProps); enum FromType { kConstantExpr, @@ -89,6 +93,7 @@ private: std::vector<std::string> colNames_; YieldColumns* yields_{nullptr}; bool distinct_{false}; + std::string userDefinedVarName_; // Generated by validator if needed, and the lifecycle of raw pinters would // be managed by object pool @@ -98,9 +103,12 @@ private: std::unordered_map<std::string, YieldColumn*> propExprColMap_; Expression* newFilter_{nullptr}; YieldColumns* newYieldCols_{nullptr}; + // Used for n steps to trace the path std::string srcVidColName_; std::string dstVidColName_; std::string firstBeginningSrcVidColName_; + // Used for get dst props + std::string joinDstVidColName_; }; } // namespace graph } // namespace nebula diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp index fd41f24d1332858a1160cef96a5e8e06de9ad457..548a8966a743ef987bc11d8a2d6b53fd9aa656c6 100644 --- a/src/validator/test/QueryValidatorTest.cpp +++ b/src/validator/test/QueryValidatorTest.cpp @@ -122,6 +122,9 @@ TEST_F(QueryValidatorTest, GoNSteps) { }; EXPECT_TRUE(checkResult(query, expected)); } +} + +TEST_F(QueryValidatorTest, GoWithPipe) { { std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS " "id | GO 2 STEPS FROM $-.id OVER like"; @@ -288,6 +291,222 @@ TEST_F(QueryValidatorTest, GoNSteps) { }; EXPECT_TRUE(checkResult(query, expected)); } + { + std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS " + "id, $$.person.name as name | GO 1 STEPS FROM $-.id OVER like " + "YIELD $-.name, like.likeness + 1, $-.id, like._dst, " + "$$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS " + "id, $$.person.name as name | GO 1 STEPS FROM $-.id OVER like " + "YIELD DISTINCT $-.name, like.likeness + 1, $-.id, like._dst, " + "$$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kDedup, + PK::kProject, + PK::kDataJoin, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO FROM \"1\" OVER like " + "YIELD $^.person.name AS name, like._dst AS id " + "| GO FROM $-.id OVER like " + "YIELD $-.name, $^.person.name, $$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS " + "id, $$.person.name as name | GO 2 STEPS FROM $-.id OVER like " + "YIELD $-.name, like.likeness + 1, $-.id, like._dst, " + "$$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kDataJoin, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kLoop, + PK::kProject, + PK::kProject, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kProject, + PK::kDataJoin, + PK::kGetNeighbors, + PK::kProject, + PK::kStart, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS " + "id, $$.person.name as name | GO 2 STEPS FROM $-.id OVER like " + "YIELD DISTINCT $-.name, like.likeness + 1, $-.id, like._dst, " + "$$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kDedup, + PK::kProject, + PK::kDataJoin, + PK::kDataJoin, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kLoop, + PK::kProject, + PK::kProject, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kProject, + PK::kDataJoin, + PK::kGetNeighbors, + PK::kProject, + PK::kStart, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } +} + +TEST_F(QueryValidatorTest, GoWithVariable) { + { + std::string query = "$var = GO FROM \"1\" OVER like " + "YIELD $^.person.name AS name, like._dst AS id;" + "GO FROM $var.id OVER like " + "YIELD $var.name, $^.person.name, $$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } +} + +TEST_F(QueryValidatorTest, GoReversely) { + { + std::string query = "GO FROM \"1\" OVER like REVERSELY " + "YIELD $$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO 2 STEPS FROM \"1\" OVER like REVERSELY " + "YIELD $$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kLoop, + PK::kStart, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } +} + +TEST_F(QueryValidatorTest, GoBidirectly) { + { + std::string query = "GO FROM \"1\" OVER like BIDIRECT"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO FROM \"1\" OVER like BIDIRECT " + "YIELD $$.person.name"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } } TEST_F(QueryValidatorTest, GoOneStep) { @@ -340,8 +559,67 @@ TEST_F(QueryValidatorTest, GoOneStep) { { std::string query = "GO FROM \"1\" OVER like " "YIELD $$.person.name,$$.person.age"; - // TODO: implement get dst props and test plan. - EXPECT_FALSE(checkResult(query)); + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO FROM \"1\" OVER like " + "YIELD $^.person.name, like._dst, " + "$$.person.name, $$.person.age + 1"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO FROM \"1\" OVER like " + "WHERE like._dst == \"2\"" + "YIELD $^.person.name, like._dst, " + "$$.person.name, $$.person.age + 1"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kFilter, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "GO FROM \"1\" OVER like " + "WHERE like._dst == \"2\"" + "YIELD DISTINCT $^.person.name, like._dst, " + "$$.person.name, $$.person.age + 1"; + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kDedup, + PK::kProject, + PK::kFilter, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kProject, + PK::kGetNeighbors, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); } { std::string query = "GO FROM \"1\",\"2\",\"3\" OVER like"; diff --git a/src/validator/test/ValidatorTestBase.h b/src/validator/test/ValidatorTestBase.h index 7ca4723c2e2f6fd92c733d3a7b3c70ded66a5820..7245c2fe585bddb54a560bc4dfab52cadda63d2b 100644 --- a/src/validator/test/ValidatorTestBase.h +++ b/src/validator/test/ValidatorTestBase.h @@ -51,6 +51,7 @@ protected: ::testing::AssertionResult checkResult(const std::string& query, const std::vector<PlanNode::Kind>& expected = {}, const std::vector<std::string> &rootColumns = {}) { + VLOG(1) << "query: " << query; auto result = GQLParser().parse(query); if (!result.ok()) { return ::testing::AssertionFailure() << result.status();