图遍历步骤(Graph Traversal Steps)

在最一般的层次上,Traversal<S,E>实现了Iterator,S代表起点,E代表结束。遍历由四个主要组成部分组成:

  • Step<S,E>: 一个用来从S产生E的方法。Step在遍历中是链式的。
  • TraversalStrategy: 拦截器方法来改变遍历的执行(例如查询重写)。
  • TraversalSideEffects: 键/值对,可用于存储有关遍历的全局信息。
  • Traverser: the object propagating through the Traversal currently representing an object of type T.

示例数据

示例数据大多出自于Modern数据,如下图:

可以通过下图加载Modern图数据,并获取图遍历引用g:

gremlin> graph = TinkerFactory.createModern()
gremlin> g = graph.traversal()

1. General Steps

Step Description
map(Traversal<S, E>) map(Function<Traverser<S>, E>) 将运行程序映射到类型E的某个对象,以便下一步处理。
flatMap(Traversal<S, E>) flatMap(Function<Traverser<S>, Iterator<E>>) 将遍历器映射到流向下一步的E对象的迭代器。
filter(Traversal<?, ?>) filter(Predicate<Traverser<S>>) 将运行程序映射为true或false,其中false将不会将运行程序传递给下一步。
sideEffect(Traversal<S, S>) sideEffect(Consumer<Traverser<S>>) 在移动器上执行一些操作,并将其传递到下一步。
branch(Traversal<S, M>) branch(Function<Traverser<S>,M>) 将移动器拆分为由M令牌索引的所有遍历。

  • filter步骤的示例
gremlin> g.V().filter(label().is('person'))
  • map步骤的示例
gremlin> g.V(1).out().map(values('name'))
  • sideEffect步骤的示例
gremlin> g.V().hasLabel('person').sideEffect(System.out.&println)
  • branch步骤的示例
gremlin> g.V().branch(values('name')).
option('marko', values('age')).
option(none, values('name'))

2. Terminal Steps

一些步骤不返回遍历,而是执行遍历并返回结果。这些步骤就是Terminal步骤(终端步骤),并且通过下面的示例来解释它们。

gremlin> g.V().out('created').hasNext()
==>true
gremlin> g.V().out('created').next()
==>v[3]
gremlin> g.V().out('created').next(2)
==>v[3]
==>v[5]
gremlin> g.V().out('nothing').tryNext()
==>Optional.empty
gremlin> g.V().out('created').toList()
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.V().out('created').toSet()
==>v[3]
==>v[5]
gremlin> g.V().out('created').toBulkSet()
==>v[3]
==>v[3]
==>v[3]
==>v[5]
gremlin> results = ['blah',3]
==>blah
==>3
gremlin> g.V().out('created').fill(results)
==>blah
==>3
==>v[3]
==>v[5]
==>v[3]
==>v[3]

注意几点:

  1. 上述tryNext()将返回一个Optional的,是一个hasNext()/next()的组合。
  2. toSet()toBulkSet()都返回一个去重的集合,区别在于后者通过加权处理重复对象。

3. AddEdge Step

推理是明确显示数据中隐含的内容的过程。图中显式的是图形的对象,即顶点和边。图中隐含的是遍历。即,通过遍历揭示了意义,而所谓的意义是有遍历的定义来决定的。

比如定义一个遍历,找出节点的合作开发者(co-developer),并在这种关系(边)上添加属性“year”:

gremlin> g.V(1).as('a').out('created').in('created').where(neq('a')).
addE('co-developer').from('a').property('year',2009)

如此,曾经隐含的含义可以可以通过addE()-step(map/sideEffect)来显式。

Question

这里addE()-step(map/sideEffect)意味着addE()步骤产生了map和sideEffect普通步骤的效果吗?

4. AddVertex Step

addV()步骤用于向图中添加顶点(map/sideEffect)。

增加顶点,示例如下:

gremlin> g.addV('person').property('name','stephen')
gremlin> g.V().outE('knows').addV().property('name','nothing')

Question

gremlin> g.V().outE('knows').addV().property('name','nothing')这种方式并没有增加节点之间的关联(边),那么它的意义在哪里?

5. AddProperty Step

property() 步骤用于向图形元素添加属性(sideEffect)。与addV()addE()不同,property()是一个完全的sideEffect步骤,它不会返回其创建的属性,而是返回添加属性的元素。那么,可以推测出在addV()addE()步骤后使用property()步骤可以在创建顶点或边的时候一次性创建多个属性。

  • 为一个节点添加一个属性
gremlin> g.V(1).property('country','usa')
  • 为一个节点添加多个属性
gremlin> g.V(1).property('city','santa fe').property('state','new mexico').valueMap()
  • (对顶点)添加多值属性(Cardinality.list)
gremlin> g.V(1).property(list,'age',35)
  • 添加元属性
gremlin> g.V(1).properties('name').property('author','inspur')

6. Aggregate Step

aggregate()步骤(sideEffect)用于将遍历特定点处的所有对象(贪婪评估方式eager evaluation)聚合到集合中。

aggregate step

gremlin> g.V(1).out('created').aggregate('x').in('created').out('created').
where(without('x')).values('name')
==>ripple

7. And Step

and()步骤确保所有提供的遍历产生结果(filter)。请参阅or()

gremlin> g.V().and(
outE('knows'),
values('age').is(lt(30))).
values('name')
==>marko

使用or()步骤会产生以下结果:

gremlin> g.V().or(
outE('knows'),
values('age').is(lt(30))).
values('name')
==>marko
==>vadas

8. As Step

as()步骤不是一个真正的步骤,而是一个类似于by()option()的“调节器”。使用as(),可以为步骤提供标签,后续的步骤或数据结果可以通过使用这些标签访问这些步骤。

gremlin> g.V().as('a').out('created').as('b').select('a','b')
==>[a:v[1],b:v[3]]
==>[a:v[4],b:v[5]]
==>[a:v[4],b:v[3]]
==>[a:v[6],b:v[3]]

一步可以有任何数量的与之相关联的标签。这对于在将来的步骤中多次引用相同的步骤很有用:

gremlin> g.V().hasLabel('software').as('a','b','c').
select('a','b','c').
by('name').
by('lang').
by(__.in('created').values('name').fold())
==>[a:lop,b:java,c:[marko,josh,peter]]
==>[a:ripple,b:java,c:[josh]]

Note

以上__.(两个_)通过匿名方式产生了GraphTraversal。并使用了后续介绍的fold()步骤。

9. Barrier Step

barrier() 步骤将延迟遍历管道转换为批量同步管道。可类比线程同步中的栅栏。在以下情况下,此步骤很有用:

  • 使用在需要栅栏的场景
  • 进行“膨胀优化”(bulking optimization)

膨胀优化对于某些程序执行效率的提高是非常明显的,如下示例:

gremlin> g = graph.traversal().withoutStrategies(LazyBarrierStrategy) //屏蔽默认的遍历策略
gremlin> clockWithResult(1){g.V().both().both().both().count().next()} //未使用barrier
gremlin> clockWithResult(1){g.V().both().barrier().both().barrier().both().barrier().count().next()} //使用barrier

上述优化过程需要去掉LazyBarrierStrategy 遍历策略,即默认情况下遍历器已经使用LazyBarrierStrategy 进行了优化(barrier()步骤会在适当的情况下自动添加)。

  • 支持参数

    如果barrier()被提供一个整数参数n,那么在将聚合的遍历器入下一个步骤之前,栅栏只会在其屏障中保留n个唯一的遍历器。

10. By Step

如果一个步骤能够接受遍历、函数或比较器等,那么by()是添加它们的手段。

gremlin> g.V().group().by(bothE().count()) //将元素按其边数进行分组,属于接受“遍历”的情况
==>[1:[v[2],v[5],v[6]],3:[v[1],v[3],v[4]]]
gremlin> g.V().group().by(bothE().count()).by('name') //将通过其名称(元素属性投影)处理分组的元素
==>[1:[vadas,ripple,peter],3:[marko,lop,josh]]
gremlin> g.V().group().by(bothE().count()).by(count()) //将计算每个组中的元素数量
==>[1:3,3:3]
  • 哪些步骤支持by()

    以下步骤支持by(),注意有些步骤支持一个by(),有些步骤支持多个by(),使用时需要参考文档。
dedup()
cyclicPath()
simplePath()
sample()
where()
groupCount()
group()
order()
path()
project()
select()
tree()
aggregate()
store()

11. Cap Step

cap()用来将一些副作用步骤产生的结果发射(emits)出来。

如下使用label进行分组统计,并使用cap()将分组结果展现出来:

gremlin> g.V().groupCount('a').by(label) //不使用cap()
==>v[1]
==>v[2]
==>v[3]
==>v[4]
==>v[5]
==>v[6]
gremlin> g.V().groupCount('a').by(label).cap('a') //使用cap()
==>[software:2,person:4]

另外,cap()支持多个key值,不同的key会组成 Map<String,Object>效果展现出来。

12. Choose Step

choose()将当前遍历器路由到特定的遍历分支选项。可用来实现if-then-else语法结构。

判断条件可以放在choose步骤中,也可以放在option步骤中:

  • choose中进行判断
gremlin> g.V().hasLabel('person').
choose(values('age').is(lte(30)),
__.in(),
__.out()).values('name')
==>marko
==>ripple
==>lop
==>lop
  • option中进行判断
gremlin> g.V().hasLabel('person').
choose(values('age')).
option(27, __.in()).
option(32, __.out()).values('name')
==>marko
==>ripple
==>lop

13. Coalesce Step

coalesce() - 步骤按顺序评估提供的遍历,并返回发出至少一个元素的第一个遍历。

coalesce()可以传入多个遍历,并对这些遍历按照顺序进行评估。若某个元素匹配上某个遍历,那么返回该元素在该遍历上产生的结果,然后跳到下一个元素进行相同的操作。

比如,当person含有nickname则返回nickname,当含有name则返回name;不会即返回nickname又返回name;当既没有nickname也没有name,则返回空。

gremlin> g.V().hasLabel('person').coalesce(values('nickname'), values('name'))
==>okram
==>vadas
==>josh
==>peter

14. Coin Step

要随机过滤出一个遍历器,请使用coin() 步骤(filter)。

gremlin> g.V().coin(0.0) //不返回节点
gremlin> g.V().coin(0.5) //随机返回节点,注意它并不是随机返回一半
gremlin> g.V().coin(1.0) //返回全部

15. Constant Step

