-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Conversation
看了下新增的MULTIUPDATE,和原有的 UPDATE 基本上语法是一致的,只是 vid 支持多个。 |
另外,关于 LOOKUP | UPDATE 语法,确实简化了很多操作,但个人不是很建议,几个原因是:
|
感谢@JackChuengQAQ 的贡献。对于上述的feature,我同意 @MuYiYong 的观点。
|
|
👍 好的。先删掉LOOKUP|UPDATE的语法吧。 |
我已经补充了一个简单的修改
|
@JackChuengQAQ 我注意到您为MultiUpdate和UpdateRef重新实现了新的parser,validator,executor,考虑到MultiUpdate和UpdateRef只是在为原有的Update扩充功能,且大部分的逻辑实际上是可以公用的,我想在原有Update的parser,validator,executor上进行扩充会更好,这样可以省去很多冗余的重复代码,也方便后续的修改和维护。从我的角度看,似乎在把更新单个vertex和更新多个vertex的语义区分两套实现是很奇怪的,后者应该是前者的超集。 |
我正在学习使用 nebula 的 tests,但在部署测试环境时遇到了困难。详见 https://discuss.nebula-graph.com.cn/t/topic/16129
更新多个节点的方法(MultiUpdate)可以直接合成到原有的 Update,相当于原有的 Update 直接接收 vid_list 而不是 v_id。 对于 UpdateRef,您看是否需要合并(还是单独拆分出两个算子比较好呢)?如果要合并 MultiUpdate 和 UpdateRef 算子的话。能想到的方法是将 Multi 的多个节点首先构建成 dataset,然后统一使用 UpdateRef 算子 (目前 delete 算子的做法)。但我感觉这个构建数据集的过程其实是不必要的。 |
您好,我认为参考Fetch或Delete的实现即可,这也和我们往常的设计风格相统一。我认为“构建数据集”的过程相比于网络和存储的开销来说或许是微不足道的。如果您认为没必要先构建一个dataset,我们或许可以仅在executor做一些输入源的判断。比如
|
补充了两个 commit
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
同学您好,请使用clang-format格式化代码并通过CI的format check。请根据comment修改您的PR。
@@ -0,0 +1,88 @@ | |||
@lookup_update |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
测试用的标记符号不要提交
按照 @Salieri-004 的意见进行了第一次修改。
|
40e4c19
to
10c175a
Compare
我的理解在于,两者检查的后续处理似乎并不相同。 |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
麻烦修复CI的UT failed并重新提交
10c175a
to
83fb894
Compare
第三次提交修改。 对于 MutateEdgeUtils 的友类实现。考虑到 checkInput 中会调用 DeleteEdgeValidator 和 UpdateEdgeValidator 的成员变量以及调用 Validator 基类的成员函数,现 MutateEdgeUtils 拟实现如下: |
好的,我认为没问题 |
Head branch was pushed to by a user without write access
00ba5c9
to
2c79dc2
Compare
2c79dc2
to
3f6637e
Compare
LGTM. 感谢您的贡献! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great Job.
What type of PR is this?
What problem(s) does this PR solve?
Issue(s) number:
#5934
Description:
批量更新语句 (MULTIUPDATE)
在原生 Nebula 中,UPDATE 语句的操作限制为一次仅更新一个节点或一条边。为实现 LOOKUP | UPDATE 语句的功能,首先必须扩展 UPDATE 的能力,以支持批量更新操作。因此,新定义了 MULTIUPDATE 语句,以支持批量更新操作。这一新增功能允许同时更新多个节点或边,并为复杂查询和更新需求提供了基本支持。
批量节点更新
更新后 UPDATE VERTEX 语句可以更新点,相较于原生 UPDATE 语句,可以一次更新一个点或者多个点。
批量节点更新的语法如下,与原生 UPDATE 保持一致
批量边更新
UPDATE EDGE 语句可以更新边,相较于原生 UPDATE 语句,可以一次更新一条边或者多个边。
批量边更新的语法如下,与原生 UPDATE 保持一致
查询 & 更新语句 (LOOKUP | UPDATE)
查询 & 更新点
为了更新满足特定条件的部分数据的需求,设计使用管道符,连接 LOOKUP 和 UPDATE 语句
使用管道符
UPDATE VERTEX 语句可以结合管道符使用,将 LOOKUP 语句的查询结果传递给 UPDATE VERTEX 语句,实现查询 & 更新的同时进行。
查询 & 更新边
类似地,对于更新边,提供了管道符的方式。
使用管道符
UPDATE VERTEX 语句可以结合管道符使用,将 LOOKUP 语句的查询结果传递给 UPDATE VERTEX 语句,实现查询 & 更新的同时进行。
How do you solve it?
技术实现主要分为以下两个部分:
对于新增算子,根据 Nebula 数据库的架构,Parser->Validator->Planner->Executor,新增算子至少需要在四个模块中增加对应的代码。
UpdateMulti- 算子实现
UpdateMulti- 算子包括 UpdateMultiVertex 算子和 UpdateMultiEdge 算子,分别对应批量节点更新和批量边更新。通过增加 UpdateMulti- 算子,实现了节点属性和边属性的批量更新。
Parser
在定义了新关键字 KW_MULTIUPDATE ("multiupdate") 后,
update_multi_vertex_sentence
语句和update_multi_edge_sentence
语句定义如下。update_multi_vertex_sentence
语句update_multi_edge_sentence
语句相比于原生 UPDATE 语句,MULTIUPDATE VERTEX 语句将
vid
替换为vid_lists
; MULTIUPDATE EDGE 语句将vid R_ARROW vid
、vid R_ARROW vid AT rank
等多种表示一条边的方式,替换为可以包含多个边的edge_keys
。在 Mutate Sentence 中,新增 UpdateMultiVertexSentence 类和 UpdateMultiEdgeSentence 类,对相应的输入信息进行存储,使用 Kind::kMultiVertex 和 Kind::kMultiEdge 进行标识,并在下一阶段进行使用。
Validator
Validator 类主要由三个函数组成。首先,构造函数接受得到的 Sentence,对 Validator 类进行初始化;其次,调用 Validator 类的
ValidateImpl
函数,对语句内容进行校验;最后,调用toPlan
函数,构造查询节点和对应的查询计划。对应地,在 MutateValidator 中新增了 UpdateMultiVertexValidator 类和 UpdateMultiEdgeValidator 类。相比于原生的 Update 语句,UpdateMulti-算子对应的 Validator 需要对节点(边)列表中的每一个节点(边)进行校验。
UpdateMultiVertexValidator::validateImpl()
节点校验部分移植原生的校验流程,对 vidList 中的每一个 vid 进行校验。
UpdateMultiEdgeValidator::validateImpl()
边校验部分在原生的
UpdateEdgeValidator
中,srcid
,dstid
,rank
作为直接的成员变量。由于UpdateEdgeValidator
需要一次性处理多条边,使用 EdgeKeys() 对边信息进行管理。为了提高代码的可读性,新建了 EdgeId 结构体,用于存储每条边的srcid
,dstid
,rank
信息。对于
toPlan
函数,则是构造算子对应的计划节点。值得注意的是,在图数据库 Nebula 中,一条边会以入边和出边的形式分别存储在两个端点中,即正向边src->dst 和反向边 dst->src。在更新边时,需要构建两个 UpdateEdge 计划节点,进行两次更新。
UpdateMultiEdgeValidator::toPlan()
采取了类似的方式。 其中,反向边的 edgeType 是-edgeType_
。函数最后的布尔值参数表示更新的是正向边 (false) 还是反向边 (true)。Planner
在 Planner 的 Mutate 计划节点中,新增了 UpdateMultiVertex 类 和 UpdateMultiEdge 类。UpdateMultiVertex 类与原生的 UpdateVertex 类相似,将 vid 替换为了 vid_list。UpdateMultiEdge 类采用相似的方法,在 UpdateEdge 类中,由于每次只更新一条边,可以在构建计划节点时直接交换 src 和 dst 节点的位置实现对反向边的更新。在 UpdateMultiEdge 类中,新增了布尔值随机变量
isRerverse_
, 以表示更新的是否为反向边。Executor
在 Executor 中,Executor 类接收计划节点,并向 storage 层发送 Request 执行,在执行完成后,接收 Responce 并对结果进行汇总。对应地,在 UpdateExecutor 中新增了 UpdateMultiVertexExecutor 类和 UpdateMultiEdgeExecutor 类。
相比于原生的 UpdateVertexExecutor 和 UpdateEdgeExecutor,UpdateMultiVertexExecutor 类和 UpdateMultiEdgeExecutor 类在执行过程中一次异步地向存储层发送多个请求,并对结果进行统一地处理。以 UpdateMultiVertexExecutor 类的
execute
函数为例。UpdateMultiVertexExecutor::execute()
请求发送和回复处理部分观察代码,首先,对应 vid_list 中的每一个节点,构建并向 storage 层发送 updateVertex 请求 Request;其次,使用
folly::collectAll
对每个请求得到的 Responce 进行汇总 (包括错误码检查和结果处理);最后,如果存在 yield 子句 (即value.props_ref().has_value()
为真),还需要将结果汇总到数据集中进行返回。为了在 Executor 中能对多个 Responce 进行处理和汇总,在 Update 基类中增加了成员函数
handleMultiResult
能够将多个 Responce 汇总到一个数据集中。在新增的四个算子中,该成员函数均被共享和使用。handleMultiResult
函数实现UpdateRef- 算子实现
UpdateRef- 算子包括 UpdateRefVertex 算子和 UpdateRefEdge 算子,分别对应节点更新和边更新。UpdateRef- 算子允许 Update 算子使用引用属性,接受其他语句的结果并进行处理,是 lookup | update 功能实现的基础。通过增加 RefMulti- 算子,允许 UPDATE 语句引用属性,实现 lookup | update 功能。
Parser
仍然使用 MULTIUPDATE 关键字,将引用属性作为需要更新的对象。
update_ref_vertex_sentence
语句和update_ref_edge_sentence
语句定义如下。update_ref_vertex_sentence
语句在构建 UpdateRefVertexSentence,可以提前对引用属性进行校验。
update_ref_edge_sentence
语句相比于原生 UPDATE 语句,支持 UpdateRef- 算子的语句将
vid
替换为引用属性vid_ref
; MULTIUPDATE EDGE 语句表示一条边的方式,替换为可以包含$-.src, $-.dst, $-.rank
的edge_key_ref
。在 Mutate Sentence 中,对应地新增 UpdateRefVertexSentence 类和 UpdateRefVertexSentence 类,对相应的输入信息进行存储,使用 Kind::kRefVertex 和 Kind::kRefEdge 进行标识,并在下一阶段进行使用。
Validator
对应地,在 MutateValidator 中新增了 UpdateRefVertexValidator 类和 UpdateRefEdgeValidator 类。
相比于原生的 Update 语句,UpdateRef-算子对应的 Validator 需要对引用属性进行校验。以 UpdateRefVertexValidator 类中的校验部分为例。
UpdateRefVertexValidator::validateImpl()
引用属性校验部分首先从 sentence 中提取 vidRef_,然后调用
deduceExprType
对引用属性进行解析,最后进行类型校验。类似地,在 UpdateRefEdgeValidator 类中,需要对
$-.src, $-.dst, $-.rank
分别进行引用属性的校验。在调用
toPlan
函数时,使用一个dedup
计划节点对接收到的引用属性内容进行去重,再将去重后的结果传递给UpdateRefVertex
或UpdateRefEdge
计划节点。UpdateRefVertexValidator::toPlan()
计划构建部分UpdateRefEdgeValidator::toPlan()
计划构建部分Planer
在 Planner 的 Mutate 计划节点中,新增了 UpdateRefVertex 类 和 UpdateRefEdge 类。UpdateRef- 计划节点的实现难点主要在于,原生 Nebula 中的 Update 基类的基类是 SingleDependencyNode,本身不具备接收输入信息的接口。因此,为了使得新增 UpdateRef- 算子能够正确地接收引用属性的输入,新建了 UpdateRef 基类,继承 SingleInputNode 类型,然后在 UpdateRef 基类的基础上,对 UpdateRefVertex 类 和 UpdateRefEdge 类进行了实现。
UpdateRef
基类的定义Executor
对应地,在 UpdateExecutor 中新增了 UpdateRefVertexExecutor 类和 UpdateRefEdgeExecutor 类。与 UpdateMultiVertexExecutor 类似,在执行过程中一次异步地向存储层发送多个请求,并对结果进行统一地处理。UpdateRef- 算子在构建请求之前,需要根据引用属性,提取出接收的结果。以 UpdateRefVertexExecutor 类为例。
UpdateRefVertexExecutor::execute()
属性提取部分。首先,根据 vidRef,从计划节点的祖先节点中获取
inputVar
;再使用getResult()
函数根据inputVar
获取inputResult
;最后遍历iter
,将需要更新的节点 id 置入vertices
中。提取出需要更新的节点(边)后,即可以使用类似于 UpdateMulti- 算子的方式,对这些节点(边)进行批量更新。
由于 UpdateRefEdgeExecutor 类将正向边和反向边的处理设计在一个计划节点中,因此,UpdateRefEdgeExecutor 需要同时构造正向和反向边,即:
###LOOKUP | UPDATE 的语句改写
在使用管道符的语句中,通过管道符
|
对 LOOKUP 语句和 UPDATE 语句进行连接,构建 PipedSentence,在 LOOKUP 语句执行完毕后,将结果输送给 UPDATE 语句进行执行。LOOKUP | UPDATE VERTEX 语句本质上等价于 通过管道符
|
对 LOOKUP 语句和 UPDATE 语句进行连接。但希望通过语法解析器,避免用户在使用过程中对于节点信息和引用属性的繁琐输入。对于两个语句,tag name 应当是相同的。对于第一个执行的 LOOKUP 语句,其 YIELD 子句应当返回id(vertex) as id
;对于第二个执行的 UPDATE 语句,其接受的引用属性应当为$-.id
。因此,在语法解析器中,需要手动添加idExpr, "id"
作为 LOOKUP 语句的 YieldColumn;同时,$-.id
作为 UPDATE 语句的引用属性。值得注意的是,在语法解析器中,需要对字符串tag_name
进行深拷贝复制。 最后使用构造的 lookup_sentence 和 update_ref_vertex_sentence 构建 lookup_pipe_update_sentence。类似的,对于LOOKUP | UPDATE EDGE 语句,进行类似的改写。对于两个语句,edge type 应当是相同的。对于第一个执行的 LOOKUP 语句,其 YIELD 子句应当返回
src(edge) as src, dst(edge) as dst, rank(edge) as rank
;对于第二个执行的 UPDATE 语句,其接受的引用属性应当为$-.src -> $-.dst @ $-.rank
。因此,在语法解析器中,需要手动添加"src", "dst", "rank"
作为 LOOKUP 语句的 YieldColumn;同时,利用$-.src, $-.dst, $-.rank
构建 EdgeKeyRef 作为 UPDATE EDGE 语句的引用属性。最后使用构造的 lookup_sentence 和 update_ref_edge_sentence 构建 lookup_pipe_update_sentence。Special notes for your reviewer, ex. impact of this fix, design document, etc:
Checklist:
Tests:
Affects:
Release notes:
Please confirm whether to be reflected in release notes and how to describe: