Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable MULTIUPDATE and LOOKUP | UPDATE #5953

Merged
merged 9 commits into from
Oct 29, 2024
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/graph/executor/mutate/DeleteExecutor.cpp
Original file line number Diff line number Diff line change
@@ -147,7 +147,7 @@ folly::Future<Status> DeleteEdgesExecutor::deleteEdges() {
DCHECK(!inputVar.empty());
auto& inputResult = ectx_->getResult(inputVar);
auto iter = inputResult.iter();
edgeKeys.reserve(iter->size());
edgeKeys.reserve(iter->size()); // TODO(zhijie): the reserve size should be doubled
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
QueryExpressionContext ctx(ectx_);
for (; iter->valid(); iter->next()) {
storage::cpp2::EdgeKey edgeKey;
316 changes: 280 additions & 36 deletions src/graph/executor/mutate/UpdateExecutor.cpp
Original file line number Diff line number Diff line change
@@ -27,7 +27,8 @@ StatusOr<DataSet> UpdateBaseExecutor::handleResult(DataSet &&data) {
return Status::Error("Wrong return prop size");
}
DataSet result;
result.colNames = std::move(yieldNames_);
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
result.colNames = yieldNames_;
// result.colNames = std::move(yieldNames_);
for (auto &row : data.rows) {
std::vector<Value> columns;
for (auto i = 1u; i < row.values.size(); i++) {
@@ -38,6 +39,7 @@ StatusOr<DataSet> UpdateBaseExecutor::handleResult(DataSet &&data) {
return result;
}

/*
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
folly::Future<Status> UpdateVertexExecutor::execute() {
SCOPED_TIMER(&execTime_);
auto *uvNode = asNode<UpdateVertex>(node());
@@ -72,6 +74,7 @@ folly::Future<Status> UpdateVertexExecutor::execute() {
NG_RETURN_IF_ERROR(handleErrorCode(code.get_code(), code.get_part_id()));
}
if (value.props_ref().has_value()) {
LOG(INFO) << "props exist";
auto status = handleResult(std::move(*value.props_ref()));
if (!status.ok()) {
return status.status();
@@ -84,57 +87,298 @@ folly::Future<Status> UpdateVertexExecutor::execute() {
return Status::OK();
});
}
*/

Status UpdateBaseExecutor::handleMultiResult(DataSet &result, DataSet &&data) {
if (data.colNames.size() <= 1) {
if (yieldNames_.empty()) {
return Status::OK();
}
return Status::Error("Empty return props");
}

if (yieldNames_.size() != data.colNames.size() - 1) {
LOG(WARNING) << "Expect colName size is " << yieldNames_.size() << ", return colName size is "
<< data.colNames.size() - 1;
return Status::Error("Wrong return prop size");
}
result.colNames = yieldNames_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

colNames的赋值操作只需在外部进行一次即可。

// result.colNames = std::move(yieldNames_);
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
for (auto &row : data.rows) {
std::vector<Value> columns;
for (auto i = 1u; i < row.values.size(); i++) {
columns.emplace_back(std::move(row.values[i]));
}
result.rows.emplace_back(std::move(columns));
}
return Status::OK();
}

folly::Future<Status> UpdateVertexExecutor::execute() {
SCOPED_TIMER(&execTime_);
auto *urvNode = asNode<UpdateVertex>(node());

auto vidRef = urvNode->getVidRef();
LOG(INFO) << "Update vertex id: " << vidRef->toString();
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
std::vector<Value> vertices;
const auto& spaceInfo = qctx()->rctx()->session()->space();

if (vidRef != nullptr) {
auto inputVar = urvNode->inputVar();
if (inputVar.empty()) {
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
DCHECK(urvNode->dep() != nullptr);
auto* gn = static_cast<const SingleInputNode*>(urvNode->dep())->dep();
DCHECK(gn != nullptr);
inputVar = static_cast<const SingleInputNode*>(gn)->inputVar();
}
DCHECK(!inputVar.empty());
auto& inputResult = ectx_->getResult(inputVar);
auto iter = inputResult.iter();
vertices.reserve(iter->size());
QueryExpressionContext ctx(ectx_);
for (; iter->valid(); iter->next()) {
auto val = Expression::eval(vidRef, ctx(iter.get()));
if (val.isNull() || val.empty()) {
continue;
}
if (!SchemaUtil::isValidVid(val, *spaceInfo.spaceDesc.vid_type_ref())) {
std::stringstream ss;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以使用fmt::format

ss << "Wrong vid type `" << val.type() << "', value `" << val.toString() << "'";
return Status::Error(ss.str());
}
vertices.emplace_back(std::move(val));
}
}

if (vertices.empty()) {
LOG(INFO) << "No vertices to update";
return Status::OK();
}

std::vector<folly::Future<StatusOr<storage::cpp2::UpdateResponse>>> futures;
futures.reserve(vertices.size());

yieldNames_ = urvNode->getYieldNames();
time::Duration updateVertTime;
auto plan = qctx()->plan();
auto sess = qctx()->rctx()->session();
StorageClient::CommonRequestParam param(
urvNode->getSpaceId(), sess->id(), plan->id(), plan->isProfileEnabled());

for (auto &vId : vertices) {
LOG(INFO) << "Update vertex id: " << vId.toString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删除该日志

futures.emplace_back(
qctx()
->getStorageClient()
->updateVertex(param,
vId,
urvNode->getTagId(),
urvNode->getUpdatedProps(),
urvNode->getInsertable(),
urvNode->getReturnProps(),
urvNode->getCondition())
.via(runner()));
}

return folly::collectAll(futures)
.via(runner())
.ensure([updateVertTime]() {
VLOG(1) << "updateVertTime: " << updateVertTime.elapsedInUSec() << "us";
})
.thenValue([this](
std::vector<folly::Try<StatusOr<storage::cpp2::UpdateResponse>>> results) {
memory::MemoryCheckGuard guard;
SCOPED_TIMER(&execTime_);
DataSet finalResult;
for (auto& result : results) {
if (result.hasException()) {
LOG(WARNING) << "Update vertex request threw an exception.";
return Status::Error("Exception occurred during update.");
}

if (!result.value().ok()) {
LOG(WARNING) << "Update vertex failed: " << result.value().status();
return result.value().status(); // if fail, return the error
}

auto value = std::move(result.value()).value();
for (auto& code : value.get_result().get_failed_parts()) {
NG_RETURN_IF_ERROR(handleErrorCode(code.get_code(), code.get_part_id()));
}

if (value.props_ref().has_value()) {
auto status = handleMultiResult(finalResult, std::move(*value.props_ref()));
if (!status.ok()) {
return status;
}
}
}

// print the final result
if (finalResult.colNames.empty()) {
return Status::OK();
} else {
return finish(ResultBuilder()
.value(std::move(finalResult))
.iter(Iterator::Kind::kDefault)
.build());
}
});
}


folly::Future<Status> UpdateEdgeExecutor::execute() {
SCOPED_TIMER(&execTime_);
auto *ueNode = asNode<UpdateEdge>(node());
storage::cpp2::EdgeKey edgeKey;
edgeKey.src_ref() = ueNode->getSrcId();
edgeKey.ranking_ref() = ueNode->getRank();
edgeKey.edge_type_ref() = ueNode->getEdgeType();
edgeKey.dst_ref() = ueNode->getDstId();
yieldNames_ = ueNode->getYieldNames();

auto *ureNode = asNode<UpdateEdge>(node());
auto *edgeKeyRef = DCHECK_NOTNULL(ureNode->getEdgeKeyRef());
auto edgeType = ureNode->getEdgeType();
time::Duration updateEdgeTime;

yieldNames_ = ureNode->getYieldNames();

const auto& spaceInfo = qctx()->rctx()->session()->space();
auto inputVar = ureNode->inputVar();
DCHECK(!inputVar.empty());
auto& inputResult = ectx_->getResult(inputVar);
auto iter = inputResult.iter();

std::vector<storage::cpp2::EdgeKey> edgeKeys;
std::vector<storage::cpp2::EdgeKey> reverse_edgeKeys;
edgeKeys.reserve(iter->size());
reverse_edgeKeys.reserve(iter->size());

QueryExpressionContext ctx(ectx_);
for (; iter->valid(); iter->next()) {
auto srcId = Expression::eval(edgeKeyRef->srcid(), ctx(iter.get()));
if (srcId.isNull() || srcId.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

似乎是一个无效的检查,我们在isValidVid中会检查其类型。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

两者似乎并不一样: isValidVid 中的检查,是检查 value.isStr() || value.isInt(),且检查失败后返回 ERROR;而 srcId.isNull() || srcId.empty() 只是跳过当前的 srcId。
这部分我是参照的 delete 的实现。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

两者似乎并不一样: isValidVid 中的检查,是检查 value.isStr() || value.isInt(),且检查失败后返回 ERROR;而 srcId.isNull() || srcId.empty() 只是跳过当前的 srcId。 这部分我是参照的 delete 的实现。

isValidVid还会检查该类型和schema中的vid type类型相同,在value.type() == propTypeToValueType(type);应该可以检查null或empty?

bool SchemaUtil::isValidVid(const Value &value, nebula::cpp2::PropertyType type) {
  return isValidVid(value) && value.type() == propTypeToValueType(type);
}

continue;
}
if (!SchemaUtil::isValidVid(srcId, *spaceInfo.spaceDesc.vid_type_ref())) {
std::stringstream ss;
ss << "Wrong srcId type `" << srcId.type() << "`, value `" << srcId.toString() << "'";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

均可使用fmt::format

return Status::Error(ss.str());
}
auto dstId = Expression::eval(edgeKeyRef->dstid(), ctx(iter.get()));
if (!SchemaUtil::isValidVid(dstId, *spaceInfo.spaceDesc.vid_type_ref())) {
std::stringstream ss;
ss << "Wrong dstId type `" << dstId.type() << "', value `" << dstId.toString() << "'";
return Status::Error(ss.str());
}
auto rank = Expression::eval(edgeKeyRef->rank(), ctx(iter.get()));
if (!rank.isInt()) {
std::stringstream ss;
ss << "Wrong rank type `" << rank.type() << "', value `" << rank.toString() << "'";
return Status::Error(ss.str());
}
DCHECK(edgeKeyRef->type());
auto type = Expression::eval(edgeKeyRef->type(), ctx(iter.get()));
if (!type.isInt()) {
std::stringstream ss;
ss << "Wrong edge type `" << type.type() << "', value `" << type.toString() << "'";
return Status::Error(ss.str());
}
storage::cpp2::EdgeKey edgeKey;
edgeKey.src_ref() = srcId;
edgeKey.dst_ref() = dstId;
edgeKey.ranking_ref() = rank.getInt();
edgeKey.edge_type_ref() = edgeType;

storage::cpp2::EdgeKey reverse_edgeKey;
reverse_edgeKey.src_ref() = std::move(dstId);
reverse_edgeKey.dst_ref() = std::move(srcId);
reverse_edgeKey.ranking_ref() = rank.getInt();
reverse_edgeKey.edge_type_ref() = -edgeType;

edgeKeys.emplace_back(std::move(edgeKey));
reverse_edgeKeys.emplace_back(std::move(reverse_edgeKey));
}

if (edgeKeys.empty()) {
return Status::OK();
}

auto plan = qctx()->plan();
StorageClient::CommonRequestParam param(
ueNode->getSpaceId(), qctx()->rctx()->session()->id(), plan->id(), plan->isProfileEnabled());
ureNode->getSpaceId(), qctx()->rctx()->session()->id(), plan->id(), plan->isProfileEnabled());
param.useExperimentalFeature = false;
return qctx()
->getStorageClient()
->updateEdge(param,
edgeKey,
ueNode->getUpdatedProps(),
ueNode->getInsertable(),
ueNode->getReturnProps(),
ueNode->getCondition())


std::vector<folly::Future<StatusOr<storage::cpp2::UpdateResponse>>> futures;
futures.reserve(edgeKeys.size() + reverse_edgeKeys.size());

for (auto &edgeKey : edgeKeys) {
futures.emplace_back(
qctx()
->getStorageClient()
->updateEdge(param,
edgeKey,
ureNode->getUpdatedProps(),
ureNode->getInsertable(),
{},
ureNode->getCondition())
.via(runner()));
}

for (auto &edgeKey : reverse_edgeKeys) {
futures.emplace_back(
qctx()
->getStorageClient()
->updateEdge(param,
edgeKey,
ureNode->getUpdatedProps(),
ureNode->getInsertable(),
ureNode->getReturnProps(),
ureNode->getCondition())
.via(runner()));
}

return folly::collectAll(futures)
.via(runner())
.ensure([updateEdgeTime]() {
VLOG(1) << "Update edge time: " << updateEdgeTime.elapsedInUSec() << "us";
VLOG(1) << "updateEdgeTime: " << updateEdgeTime.elapsedInUSec() << "us";
})
.thenValue([this](StatusOr<storage::cpp2::UpdateResponse> resp) {
.thenValue([this](
std::vector<folly::Try<StatusOr<storage::cpp2::UpdateResponse>>> results) {
memory::MemoryCheckGuard guard;
SCOPED_TIMER(&execTime_);
if (!resp.ok()) {
LOG(WARNING) << "Update edge failed: " << resp.status();
return resp.status();
}
auto value = std::move(resp).value();
for (auto &code : value.get_result().get_failed_parts()) {
NG_RETURN_IF_ERROR(handleErrorCode(code.get_code(), code.get_part_id()));
}
if (value.props_ref().has_value()) {
auto status = handleResult(std::move(*value.props_ref()));
if (!status.ok()) {
return status.status();
DataSet finalResult;
for (auto& result : results) {
// LOG(INFO) << "Update edge result";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删除无效的注释

if (result.hasException()) {
LOG(WARNING) << "Update edge request threw an exception.";
return Status::Error("Exception occurred during update.");
}

if (!result.value().ok()) {
LOG(WARNING) << "Update edge failed: " << result.value().status();
return result.value().status(); // if fail, return the error
}

auto value = std::move(result.value()).value();
for (auto& code : value.get_result().get_failed_parts()) {
NG_RETURN_IF_ERROR(handleErrorCode(code.get_code(), code.get_part_id()));
}

// skip the edge result
if (value.props_ref().has_value() && value.props_ref()->colNames.size() > 1) {
auto status = handleMultiResult(finalResult, std::move(*value.props_ref()));
if (!status.ok()) {
return status;
}
}
}

// print the final result
if (finalResult.colNames.empty()) {
return Status::OK();
} else {
return finish(ResultBuilder()
.value(std::move(status).value())
.iter(Iterator::Kind::kDefault)
.build());
.value(std::move(finalResult))
.iter(Iterator::Kind::kDefault)
.build());
}
return Status::OK();
});
}

} // namespace graph
} // namespace nebula
4 changes: 4 additions & 0 deletions src/graph/executor/mutate/UpdateExecutor.h
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ class UpdateBaseExecutor : public StorageAccessExecutor {
protected:
StatusOr<DataSet> handleResult(DataSet &&data);

Status handleMultiResult(DataSet &result, DataSet &&data);

protected:
std::vector<std::string> yieldNames_;
};
@@ -32,6 +34,7 @@ class UpdateVertexExecutor final : public UpdateBaseExecutor {
folly::Future<Status> execute() override;
};


class UpdateEdgeExecutor final : public UpdateBaseExecutor {
public:
UpdateEdgeExecutor(const PlanNode *node, QueryContext *qctx)
@@ -40,6 +43,7 @@ class UpdateEdgeExecutor final : public UpdateBaseExecutor {
folly::Future<Status> execute() override;
};


} // namespace graph
} // namespace nebula

5 changes: 2 additions & 3 deletions src/graph/planner/plan/Mutate.cpp
Original file line number Diff line number Diff line change
@@ -49,6 +49,7 @@ std::unique_ptr<PlanNodeDescription> Update::explain() const {
return desc;
}


std::unique_ptr<PlanNodeDescription> UpdateVertex::explain() const {
auto desc = Update::explain();
addDescription("vid", vId_.toString(), desc.get());
@@ -58,9 +59,7 @@ std::unique_ptr<PlanNodeDescription> UpdateVertex::explain() const {

std::unique_ptr<PlanNodeDescription> UpdateEdge::explain() const {
auto desc = Update::explain();
addDescription("srcId", srcId_.toString(), desc.get());
addDescription("dstId", dstId_.toString(), desc.get());
addDescription("rank", folly::to<std::string>(rank_), desc.get());
addDescription("edgeKeyRef", edgeKeyRef_ ? edgeKeyRef_->toString() : "", desc.get());
addDescription("edgeType", folly::to<std::string>(edgeType_), desc.get());
return desc;
}
91 changes: 64 additions & 27 deletions src/graph/planner/plan/Mutate.h
Original file line number Diff line number Diff line change
@@ -152,7 +152,7 @@ class InsertEdges final : public SingleDependencyNode {
bool useChainInsert_{false};
};

class Update : public SingleDependencyNode {
class Update : public SingleInputNode {
public:
bool getInsertable() const {
return insertable_;
@@ -196,7 +196,7 @@ class Update : public SingleDependencyNode {
std::vector<std::string> returnProps,
std::string condition,
std::vector<std::string> yieldNames)
: SingleDependencyNode(qctx, kind, input),
: SingleInputNode(qctx, kind, input),
spaceId_(spaceId),
schemaName_(std::move(name)),
insertable_(insertable),
@@ -241,12 +241,41 @@ class UpdateVertex final : public Update {
std::move(yieldNames));
}

static UpdateVertex* make(QueryContext* qctx,
PlanNode* input,
GraphSpaceID spaceId,
std::string name,
Expression* vidRef,
TagID tagId,
bool insertable,
std::vector<storage::cpp2::UpdatedProp> updatedProps,
std::vector<std::string> returnProps,
std::string condition,
std::vector<std::string> yieldNames) {
return qctx->objPool()->makeAndAdd<UpdateVertex>(qctx,
input,
spaceId,
std::move(name),
vidRef,
tagId,
insertable,
std::move(updatedProps),
std::move(returnProps),
std::move(condition),
std::move(yieldNames));
}

std::unique_ptr<PlanNodeDescription> explain() const override;

// no used any more
const Value& getVId() const {
return vId_;
}

Expression* getVidRef() const {
return vidRef_;
}

TagID getTagId() const {
return tagId_;
}
@@ -277,8 +306,34 @@ class UpdateVertex final : public Update {
vId_(std::move(vId)),
tagId_(tagId) {}

UpdateVertex(QueryContext* qctx,
PlanNode* input,
GraphSpaceID spaceId,
std::string name,
Expression* vidRef,
TagID tagId,
bool insertable,
std::vector<storage::cpp2::UpdatedProp> updatedProps,
std::vector<std::string> returnProps,
std::string condition,
std::vector<std::string> yieldNames)
: Update(qctx,
Kind::kUpdateVertex,
input,
spaceId,
std::move(name),
insertable,
std::move(updatedProps),
std::move(returnProps),
std::move(condition),
std::move(yieldNames)),
vidRef_(vidRef),
tagId_(tagId) {}


private:
Value vId_;
Expression* vidRef_{nullptr};
TagID tagId_{-1};
};

@@ -288,10 +343,8 @@ class UpdateEdge final : public Update {
PlanNode* input,
GraphSpaceID spaceId,
std::string name,
Value srcId,
Value dstId,
EdgeKeyRef* edgeKeyRef,
EdgeType edgeType,
int64_t rank,
bool insertable,
std::vector<storage::cpp2::UpdatedProp> updatedProps,
std::vector<std::string> returnProps,
@@ -301,10 +354,8 @@ class UpdateEdge final : public Update {
input,
spaceId,
std::move(name),
std::move(srcId),
std::move(dstId),
edgeKeyRef,
edgeType,
rank,
insertable,
std::move(updatedProps),
std::move(returnProps),
@@ -314,16 +365,8 @@ class UpdateEdge final : public Update {

std::unique_ptr<PlanNodeDescription> explain() const override;

const Value& getSrcId() const {
return srcId_;
}

const Value& getDstId() const {
return dstId_;
}

int64_t getRank() const {
return rank_;
EdgeKeyRef* getEdgeKeyRef() const {
return edgeKeyRef_;
}

int64_t getEdgeType() const {
@@ -340,10 +383,8 @@ class UpdateEdge final : public Update {
PlanNode* input,
GraphSpaceID spaceId,
std::string name,
Value srcId,
Value dstId,
EdgeKeyRef* edgeKeyRef,
EdgeType edgeType,
int64_t rank,
bool insertable,
std::vector<storage::cpp2::UpdatedProp> updatedProps,
std::vector<std::string> returnProps,
@@ -359,15 +400,11 @@ class UpdateEdge final : public Update {
std::move(returnProps),
std::move(condition),
std::move(yieldNames)),
srcId_(std::move(srcId)),
dstId_(std::move(dstId)),
rank_(rank),
edgeKeyRef_(edgeKeyRef),
edgeType_(edgeType) {}

private:
Value srcId_;
Value dstId_;
int64_t rank_{0};
EdgeKeyRef* edgeKeyRef_{nullptr};
EdgeType edgeType_{-1};
};

3 changes: 2 additions & 1 deletion src/graph/service/PermissionCheck.cpp
Original file line number Diff line number Diff line change
@@ -23,7 +23,8 @@ namespace graph {
* kYield, kOrderBy, kFetchVertices, kFind
* kFetchEdges, kFindPath, kLimit, KGroupBy, kReturn
* Write data: kBuildTagIndex, kBuildEdgeIndex,
* kInsertVertex, kUpdateVertex, kInsertEdge,
* kInsertVertex, kUpdateVertex,
* kInsertEdge,
* kUpdateEdge, kDeleteVertex, kDeleteEdges, kAdminJob(other)
* Special operation : kShow, kChangePassword
*/
3 changes: 2 additions & 1 deletion src/graph/service/QueryInstance.cpp
Original file line number Diff line number Diff line change
@@ -125,11 +125,12 @@ bool QueryInstance::explainOrContinue() {

void QueryInstance::onFinish() {
auto rctx = qctx()->rctx();
VLOG(1) << "Finish query: " << rctx->query();
LOG(INFO) << "Finish query: " << rctx->query();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删除改LOG,或修改为VLOG

auto &spaceName = rctx->session()->space().name;
rctx->resp().spaceName = std::make_unique<std::string>(spaceName);

fillRespData(&rctx->resp());
LOG(INFO) << "fillRespData";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上


auto latency = rctx->duration().elapsedInUSec();
rctx->resp().latencyInUs = latency;
237 changes: 191 additions & 46 deletions src/graph/validator/MutateValidator.cpp
Original file line number Diff line number Diff line change
@@ -836,12 +836,40 @@ Expression *UpdateValidator::rewriteSymExpr(Expression *expr,

Status UpdateVertexValidator::validateImpl() {
auto sentence = static_cast<UpdateVertexSentence *>(sentence_);
auto idRet = SchemaUtil::toVertexID(sentence->getVid(), vidType_);
if (!idRet.ok()) {
LOG(ERROR) << idRet.status();
return std::move(idRet).status();
// vid != nullptr
if (sentence->getVid() != nullptr) {
auto idRet = SchemaUtil::toVertexID(sentence->getVid(), vidType_);
if (!idRet.ok()) {
LOG(ERROR) << idRet.status();
return std::move(idRet).status();
}
vId_ = std::move(idRet).value();
vertices_.emplace_back(vId_);
} else if (sentence->vertices()->isRef()) {
vidRef_ = sentence->vertices()->ref();
auto type = deduceExprType(vidRef_);
NG_RETURN_IF_ERROR(type);
if (type.value() != vidType_) {
std::stringstream ss;
ss << "The vid `" << vidRef_->toString() << "' should be type of `" << vidType_
<< "', but was`" << type.value() << "'";
return Status::SemanticError(ss.str());
}
} else {
// get vertices
for (auto &vid : sentence->vertices()->vidList()) {
auto idRet = SchemaUtil::toVertexID(vid, vidType_);
if (!idRet.ok()) {
LOG(ERROR) << idRet.status();
return std::move(idRet).status();
}
vertices_.emplace_back(std::move(idRet).value());
}
// unique vidList
std::sort(vertices_.begin(), vertices_.end());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

前置dedup的算子时,这里的去重似乎是多余的

auto last = std::unique(vertices_.begin(), vertices_.end());
vertices_.erase(last, vertices_.end());
}
vId_ = std::move(idRet).value();
NG_RETURN_IF_ERROR(initProps());
auto ret = qctx_->schemaMng()->toTagID(spaceId_, name_);
if (!ret.ok()) {
@@ -852,77 +880,194 @@ Status UpdateVertexValidator::validateImpl() {
return Status::OK();
}

// same as DeleteVerticesValidator
std::string UpdateVertexValidator::buildVIds() {
auto input = vctx_->anonVarGen()->getVar();
DataSet ds;
ds.colNames.emplace_back(kVid);
for (auto &vid : vertices_) {
Row row;
row.values.emplace_back(vid);
ds.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult(input, ResultBuilder().value(Value(std::move(ds))).build());
auto *pool = qctx_->objPool();
auto *vIds = VariablePropertyExpression::make(pool, input, kVid);
vidRef_ = vIds;
return input;
}


Status UpdateVertexValidator::toPlan() {
std::string vidVar;
if (!vertices_.empty() && vidRef_ == nullptr) {
vidVar = buildVIds();
} else if (vidRef_ != nullptr && vidRef_->kind() == Expression::Kind::kVarProperty) {
vidVar = static_cast<PropertyExpression *>(vidRef_)->sym();
} else if (vidRef_ != nullptr && vidRef_->kind() == Expression::Kind::kInputProperty) {
vidVar = inputVarName_;
}

auto *dedupVid = Dedup::make(qctx_, nullptr);
dedupVid->setInputVar(vidVar);

auto *update = UpdateVertex::make(qctx_,
nullptr,
dedupVid,
spaceId_,
std::move(name_),
vId_,
vidRef_,
tagId_,
insertable_,
std::move(updatedProps_),
std::move(returnProps_),
std::move(condition_),
std::move(yieldColNames_));
root_ = update;
tail_ = root_;
tail_ = dedupVid;
return Status::OK();
}


Status UpdateEdgeValidator::validateImpl() {
auto sentence = static_cast<UpdateEdgeSentence *>(sentence_);
auto srcIdRet = SchemaUtil::toVertexID(sentence->getSrcId(), vidType_);
if (!srcIdRet.ok()) {
LOG(ERROR) << srcIdRet.status();
return srcIdRet.status();
}
srcId_ = std::move(srcIdRet).value();
auto dstIdRet = SchemaUtil::toVertexID(sentence->getDstId(), vidType_);
if (!dstIdRet.ok()) {
LOG(ERROR) << dstIdRet.status();
return dstIdRet.status();
}
dstId_ = std::move(dstIdRet).value();
rank_ = sentence->getRank();
spaceId_ = vctx_->whichSpace().id;

NG_RETURN_IF_ERROR(initProps());
auto ret = qctx_->schemaMng()->toEdgeType(spaceId_, name_);
if (!ret.ok()) {
LOG(ERROR) << "No schema found for " << name_ << " : " << ret.status();
return Status::SemanticError("No schema found for `%s'", name_.c_str());
}
edgeType_ = ret.value();

if (sentence->getSrcId() != nullptr && sentence->getDstId() != nullptr) {
auto srcIdRet = SchemaUtil::toVertexID(sentence->getSrcId(), vidType_);
if (!srcIdRet.ok()) {
LOG(ERROR) << srcIdRet.status();
return srcIdRet.status();
}
srcId_ = std::move(srcIdRet).value();
auto dstIdRet = SchemaUtil::toVertexID(sentence->getDstId(), vidType_);
if (!dstIdRet.ok()) {
LOG(ERROR) << dstIdRet.status();
return dstIdRet.status();
}
dstId_ = std::move(dstIdRet).value();
rank_ = sentence->getRank();
EdgeId edgeId = EdgeId(srcId_, dstId_, rank_);
edgeIds_.emplace_back(std::move(edgeId));
} else if (sentence->isRef()) {
auto *pool = qctx_->objPool();
edgeKeyRefs_.emplace_back(sentence->edgeKeyRef());
(*edgeKeyRefs_.begin())->setType(ConstantExpression::make(pool, edgeType_));
NG_RETURN_IF_ERROR(checkInput());
} else {
for (auto &edgekey : sentence->getEdgeKeys()->keys()) {
auto srcIdRet = SchemaUtil::toVertexID(edgekey->srcid(), vidType_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

和第一个分支重复的逻辑,可以考虑封装为函数

if (!srcIdRet.ok()) {
LOG(ERROR) << srcIdRet.status();
return srcIdRet.status();
}
auto dstIdRet = SchemaUtil::toVertexID(edgekey->dstid(), vidType_);
if (!dstIdRet.ok()) {
LOG(ERROR) << dstIdRet.status();
return dstIdRet.status();
}
auto rank = edgekey->rank();
EdgeId edgeId = EdgeId(std::move(srcIdRet).value(), std::move(dstIdRet).value(), rank);

edgeIds_.emplace_back(std::move(edgeId));
}
}

if (!sentence->isRef()) {
assert(!edgeIds_.empty());
buildEdgeKeyRef();
}

return Status::OK();
}


Status UpdateEdgeValidator::buildEdgeKeyRef() {
edgeKeyVar_ = vctx_->anonVarGen()->getVar();
DataSet ds({kSrc, kType, kRank, kDst});
for (auto &edgeId : edgeIds_) {
Row row;
row.emplace_back(edgeId.srcid());
row.emplace_back(edgeType_);
row.emplace_back(edgeId.rank());
row.emplace_back(edgeId.dstid());
ds.emplace_back(std::move(row));
}

qctx_->ectx()->setResult(edgeKeyVar_, ResultBuilder().value(Value(std::move(ds))).build());
auto *pool = qctx_->objPool();
auto *srcIdExpr = InputPropertyExpression::make(pool, kSrc);
auto *typeExpr = InputPropertyExpression::make(pool, kType);
auto *rankExpr = InputPropertyExpression::make(pool, kRank);
auto *dstIdExpr = InputPropertyExpression::make(pool, kDst);
auto *edgeKeyRef = qctx_->objPool()->makeAndAdd<EdgeKeyRef>(srcIdExpr, dstIdExpr, rankExpr);
edgeKeyRef->setType(typeExpr);

edgeKeyRefs_.emplace_back(edgeKeyRef);
return Status::OK();
}

Status UpdateEdgeValidator::toPlan() {
auto *outNode = UpdateEdge::make(qctx_,
nullptr,
spaceId_,
name_,
srcId_,
dstId_,
edgeType_,
rank_,
insertable_,
updatedProps_,
{},
condition_,
{});
auto *inNode = UpdateEdge::make(qctx_,
outNode,
auto *dedup = Dedup::make(qctx_, nullptr);
dedup->setInputVar(edgeKeyVar_);

auto *ureNode = UpdateEdge::make(qctx_,
dedup,
spaceId_,
std::move(name_),
std::move(dstId_),
std::move(srcId_),
-edgeType_,
rank_,
edgeKeyRefs_.front(),
edgeType_,
insertable_,
std::move(updatedProps_),
std::move(returnProps_),
std::move(condition_),
std::move(yieldColNames_));
root_ = inNode;
tail_ = outNode;
updatedProps_,
returnProps_,
condition_,
yieldColNames_);
root_ = ureNode;
tail_ = dedup;
return Status::OK();
}

Status UpdateEdgeValidator::checkInput() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

似乎等同于DeleteEdgeValidator的逻辑?考虑合并

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,直接使用了 DeleteEdgeValidator的逻辑。您觉得合并的话,作为哪个基类的函数会比较合适一些?作为 Validator 类的函数好像层级又有些过高了。或者定义在类外定义,作为友类函数?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,直接使用了 DeleteEdgeValidator的逻辑。您觉得合并的话,作为哪个基类的函数会比较合适一些?作为 Validator 类的函数好像层级又有些过高了。或者定义在类外定义,作为友类函数?

或许可以考虑定义一个MutateUtils类,作为静态成员函数存在。

CHECK(!edgeKeyRefs_.empty());
auto &edgeKeyRef = *edgeKeyRefs_.begin();
NG_LOG_AND_RETURN_IF_ERROR(deduceProps(edgeKeyRef->srcid(), exprProps_));
NG_LOG_AND_RETURN_IF_ERROR(deduceProps(edgeKeyRef->dstid(), exprProps_));
NG_LOG_AND_RETURN_IF_ERROR(deduceProps(edgeKeyRef->rank(), exprProps_));

if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty() ||
!exprProps_.edgeProps().empty()) {
return Status::SyntaxError("Only support input and variable.");
}

if (!exprProps_.inputProps().empty() && !exprProps_.varProps().empty()) {
return Status::SemanticError("Not support both input and variable.");
}

if (!exprProps_.varProps().empty() && exprProps_.varProps().size() > 1) {
return Status::SemanticError("Only one variable allowed to use.");
}

auto status = deduceExprType(edgeKeyRef->srcid());
NG_RETURN_IF_ERROR(status);

status = deduceExprType(edgeKeyRef->dstid());
NG_RETURN_IF_ERROR(status);

status = deduceExprType(edgeKeyRef->rank());
NG_RETURN_IF_ERROR(status);

if (edgeKeyRef->srcid()->kind() == Expression::Kind::kVarProperty) {
edgeKeyVar_ = static_cast<PropertyExpression *>(edgeKeyRef->srcid())->sym();
} else if (edgeKeyRef->srcid()->kind() == Expression::Kind::kInputProperty) {
edgeKeyVar_ = inputVarName_;
}
return Status::OK();
}

16 changes: 16 additions & 0 deletions src/graph/validator/MutateValidator.h
Original file line number Diff line number Diff line change
@@ -178,10 +178,14 @@ class UpdateVertexValidator final : public UpdateValidator {
private:
Status validateImpl() override;

std::string buildVIds();

Status toPlan() override;

private:
Value vId_;
std::vector<Value> vertices_;
Expression* vidRef_{nullptr};
TagID tagId_{-1};
};

@@ -195,12 +199,24 @@ class UpdateEdgeValidator final : public UpdateValidator {

Status toPlan() override;

Status checkInput();

Status buildEdgeKeyRef();

private:
Value srcId_;
Value dstId_;
EdgeRanking rank_{0};
EdgeType edgeType_{-1};

std::vector<EdgeId> edgeIds_;
std::vector<EdgeKeyRef*> edgeKeyRefs_;
std::string edgeKeyVar_;
ExpressionProps exprProps_;
};



} // namespace graph
} // namespace nebula
#endif // GRAPH_VALIDATOR_MUTATEVALIDATOR_H_
31 changes: 31 additions & 0 deletions src/parser/EdgeKey.h
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
#include <vector>

#include "common/thrift/ThriftTypes.h"
#include "common/datatypes/Value.h"

namespace nebula {

@@ -47,6 +48,36 @@ class EdgeKey final {
EdgeRanking rank_;
};

class EdgeId final {
public:
EdgeId(Value srcid, Value dstid, EdgeRanking rank) {
srcid_ = std::move(srcid);
dstid_ = std::move(dstid);
rank_ = rank;
}

const Value &srcid() const {
return srcid_;
}

const Value &dstid() const {
return dstid_;
}

EdgeRanking rank() const {
return rank_;
}

std::string toString() const {
return srcid_.toString() + "->" + dstid_.toString() + "@" + std::to_string(rank_);
}

private:
Value srcid_;
Value dstid_;
EdgeRanking rank_;
};

class EdgeKeys final {
public:
EdgeKeys() = default;
23 changes: 19 additions & 4 deletions src/parser/MutateSentences.cpp
Original file line number Diff line number Diff line change
@@ -204,6 +204,11 @@ std::string UpdateVertexSentence::toString() const {
if (name_ != nullptr) {
buf += "ON " + *name_ + " ";
}
if (vid_ != nullptr) {
buf += vid_->toString();
} else {
buf += vertices_->toString();
}
buf += vid_->toString();
buf += " SET ";
buf += updateList_->toString();
@@ -219,6 +224,9 @@ std::string UpdateVertexSentence::toString() const {
return buf;
}




std::string UpdateEdgeSentence::toString() const {
std::string buf;
buf.reserve(256);
@@ -228,10 +236,16 @@ std::string UpdateEdgeSentence::toString() const {
buf += "UPDATE ";
}
buf += "EDGE ";
buf += srcId_->toString();
buf += "->";
buf += dstId_->toString();
buf += "@" + std::to_string(rank_);
if (srcId_ != nullptr) {
buf += srcId_->toString();
buf += "->";
buf += dstId_->toString();
buf += "@" + std::to_string(rank_);
} else if (isRef()) {
buf += edgeKeyRef_->toString();
} else {
buf += edgeKeys_->toString();
}
buf += " OF " + *name_;
buf += " SET ";
buf += updateList_->toString();
@@ -247,6 +261,7 @@ std::string UpdateEdgeSentence::toString() const {
return buf;
}


std::string DeleteVerticesSentence::toString() const {
std::string buf;
buf.reserve(256);
66 changes: 61 additions & 5 deletions src/parser/MutateSentences.h
Original file line number Diff line number Diff line change
@@ -417,25 +417,37 @@ class UpdateBaseSentence : public Sentence {

class UpdateVertexSentence final : public UpdateBaseSentence {
public:
// compatible with 1.0
UpdateVertexSentence(Expression *vid,
std::string *tagName,
UpdateList *updateList,
WhenClause *whenClause,
YieldClause *yieldClause,
bool isInsertable = false)
: UpdateBaseSentence(updateList, whenClause, yieldClause, tagName, isInsertable) {
: UpdateBaseSentence(updateList, whenClause, yieldClause, nullptr, isInsertable) {
kind_ = Kind::kUpdateVertex;
vid_ = vid;
}

UpdateVertexSentence(Expression *vid,
UpdateVertexSentence(VertexIDList *vidList,
std::string *tagName,
UpdateList *updateList,
WhenClause *whenClause,
YieldClause *yieldClause,
bool isInsertable = false)
: UpdateBaseSentence(updateList, whenClause, yieldClause, nullptr, isInsertable) {
: UpdateBaseSentence(updateList, whenClause, yieldClause, tagName, isInsertable),
vertices_(new VerticesClause(vidList)) {
kind_ = Kind::kUpdateVertex;
}

UpdateVertexSentence(Expression *vid_ref,
std::string *tagName,
UpdateList *updateList,
WhenClause *whenClause,
YieldClause *yieldClause,
bool isInsertable = false)
: UpdateBaseSentence(updateList, whenClause, yieldClause, tagName, isInsertable),
vertices_(new VerticesClause(vid_ref)) {
kind_ = Kind::kUpdateVertex;
vid_ = vid;
}

~UpdateVertexSentence() {}
@@ -448,6 +460,10 @@ class UpdateVertexSentence final : public UpdateBaseSentence {
return vid_;
}

const VerticesClause *vertices() const {
return vertices_.get();
}

const UpdateList *updateList() const {
return updateList_.get();
}
@@ -464,6 +480,7 @@ class UpdateVertexSentence final : public UpdateBaseSentence {

private:
Expression *vid_{nullptr};
std::unique_ptr<VerticesClause> vertices_;
};

class UpdateEdgeSentence final : public UpdateBaseSentence {
@@ -483,6 +500,37 @@ class UpdateEdgeSentence final : public UpdateBaseSentence {
rank_ = rank;
}

UpdateEdgeSentence(EdgeKeys *edge_keys,
std::string *edgeName,
UpdateList *updateList,
WhenClause *whenClause,
YieldClause *yieldClause,
bool isInsertable = false)
: UpdateBaseSentence(updateList, whenClause, yieldClause, edgeName, isInsertable) {
edgeKeys_.reset(edge_keys);
kind_ = Kind::kUpdateEdge;
}


UpdateEdgeSentence(EdgeKeyRef *ref,
std::string *edgeName,
UpdateList *updateList,
WhenClause *whenClause,
YieldClause *yieldClause,
bool isInsertable = false)
: UpdateBaseSentence(updateList, whenClause, yieldClause, edgeName, isInsertable) {
edgeKeyRef_.reset(ref);
kind_ = Kind::kUpdateEdge;
}

EdgeKeys *getEdgeKeys() const {
return edgeKeys_.get();
}

EdgeKeyRef *edgeKeyRef() const {
return edgeKeyRef_.get();
}

Expression *getSrcId() const {
return srcId_;
}
@@ -495,14 +543,22 @@ class UpdateEdgeSentence final : public UpdateBaseSentence {
return rank_;
}

bool isRef() const {
return edgeKeyRef_ != nullptr;
}

std::string toString() const override;

private:
Expression *srcId_{nullptr};
Expression *dstId_{nullptr};
int64_t rank_{0L};

std::unique_ptr<EdgeKeys> edgeKeys_;
std::unique_ptr<EdgeKeyRef> edgeKeyRef_;
};


class DeleteVerticesSentence final : public Sentence {
public:
DeleteVerticesSentence(VertexIDList *vidList, bool withEdge)
45 changes: 42 additions & 3 deletions src/parser/parser.yy
Original file line number Diff line number Diff line change
@@ -393,8 +393,10 @@ using namespace nebula;
%type <sentence> mutate_sentence
%type <sentence> insert_vertex_sentence insert_edge_sentence
%type <sentence> delete_vertex_sentence delete_edge_sentence delete_tag_sentence delete_vertex_with_edge_sentence
%type <sentence> update_vertex_sentence update_edge_sentence
%type <sentence> update_vertex_sentence
%type <sentence> update_edge_sentence
%type <sentence> download_sentence ingest_sentence
%type <sentence> lookup_pipe_update_sentence

%type <sentence> traverse_sentence unwind_sentence
%type <sentence> go_sentence match_sentence lookup_sentence find_path_sentence get_subgraph_sentence
@@ -2203,6 +2205,15 @@ lookup_sentence
}
;

lookup_pipe_update_sentence
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

似乎没有必要单独为 lookup|update加一个语法,仿照delete即可

: lookup_sentence PIPE update_vertex_sentence {
$$ = new PipedSentence($1, $3);
}
| lookup_sentence PIPE update_edge_sentence {
$$ = new PipedSentence($1, $3);
}
;

order_factor
: expression {
$$ = new OrderFactor($1, OrderFactor::ASCEND);
@@ -3209,6 +3220,7 @@ update_item

update_vertex_sentence
// ======== Begin: Compatible with 1.0 =========
// keep the syntex with 1.0
: KW_UPDATE KW_VERTEX vid KW_SET update_list when_clause yield_clause {
auto sentence = new UpdateVertexSentence($3, $5, $6, $7);
$$ = sentence;
@@ -3218,11 +3230,26 @@ update_vertex_sentence
$$ = sentence;
}
// ======== End: Compatible with 1.0 =========
| KW_UPDATE KW_VERTEX KW_ON name_label vid KW_SET update_list when_clause yield_clause {

| KW_UPDATE KW_VERTEX KW_ON name_label vid_list KW_SET update_list when_clause yield_clause {
auto sentence = new UpdateVertexSentence($5, $4, $7, $8, $9);
$$ = sentence;
}
| KW_UPSERT KW_VERTEX KW_ON name_label vid KW_SET update_list when_clause yield_clause {
| KW_UPSERT KW_VERTEX KW_ON name_label vid_list KW_SET update_list when_clause yield_clause {
auto sentence = new UpdateVertexSentence($5, $4, $7, $8, $9, true);
$$ = sentence;
}
| KW_UPDATE KW_VERTEX KW_ON name_label vid_ref_expression KW_SET update_list when_clause yield_clause {
if(graph::ExpressionUtils::findAny($5,{Expression::Kind::kVar})) {
throw nebula::GraphParser::syntax_error(@5, "Parameter is not supported in update clause");
}
auto sentence = new UpdateVertexSentence($5, $4, $7, $8, $9);
$$ = sentence;
}
| KW_UPSERT KW_VERTEX KW_ON name_label vid_ref_expression KW_SET update_list when_clause yield_clause {
if(graph::ExpressionUtils::findAny($5,{Expression::Kind::kVar})) {
throw nebula::GraphParser::syntax_error(@5, "Parameter is not supported in update clause");
}
auto sentence = new UpdateVertexSentence($5, $4, $7, $8, $9, true);
$$ = sentence;
}
@@ -3271,8 +3298,19 @@ update_edge_sentence
auto sentence = new UpdateEdgeSentence($5, $7, $9, $4, $11, $12, $13, true);
$$ = sentence;
}
| KW_UPDATE KW_EDGE KW_ON name_label edge_keys
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
KW_SET update_list when_clause yield_clause {
auto sentence = new UpdateEdgeSentence($5, $4, $7, $8, $9);
$$ = sentence;
}
| KW_UPDATE KW_EDGE KW_ON name_label edge_key_ref
KW_SET update_list when_clause yield_clause {
auto sentence = new UpdateEdgeSentence($5, $4, $7, $8, $9);
$$ = sentence;
}
;


delete_vertex_sentence
: KW_DELETE KW_VERTEX vid_list {
auto sentence = new DeleteVerticesSentence($3, false);
@@ -3950,6 +3988,7 @@ mutate_sentence
| download_sentence { $$ = $1; }
| ingest_sentence { $$ = $1; }
| admin_job_sentence { $$ = $1; }
| lookup_pipe_update_sentence { $$ = $1; }
;

maintain_sentence
8 changes: 4 additions & 4 deletions tests/Makefile
Original file line number Diff line number Diff line change
@@ -44,21 +44,21 @@ test_j_sa = $(test_without_skip_sa) -n$(J)


install-deps:
pip3 install --user -Ur $(CURR_DIR)/requirements.txt -i $(PYPI_MIRROR)
pip3 install -Ur $(CURR_DIR)/requirements.txt -i $(PYPI_MIRROR)
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved

install-nebula-py: install-deps
git clone --branch release-3.5 https://github.com/vesoft-inc/nebula-python $(CURR_DIR)/nebula-python
cd $(CURR_DIR)/nebula-python \
&& pip3 install --user . -i $(PYPI_MIRROR) --upgrade
&& pip3 install . -i $(PYPI_MIRROR) --upgrade
rm -rf $(CURR_DIR)/nebula-python

gherkin-fmt: install-deps
@if [ $(PY_VERSION) -lt 7 ]; then echo 'Python version must >= 3.7'; exit 1; fi
pip3 install --user poetry
pip3 install poetry
git clone --branch master https://github.com/OneContainer/reformat-gherkin $(CURR_DIR)/reformat-gherkin
cd $(CURR_DIR)/reformat-gherkin && python3 -m poetry build
pip3 uninstall -y reformat-gherkin
pip3 install --user $(CURR_DIR)/reformat-gherkin/dist/reformat_gherkin*.whl
pip3 install $(CURR_DIR)/reformat-gherkin/dist/reformat_gherkin*.whl
rm -rf $(CURR_DIR)/reformat-gherkin

init: clean install-nebula-py
88 changes: 88 additions & 0 deletions tests/tck/features/lookup/LookUpUpdate.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
@lookup_update
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试用的标记符号不要提交

Feature: lookup and update

Background:
Given an empty graph
And create a space with following options:
| partition_num | 1 |
| replica_factor | 1 |
| vid_type | FIXED_STRING(32) |

Scenario: LookupTest lookup and update vertex
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
Given having executed:
"""
CREATE TAG lookup_tag_1(col1 int, col2 int, col3 int);
CREATE TAG lookup_tag_2(col1 bool, col2 int, col3 double, col4 bool);
CREATE TAG INDEX t_index_1 ON lookup_tag_1(col1, col2, col3);
CREATE TAG INDEX t_index_2 ON lookup_tag_2(col2, col3, col4);
CREATE TAG INDEX t_index_3 ON lookup_tag_1(col2, col3);
CREATE TAG INDEX t_index_4 ON lookup_tag_2(col3, col4);
CREATE TAG INDEX t_index_5 ON lookup_tag_2(col4);
"""
And wait 6 seconds
When executing query and retrying it on failure every 6 seconds for 3 times:
"""
INSERT VERTEX
lookup_tag_1(col1, col2, col3)
VALUES
"200":(200, 200, 200),
"201":(201, 201, 201),
"202":(202, 202, 202)
"""
Then the execution should be successful
When executing query:
"""
LOOKUP ON lookup_tag_1 WHERE lookup_tag_1.col2 == 200 YIELD id(vertex) as id
"""
Then the result should be, in any order:
| id |
| "200" |
When executing query:
"""
LOOKUP ON lookup_tag_1 WHERE lookup_tag_1.col2 == 200 YIELD id(vertex) as id | UPDATE VERTEX ON lookup_tag_1 $-.id SET col2 = 201
"""
Then the execution should be successful
When executing query:
"""
LOOKUP ON lookup_tag_1 WHERE lookup_tag_1.col2 == 201 YIELD id(vertex) as id
"""
Then the result should be, in any order:
| id |
| "200" |
| "201" |

Scenario: LookupTest lookup and update edge
Given having executed:
"""
CREATE EDGE lookup_edge_1(col1 int, col2 int, col3 int);
CREATE EDGE INDEX e_index_1 ON lookup_edge_1(col1, col2, col3);
CREATE EDGE INDEX e_index_3 ON lookup_edge_1(col2, col3);
"""
And wait 6 seconds
When executing query and retrying it on failure every 6 seconds for 3 times:
"""
INSERT EDGE
lookup_edge_1(col1, col2, col3)
VALUES
'200' -> '201'@0:(201, 201, 201),
'200' -> '202'@0:(202, 202, 202)
"""
Then the execution should be successful
When executing query:
"""
LOOKUP ON lookup_edge_1 WHERE lookup_edge_1.col2 > 200 YIELD src(edge) as src, dst(edge) as dst, rank(edge) as rank |
UPDATE EDGE ON lookup_edge_1 $-.src ->$-.dst@$-.rank SET col3 = 203
"""
Then the execution should be successful
When executing query:
"""
LOOKUP ON lookup_edge_1 YIELD
lookup_edge_1.col1 AS col1,
lookup_edge_1.col2 AS col2,
lookup_edge_1.col3
"""
Then the result should be, in any order:
| col1 | col2 | lookup_edge_1.col3 |
| 201 | 201 | 203 |
| 202 | 202 | 203 |
Then drop the used space
248 changes: 248 additions & 0 deletions tests/tck/features/update/MultiUpdate.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# Copyright (c) 2020 vesoft inc. All rights reserved.
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
#
# This source code is licensed under Apache 2.0 License.
@multi_update
Salieri-004 marked this conversation as resolved.
Show resolved Hide resolved
Feature: Multi Update string vid of vertex and edge

Background: Prepare space
Given an empty graph
And create a space with following options:
| partition_num | 1 |
| replica_factor | 1 |
| vid_type | FIXED_STRING(200) |
And having executed:
"""
CREATE TAG IF NOT EXISTS course(name string, credits int);
CREATE TAG IF NOT EXISTS building(name string);
CREATE TAG IF NOT EXISTS student(name string, age int, gender string);
CREATE TAG IF NOT EXISTS student_default(
name string NOT NULL,
age int NOT NULL,
gender string DEFAULT "one",
birthday int DEFAULT 2010
);
CREATE EDGE IF NOT EXISTS like(likeness double);
CREATE EDGE IF NOT EXISTS select(grade int, year int);
CREATE EDGE IF NOT EXISTS select_default(grade int NOT NULL,year TIMESTAMP DEFAULT 1546308000);
"""
And having executed:
"""
INSERT VERTEX
student(name, age, gender)
VALUES
"200":("Monica", 16, "female"),
"201":("Mike", 18, "male"),
"202":("Jane", 17, "female");
INSERT VERTEX
course(name, credits),
building(name)
VALUES
"101":("Math", 3, "No5"),
"102":("English", 6, "No11");
INSERT VERTEX course(name, credits) VALUES "103":("CS", 5);
INSERT EDGE
select(grade, year)
VALUES
"200"->"101"@0:(5, 2018),
"200"->"102"@0:(3, 2018),
"201"->"102"@0:(3, 2019),
"202"->"102"@0:(3, 2019);
INSERT EDGE
like(likeness)
VALUES
"200"->"201"@0:(92.5),
"201"->"200"@0:(85.6),
"201"->"202"@0:(93.2);
"""

Scenario: multi update test
When executing query:
"""
UPDATE VERTEX ON course "101", "102"
SET credits = credits + 1;
"""
Then the execution should be successful
When executing query:
"""
UPDATE VERTEX ON course "101", "102"
SET credits = credits + 1
WHEN name == "Math" AND credits > 2
"""
Then the execution should be successful
When executing query:
"""
UPDATE VERTEX ON course "101", "102"
SET credits = credits + 1
YIELD name AS Name, credits AS Credits
"""
Then the result should be, in any order:
| Name | Credits |
| 'Math' | 6 |
| 'English' | 8 |
When executing query:
"""
UPDATE VERTEX ON course "101", "102"
SET credits = credits + 1
WHEN name == "Math" AND credits > 2
YIELD name AS Name, credits AS Credits
"""
Then the result should be, in any order:
| Name | Credits |
| 'Math' | 7 |
| 'English' | 8 |
When executing query:
"""
UPDATE VERTEX ON course "101", "102"
SET credits = credits + 1
WHEN name == "nonexistent" AND credits > 2
YIELD name AS Name, credits AS Credits
"""
Then the result should be, in any order:
| Name | Credits |
| 'Math' | 7 |
| 'English' | 8 |
When executing query:
"""
FETCH PROP ON select "200"->"101"@0 YIELD select.grade, select.year
"""
Then the result should be, in any order:
| select.grade | select.year |
| 5 | 2018 |
When executing query:
"""
FETCH PROP ON select "200" -> "102"@0 YIELD select.grade, select.year
"""
Then the result should be, in any order:
| select.grade | select.year |
| 3 | 2018 |
# update edge succeeded
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET grade = grade + 1, year = 2000;
"""
Then the execution should be successful
When executing query:
"""
FETCH PROP ON select "200"->"101"@0 YIELD select.grade, select.year
"""
Then the result should be, in any order:
| select.grade | select.year |
| 6 | 2000 |
When executing query:
"""
FETCH PROP ON select "200"->"102"@0 YIELD select.grade, select.year
"""
Then the result should be, in any order:
| select.grade | select.year |
| 4 | 2000 |
When executing query:
"""
GO FROM "101" OVER select REVERSELY YIELD select.grade, select.year
"""
Then the result should be, in any order:
| select.grade | select.year |
| 6 | 2000 |
# 2.0 test, filter out
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET grade = grade + 1, year = 2000
WHEN grade > 1024
"""
Then the execution should be successful
# set filter
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET grade = grade + 1, year = 2000 WHEN grade > 4
"""
Then the execution should be successful
# set yield
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET grade = grade + 1, year = 2018
YIELD grade AS Grade, year AS Year
"""
Then the result should be, in any order:
| Grade | Year |
| 8 | 2018 |
| 5 | 2018 |
# filter and yield
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET grade = select.grade + 1, year = 2019
WHEN select.grade > 6
YIELD select.grade AS Grade, select.year AS Year
"""
Then the result should be, in any order:
| Grade | Year |
| 9 | 2019 |
| 5 | 2018 |
# set filter out and yield
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET grade = grade + 1, year = 2019
WHEN grade > 233333333333
YIELD grade AS Grade, year AS Year
"""
Then the result should be, in any order:
| Grade | Year |
| 9 | 2019 |
| 5 | 2018 |
# update vertex: the item is TagName.PropName = Expression in SET clause
When executing query:
"""
UPDATE VERTEX ON course "101", "102"
SET credits = credits + 1, name = "No9"
"""
Then the execution should be successful
# make sure TagName and PropertyName must exist in all clauses
When executing query:
"""
UPDATE VERTEX ON nonexistentTag "101"
SET credits = credits + 1
"""
Then a SemanticError should be raised at runtime: No schema found for `nonexistentTag'
# make sure EdgeName and PropertyName must exist in all clauses
When executing query:
"""
UPDATE EDGE ON select "200"->"101"@0, "200"->"102"@0
SET nonexistentProperty = grade + 1, year = 2019
"""
Then a ExecutionError should be raised at runtime: Storage Error: Edge prop not found.
# make sure the edge_type must not exist
When executing query:
"""
UPDATE EDGE ON nonexistentEdgeTypeName "200"->"101"@0, "200"->"102"@0
SET grade = grade + 1, year = 2019
"""
Then a SemanticError should be raised at runtime: No schema found for `nonexistentEdgeTypeName'
When executing query:
"""
FETCH PROP ON course "103" YIELD course.name, course.credits
"""
Then the result should be, in any order:
| course.name | course.credits |
| "CS" | 5 |
When executing query:
"""
UPDATE VERTEX ON course "103"
SET credits = credits + 1
WHEN name == "CS" AND credits > 2
YIELD name AS Name, credits AS Credits
"""
Then the result should be, in any order:
| Name | Credits |
| 'CS' | 6 |
# when tag ON vertex not exists, update failed
When executing query:
"""
UPDATE VERTEX ON course "103", "104" SET credits = credits + 1
WHEN name == "CS" AND credits > 2
YIELD name AS Name, credits AS Credits
"""
Then a ExecutionError should be raised at runtime: Storage Error: Vertex or edge not found.
1 change: 1 addition & 0 deletions tests/tck/features/update/Update.IntVid.feature
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2020 vesoft inc. All rights reserved.
#
# This source code is licensed under Apache 2.0 License.
@update
Feature: Update int vid of vertex and edge

Scenario: update and upsert test