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

图遍历步骤(Graph Traversal Steps)

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

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

示例数据

示例数据大多出自于Modern数据,如下图:
TinkerPop中的遍历:图的遍历步骤(1/3)_第1张图片

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

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

1. General Steps

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

TinkerPop中的遍历:图的遍历步骤(1/3)_第2张图片

  • 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
775861-20171103155007779-977210934.png

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效果展现出来。

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()

转载于:https://www.cnblogs.com/myitroad/p/7778455.html

你可能感兴趣的:(TinkerPop中的遍历:图的遍历步骤(1/3))