要为运行程序指定常量值,请使用常量constant() 步骤(map)。对于诸如choose()或`coalesce() 的条件步骤通常很有用。

gremlin> g.V().hasLabel('person').coalesce(values('nickname11'), values('name11'),constant("haha"))
==>haha
==>haha
==>haha
==>haha

16. Count Step

属于map步骤范畴。举例如下:

gremlin> g.V().count()
==>6
gremlin> g.V().hasLabel('person').count()
==>4

该步骤是减少栅栏的步骤(reducing barrier step),意味着所有先前的遍历器被折叠成新的遍历器。

gremlin> g.V().hasLabel('person').outE('created').path()
==>[v[1],e[9][1-created->3]]
==>[v[4],e[10][4-created->5]]
==>[v[4],e[11][4-created->3]]
==>[v[6],e[12][6-created->3]]
gremlin> g.V().hasLabel('person').outE('created').count()
==>4
gremlin> g.V().hasLabel('person').outE('created').count().path()
==>[4]

17. CyclicPath Step

每个遍历器在遍历图形的时候会维护其历史,即其路径。如果重要的是遍历器重复它的过程,那么应该使用cyclic()- 路径(filter)。

该步骤分析到目前为止的运行程序的路径,并且如果有任何重复,则遍历器被过滤掉以免重复遍历。

如果需要非循环行为,请参阅simplePath()

gremlin> g.V(1).both().both().cyclicPath().path() //查看循环的遍历路径
==>[v[1],v[3],v[1]]
==>[v[1],v[2],v[1]]
==>[v[1],v[4],v[1]]
gremlin> g.V(1).both().both().simplePath().path() //查看非循环的遍历路径
==>[v[1],v[3],v[4]]
==>[v[1],v[3],v[6]]
==>[v[1],v[4],v[5]]
==>[v[1],v[4],v[3]]

18. Dedup Step

使用dedup()步骤(filter),重复的对象将从遍历流中删除。

gremlin> g.V().values('lang')
==>java
==>java
gremlin> g.V().values('lang').dedup()
==>java

19. Drop Step

drop()步骤(filter/sideEffect)用于从图形中删除元素和属性(即删除)。它是一个过滤器步骤,因为遍历不产生传出对象。

gremlin> g.V().outE().drop() //删除边
gremlin> g.E()
gremlin> g.V().properties('name').drop() 删除属性
gremlin> g.V().valueMap()
==>[age:[29]]
==>[age:[27]]
==>[lang:[java]]
==>[age:[32]]
==>[lang:[java]]
==>[age:[35]]
gremlin> g.V().drop() //删除图
gremlin> g.V()

20. Explain Step

explain() 步骤(terminal)将返回一个TraversalExplanation。遍历说明详细说明了如何根据注册的遍历策略编译遍历(在explain()之前)。

第一列是应用的遍历策略。第二列是遍历策略类别:[D]ecoration,[O]ptimization,[P]rovider optimization,[F]inalization和[V]erification。最后,第三列是遍历后策略应用的状态。最终遍历是最终的执行计划。

21. Fold Step

有些情况下,遍历流需要“栅栏”来聚合所有对象并产生作为聚合函数的计算结果。fold()步骤(map)就是这样的例子。

gremlin> g.V(1).out('knows').values('name')
==>vadas
==>josh
gremlin> g.V(1).out('knows').values('name').fold() //简单合并
==>[vadas,josh]
gremlin> g.V(1).out('knows').values('name').fold(0) {a,b -> a + b.length()} //统计name值的字符长度
==>9
gremlin> g.V(1).out('knows').values('name').fold(0) {a,b -> a + b} //合并所有名字为一个长串
==>vadasjosh

22. Graph Step

V() 步骤通常用于启动GraphTraversal,但也可以在遍历中间使用。

gremlin> g.V().has('name', within('marko', 'vadas', 'josh')).as('person').
V().has('name', within('lop', 'ripple')).addE('uses').from('person')
==>e[13][1-uses->3]
==>e[14][1-uses->5]
==>e[15][2-uses->3]
==>e[16][2-uses->5]
==>e[17][4-uses->3]
==>e[18][4-uses->5]

23. From Step

from()不是一个真正的步骤,而是类似by()as()等的步骤调节器(step-modulator)。如果一个步骤可以接受一个遍历器或者String,那么可以用from()

  • 接受from()的步骤
simplePath()
cyclicPath()
path()
addE()

TinkerPop中的遍历:图的遍历步骤(1/3)的更多相关文章

  1. TinkerPop中的遍历:图的遍历步骤(3/3)

    48 Project Step project() 步骤(map)将当前对象投射到由提供的标签键入的Map<String,Object>中. gremlin> g.V().out(' ...

  2. TinkerPop中的遍历:图的遍历步骤(2/3)

    24 Group Step 有时,所运行的实际路径或当前运行位置不是计算的最终输出,而是遍历的一些其他表示.group()步骤(map / sideEffect)是根据对象的某些功能组织对象的一个方法 ...

  3. TinkerPop中的遍历:图的遍历策略

    遍历策略 一个TraversalStrategy分析一个遍历,如果遍历符合它的标准,可以相应地改变它.遍历策略在编译时被执行,并构成Gremlin遍历机的编译器的基础.有五类策略分列如下: decor ...

  4. TinkerPop中的遍历:图的遍历中谓词、栅栏、范围和Lambda的说明

    关于谓词的注意事项 P是Function<Object,Boolean>形式的谓词.也就是说,给定一些对象,返回true或false.所提供的谓词在下表中概述,并用于各种步骤,例如has( ...

  5. Python 非递归遍历图

    class Queue: def __init__(self,max_size): self.max_size = int(max_size) self.queue = [] def put(self ...

  6. 图的遍历BFS广度优先搜索

    图的遍历BFS广度优先搜索 1. 简介 BFS(Breadth First Search,广度优先搜索,又名宽度优先搜索),与深度优先算法在一个结点"死磕到底"的思维不同,广度优先 ...

  7. 【PHP数据结构】图的遍历:深度优先与广度优先

    在上一篇文章中,我们学习完了图的相关的存储结构,也就是 邻接矩阵 和 邻接表 .它们分别就代表了最典型的 顺序存储 和 链式存储 两种类型.既然数据结构有了,那么我们接下来当然就是学习对这些数据结构的 ...

  8. 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)

    图的遍历的定义: 从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次.(连通图与非连通图) 深度优先遍历(DFS): 1.访问指定的起始顶点: 2.若当前访问的顶点的邻接顶点有未被访问的,则 ...

  9. Java中关于HashMap的元素遍历的顺序问题

    Java中关于HashMap的元素遍历的顺序问题 今天在使用如下的方式遍历HashMap里面的元素时 1 for (Entry<String, String> entry : hashMa ...

随机推荐

  1. 每天一个linux命令(16):tail命令

    版权声明更新:2017-05-20博主:LuckyAlan联系:liuwenvip163@163.com声明:吃水不忘挖井人,转载请注明出处! 1 文章介绍 本文介绍了Linux下面的mv命令. 2. ...

  2. 1151 LCA in a Binary Tree(30 分)

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  3. Unity之将Texture保存成png

    http://blog.csdn.net/bingheliefeng/article/details/51177505 using UnityEngine;using System.Collectio ...

  4. Maven里头的pom.xml配置详解

    正常的pom配置文件如下所示: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http ...

  5. 洛谷【P1004】方格取数

    浅谈\(DP\):https://www.cnblogs.com/AKMer/p/10437525.html 题目传送门:https://www.luogu.org/problemnew/show/P ...

  6. java中List、Map、Set、Collection、Stack、Queue等的使用

    java中这几个东西是比较常用的,虽然我用的不多,也正是因为用的不多,所以我一直搞不清楚他们之间的具体用法以及相互之间的关系,现在特单独作为一个东西来总结一下. 本文参考一下资料: 1.<jav ...

  7. echo 的部分用法

    echo “内容” > 文件名 (会覆盖文件里的所有内容) echo “内容” >> 文件名 (追加内容到文件里,会另起一行写入) 如果您阅读过此文章有所收获,请为我顶一个,如果文章 ...

  8. eclipse中删除tomcat server 导致不能重新创建该server

    定位到:workspace\.metadata\.plugins\org.eclipse.core.runtime\.settings 1 打开org.eclipse.jst.server.tomca ...

  9. Mysql 5.6 MHA (gtid) on Kylin

    mha on Kylinip hostname repl role mha role192.168.19.69 mysql1 master node192.168.19.73 mysql2 slave ...

  10. Mybatis 内置 Java 类型别名与 typeHandlers

    aliases There are many built-in type aliases for common Java types. They are all case insensitive, n ...