第6期 Gremlin Steps:
repeat()
、times()
、until()
、emit()
、loops()
本系列文章的Gremlin示例均在HugeGraph图数据库上执行,环境搭建可参考准备Gremlin执行环境,本文示例均以其中的“TinkerPop关系图”为初始数据,如下图所示:
循环操作说明
循环操作是指多次执行某一部分语句,用于语句需要重复运行的场景,比如“查找朋友的朋友的朋友”,可以直接使用循环操作来完成即“查找3层朋友”,下面对具体的循环相关的Step进行说明:
repeat()
: 指定要重复执行的语句,如repeat(out('friend'))
times()
: 指定要重复执行的次数,如执行3次repeat(out('friend')).times(3)
until()
: 指定循环终止的条件,如一直找到某个名字的朋友为止repeat(out('friend')).until(has('name','xiaofang'))
emit()
: 指定循环语句的执行过程中收集数据的条件,每一步的结果只要符合条件则被收集,不指定条件时收集所有结果loops()
: 当前循环的次数,可用于控制最大循环次数等,如最多执行3次repeat(out('friend')).until(loops().is(3))
实例讲解
下面通过实例来深入理解每一个操作。
-
Step
repeat()
+times()
:按照指定的次数重复执行语句示例1:
// 访问某个顶点的OUT邻接点(1次) // 注意'okram'是顶点的id g.V('okram').repeat(out()).times(1)
示例2:
// 访问某个顶点的2度双向邻接点 // 访问第1个顶点的所有邻接点(第1层) // 再访问第1层结果顶点的邻接点(第2层) g.V('okram').repeat(both()).times(2)
示例3:
// 访问某个顶点的3度OUT邻接点 // 访问第1个顶点的所有邻接点(第1层) // 再访问第1层结果顶点的邻接点(第2层) // 再访问第2层结果顶点的邻接点(第3层) g.V('okram').repeat(out()).times(3)
比一比:
g.V('okram').out().out().out()
g.V('okram').repeat(out()).times(3)
-
Step
repeat()
+until()
:根据条件来重复执行语句示例1:
// 查询顶点'okram'到顶点'Gremlin'之间的路径 // 循环的终止条件是遇到名称是'Gremlin'的顶点 g.V('okram') .repeat(out()) .until(has('name', 'Gremlin')) .path()
注意1:这里用到了
path()
来获取经过的路径,path的讲解请参考上一期。注意2:
until()
与times()
是互斥的,两个语句无法同时存在于同一个循环中。注意3:
until()
放在repeat()
之前或之后的顺序是会影响逻辑的,放前面表示先判断再执行,放后面表示先执行后判断。请对比如下两个语句的执行结果:
g.V('okram').repeat(out()).until(hasLabel('person')).path()
g.V('okram').until(hasLabel('person')).repeat(out()).path()
-
Step
repeat()
+emit()
:收集执行过程中的数据示例1:
// 查询顶点'okram'的所有OUT可达点的路径 g.V('okram') .repeat(out()) .emit() .path()
示例2:
// 查询顶点'okram'的所有OUT可达点的路径 // 且必须满足是'person'类型的点 g.V('okram') .repeat(out()) .emit(hasLabel('person')) .path()
注意:
emit()
放在repeat()
之前或之后的顺序是会影响结果的,放前面表示先收集再执行,放后面表示先执行后收集。请对比如下两个语句的执行结果:
g.V('okram').repeat(out()).emit(hasLabel('person')).path()
g.V('okram').emit(hasLabel('person')).repeat(out()).path()
示例3:
// 查询顶点'okram'到顶点'Gremlin'之间的路径 // 此外还收集过程中的'person'类型的顶点 g.V('okram') .repeat(out()) .until(has('name', 'Gremlin')) .emit(hasLabel('person')) .path()
注意:
emit()
与until()
搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。示例4:
// 查询顶点'okram'的2度OUT可达点的路径 // 此外还收集'person'类型的顶点 g.V('okram') .repeat(out()).times(2) .emit(hasLabel('person')) .path()
注意:
emit()
与times()
搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。 -
Step
repeat()
+loops()
:根据最大次数限制来重复执行语句示例1:
// 查询顶点'okram'的3度OUT可达点路径 g.V('okram') .repeat(out()) .until(loops().is(3)) .path()
示例2:
// 查询顶点'okram'到顶点'Gremlin'之间的路径 // 且之间只相差2跳的距离 // 其中的and()是指两个条件都满足 g.V('okram') .repeat(out()) .until(has('name', 'Gremlin') .and().loops().is(2)) .path()
综合运用
-
查找子树
// 查找从一个节点出发,到 // 叶子节点结束的所有路径 // 这些路径的集合为一颗子树(子图) g.V('okram') .repeat(out()) .until(outE().count().is(0)) .path()
-
查找两点之间的最短路径
// 已知两个顶点'okram'和'javeme', // 通过任意关系来找到这两点之间的路径 // 且限制了最大深度为3 // 若存在那么第一条结果即是最短路径 g.V('okram') .repeat(bothE().otherV().simplePath()) .until(hasId('javeme').and().loops().is(lte(3))) .hasId('javeme') .path()
注意:
bothE().otherV()
一般等价于both()
,但是在这里有一些差别,后者仅仅返回路径中的顶点信息,前者会把路径中的边信息也返回。试一试:将语句中的
lte(3)
改为lte(4)
看看结果有什么变化