3.3.3
图的遍历
barrier()-step
(barrier)将原有管道延迟转换为大容量同步管道。这一步骤可在以下场景使用:
当需要在某个步骤执行之前,需要之前的Step全部执行完毕方可执行时,可用barrier() (例如排序)
当通过barrier() “延缓”Step时,可能会促使重复元素的“批量优化”(即优化)。
gremlin\> g.V().sideEffect{
println "first: \${it}"}.sideEffect{
println
"second: \${it}"}.iterate()
- first: v[1]
- second: v[1]
- first: v[2]
- second: v[2]
- first: v[3]
- second: v[3]
- first: v[4]
- second: v[4]
- first: v[5]
- second: v[5]
- first: v[6]
- second: v[6]
- gremlin\> g.V().sideEffect{
println "first:
\${
it}"}.barrier().sideEffect{println "second: \${
it}"}.iterate()
- first: v[1]
- first: v[2]
- first: v[3]
- first: v[4]
- first: v[5]
- first: v[6]
- second: v[1]
- second: v[2]
- second: v[3]
- second: v[4]
- second: v[5]
- second: v[6]
“批量优化”背后的理论很简单。如果顶点1有一百万次遍历,那么就不需要计算一百万个both()计算。相反,使用traverser
.bulk()将这100万遍历表示为单个遍历等同于这100万遍历再执行一次both()。下面的例子展示了利用Grateful
Dead图,在大的Graph上作批量优化例子。
> gremlin\> graph = TinkerGraph.open()
> ==\>tinkergraph[vertices:0 edges:0]
> gremlin\> graph.io(graphml()).readGraph('data/grateful-dead.xml')
> gremlin\> g = graph.traversal().withoutStrategies(LazyBarrierStrategy)
> ==\>graphtraversalsource[tinkergraph[vertices:808 edges:8049], standard]
> gremlin\> clockWithResult(1){
g.V().both().both().both().count().next()}
> ==\>10339.5204
> ==\>126653966
> gremlin\> clockWithResult(1){
g.V().repeat(both()).times(3).count().next()}
> \\
> ==\>29.239853
> ==\>126653966
> gremlin\>
> clockWithResult(1){
g.V().both().barrier().both().barrier().both().barrier().count().next()}
> ==\>14.841833
> ==\>126653966
用LazyBarrierStrategy(批量优化)显式地删除。
每个遍历器处理非大量的遍历。
每个进入repeat()的遍历器都将其递归增涨。
不处理隐式遍历的大容量遍历。
如果barrier()提供了一个整数参数,那么barrier在将聚合遍历释放到下一个步骤之前,它的barrier中只包含n个惟一性的遍历器。这在前面提到的批量优化场景中非常有用,还可以减少内存不足异常的风险。
LazyBarrierStrategy将barrier()步骤插入到适当的遍历中,以获得“批量优化”。
> gremlin\> graph = TinkerGraph.open()
> ==\>tinkergraph[vertices:0 edges:0]
> gremlin\> graph.io(graphml()).readGraph('data/grateful-dead.xml')
> gremlin\> g = graph.traversal() \\
> ==\>graphtraversalsource[tinkergraph[vertices:808 edges:8049], standard]
> gremlin\> clockWithResult(1){
g.V().both().both().both().count().next()}
> ==\>17.782709
> ==\>126653966
> gremlin\> g.V().both().both().both().count().iterate().toString()
> /==\>[TinkerGraphStep(vertex,**[]**), VertexStep(BOTH,vertex),
> NoOpBarrierStep(2500), VertexStep(BOTH,vertex), NoOpBarrierStep(2500),
> VertexStep(BOTH,edge), CountGlobalStep, NoneStep]
LazyBarrierStrategy是默认策略,因此不需要显式激活。
激活LazyBarrierStrategy后,barrier()步骤将自动插入适当的位置。
其他用法
barrier(), barrier(Consumer), barrier(int)
by()-step不是一个实际的步骤,而是一个类似as()和option()的“步进调制器”。如果一个步骤能够接受遍历、函数、比较器等,那么by()就是添加它们的方法。一般的模式是step().by()…by()。有些步骤只能接受一次
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(outE().count())将根据元素的边计数(遍历之后)对其进行分组。
by(‘name’)将按名称处理分组的元素(元素属性投影)。
by(count())将计算每个组中的元素数量(遍历)。
下面的Step均支持by()操作。使用方式也遵循 step-by-step的逻辑,同时,在文档的其他部分中也有描述。
dedup():根据 by()的结果而去重。
cyclicPath():通过by()过滤遍历器的循环路径。
simplePath():通过by()过滤遍历器的简单路径。
sample():使用by()-的返回值进行采样。
where():给定by()-调制的测试结果,确定谓词。
groupCount():对 by()过滤的group keys进行计数。
group():根据by()-过滤分组。
order():根据by()-过滤排序。
path():根据by()-过滤遍历器的路径。
project():根据by()-不同的过滤条件获取当前对象map的结果。
select():根据by()-过滤路径元素。
tree():根据by()-过滤获得一个Tree遍历器。
aggregate():存储合并集合中by()-过滤的属性值。
store():存储合并集合中by()-过滤的对象值。
其他用法
by(), by(Comparator), by(Function,Comparator), by(Function), by(Order), by(String), by(String,Comparator), by(T), by(Traversal), by(Traversal,Comparator), T, Order
cap()-step (barrier)
是自引用遍历。如果提供了多个key,则sideEffect 的结果会是 Map
> gremlin\> g.V().groupCount('a').by(label).cap('a') \\
> ==\>[software:2,person:4]
> gremlin\>
> g.V().groupCount('a').by(label).groupCount('b').by(outE().count()).cap('a','b')
> ==\>[a:[software:2,person:4],b:[0:3,1:1,2:1,3:1]]
根据顶点的label对顶点进行分组和计数。以a为标记此次结果,即按label计数的分组。
与语句1相同,但也会产生标记为“b”的结果,它根据输出边的数量对顶点进行分组。
其他用法
cap(String,String…)
choose ()-step
(branch)将当前遍历提供路由选择器。使用choose(),可以实现if/then/else语义以及更复杂的选择方式。
> gremlin\> g.V().hasLabel('person').
> choose(values('age').is(lte(30)),
> \_\_.in(),
> \_\_.out()).values('name') \\
> ==\>marko
> ==\>ripple
> ==\>lop
> ==\>lop
> gremlin\> g.V().hasLabel('person').
> choose(values('age')).
> option(27, \_\_.in()).
> option(32, \_\_.out()).values('name') \\
> ==\>marko
> ==\>ripple
> ==\>lop
如果遍历查找label=person的元素,如果age<=30,那么执行in,否则执行out,最终取name。
同样的查找,只是在判断时,27才执行In,
32执行Out。意思是找到person属性顶点中27的入边名称和32的出边名称
Choose是三元式,如果没有提供“false”-分支,则默认用If /then实现。
gremlin> g.V().choose(hasLabel(‘person’), out(‘created’)).values(‘name’) ==>lop ==>lop ==>ripple ==>lop ==>ripple ==>lop gremlin> g.V().choose(hasLabel(‘person’), out(‘created’), identity()).values(‘name’) ==>lop ==>lop ==>ripple ==>lop ==>ripple ==>lop |
---|
如果有person标签则找到他们出边created的顶点name,否则输出自己的name。
与1 等价。
注意,choose()和options 可以任意组合多个,而且可以将匿名遍历作为其选择函数。
> gremlin\> g.V().hasLabel('person').
> choose(values('name')).
> option('marko', values('age')).
> option('josh', values('name')).
> option('vadas', valueMap()).
> option('peter', label())
> ==\>29
> ==\>[name:[vadas],age:[27]]
> ==\>josh
> ==\>person
choose()-step可以Pick.none提供未匹配的场景。对于任何与指定选项不匹配的内容,将采用none-选项。
> gremlin\> g.V().hasLabel('person').
> choose(values('name')).
> option('marko', values('age')).
> option(none, values('name'))
> ==\>29
> ==\>vadas
> ==\>josh
> ==\>peter
其他用法
choose(Function), choose(Predicate,Traversal), choose(Predicate,Traversal,Traversal), choose(Traversal,Traversal), choose(Traversal,Traversal,Traversal), choose(Traversal)
coalesce()步骤按遍历顺序将多个traversal 合并,并返回至少产生一个元素的第一次遍历。
> gremlin\> g.V(1).coalesce(outE('knows'),
> outE('created')).inV().path().by('name').by(label)
> ==\>[marko,knows,vadas]
> ==\>[marko,knows,josh]
> gremlin\> g.V(1).coalesce(outE('created'),
> outE('knows')).inV().path().by('name').by(label)
> ==\>[marko,created,lop]
> gremlin\> g.V(1).property('nickname', 'okram')
> ==\>v[1]
> gremlin\> g.V().hasLabel('person').coalesce(values('nickname'),
> values('name'))
> ==\>okram
> ==\>vadas
> ==\>josh
> ==\>peter
其他用法
coalesce(Traversal…)
,使用coin()-step (filter) 作为随机筛选遍历器。coin
使用double类型参数,结果会有所偏差。
> gremlin\> g.V().coin(0.5)
> ==\>v[1]
> ==\>v[2]
> ==\>v[5]
> ==\>v[6]
> gremlin\> g.V().coin(0.0)
> gremlin\> g.V().coin(1.0)
> ==\>v[1]
> ==\>v[2]
> ==\>v[3]
> ==\>v[4]
> ==\>v[5]
> ==\>v[6]
其他用法
coin(double)
constant()-step
(map)可以将一个常数值作为一个遍历器,在choose()-step或coalesce()-step中比较有用。
> gremlin\> g.V().choose(hasLabel('person'),
> values('name'),
> constant('inhuman'))
> ==\>marko
> ==\>vadas
> ==\>inhuman
> ==\>josh
> ==\>inhuman
> ==\>peter
> gremlin\> g.V().coalesce(
> hasLabel('person').values('name'),
> constant('inhuman')) \\
> ==\>marko
> ==\>vadas
> ==\>inhuman
> ==\>josh
> ==\>inhuman
> ==\>peter
Label=person的顶点显示name属性,不是的则显示inhuman。
与表述1相同 coalesce的写法。
其他用法
constant(Object)
count()-step (map)对流中所表示的遍历器的总数进行计数(即批量计数)。
> gremlin\> g.V().count()
> ==\>6
> gremlin\> g.V().hasLabel('person').count()
> ==\>4
> gremlin\> g.V().hasLabel('person').outE('created').count().path() \\
> ==\>[4]
> gremlin\> g.V().hasLabel('person').outE('created').count().map {
it.get() \*
> 10}.path()
> ==\>[4,40]
count()-步骤是一个 reducing
barrier 步骤,意思是说所有以前的遍历器都被折叠到一个新的遍历器中。
由count()发出的遍历路径从count()开始。
IMPORTANT | count(local)对当前本地对象(而不是遍历流中的对象)进行计数。这适用于 Collection - - Map类型的对象。对于任何其他对象,返回的计数为1。 |
---|
其他用法
count(), count(Scope), Scope
CyclicPath (环路) Step
每个遍历器通过遍历图来维护其历史——即path。如果遍历器重复它的过程很重要,那么应该使用cycle
()-path
(filter)。这个Step分析遍历器到目前为止的路径,如果有任何重复,遍历器将在遍历计算中过滤掉。如果需要非循环的处理,请参阅simplePath()。
> gremlin\> g.V(1).both().both()
> ==\>v[1]
> ==\>v[4]
> ==\>v[6]
> ==\>v[1]
> ==\>v[5]
> ==\>v[3]
> ==\>v[1]
> gremlin\> g.V(1).both().both().cyclicPath()
> ==\>v[1]
> ==\>v[1]
> ==\>v[1]
> 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).as('a').out('created').as('b').
> in('created').as('c').
> cyclicPath().
> path()
> ==\>[v[1],v[3],v[1]]
> gremlin\> g.V(1).as('a').out('created').as('b').
> in('created').as('c').
> cyclicPath().from('a').to('b').
> path()
其他用法
cyclicPath
()
Dedup(去重) Step
使用dedup()-step
(filter),可以从遍历流中删除重复看到的对象。注意,如果遍历器的容量大于1,则在遍历之前将其设置为1。
> gremlin\> g.V().values('lang')
> ==\>java
> ==\>java
> gremlin\> g.V().values('lang').dedup()
> ==\>java
> gremlin\> g.V(1).repeat(bothE('created').dedup().otherV()).emit().path() \\
> ==\>[v[1],e[9][1-created-\>3],v[3]]
> ==\>[v[1],e[9][1-created-\>3],v[3],e[11][4-created-\>3],v[4]]
> ==\>[v[1],e[9][1-created-\>3],v[3],e[12][6-created-\>3],v[6]]
> ==\>[v[1],e[9][1-created-\>3],v[3],e[11][4-created-\>3],v[4],e[10][4-created-\>5],v[5]]
如果dedup()和By-step 结合使用,则在确定它是否被看到之前对该对象进行相应的处理。
> gremlin\> g.V().valueMap(true, 'name')
> ==\>[id:1,name:[marko],label:person]
> ==\>[id:2,name:[vadas],label:person]
> ==\>[id:3,name:[lop],label:software]
> ==\>[id:4,name:[josh],label:person]
> ==\>[id:5,name:[ripple],label:software]
> ==\>[id:6,name:[peter],label:person]
> gremlin\> g.V().dedup().by(label).values('name')
> ==\>marko
> ==\>lop
最后,如果提供了字符串数组dedup(),那么它将确保重复数据删除不是针对当前Step的遍历器对象,而是针对遍历器的路径历史(即dedup之前的语句)。
> gremlin\>
> g.V().as('a').out('created').as('b').in('created').as('c').select('a','b','c')
> ==\>[a:v[1],b:v[3],c:v[1]]
> ==\>[a:v[1],b:v[3],c:v[4]]
> ==\>[a:v[1],b:v[3],c:v[6]]
> ==\>[a:v[4],b:v[5],c:v[4]]
> ==\>[a:v[4],b:v[3],c:v[1]]
> ==\>[a:v[4],b:v[3],c:v[4]]
> ==\>[a:v[4],b:v[3],c:v[6]]
> ==\>[a:v[6],b:v[3],c:v[1]]
> ==\>[a:v[6],b:v[3],c:v[4]]
> ==\>[a:v[6],b:v[3],c:v[6]]
> gremlin\>
> g.V().as('a').out('created').as('b').in('created').as('c').dedup('a','b').select('a','b','c')
> ==\>[a:v[1],b:v[3],c:v[1]]
> ==\>[a:v[4],b:v[5],c:v[4]]
> ==\>[a:v[4],b:v[3],c:v[1]]
> ==\>[a:v[6],b:v[3],c:v[1]]
其他用法
dedup(Scope,String…), dedup(String…), Scope
drop()步骤(filter/sideEffect)模式均可)用于从图中删除元素和属性(即remove)。它是一个过滤器步骤,因为它不会产出对象。
> 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()
其他用法
drop()
emit-step不是一个实际意义上的步骤,而是repeat()的一个步骤调节器(在那里可以找到emit()的更多文档)。
其他用法
emit(), emit(Predicate), emit(Traversal)
explain()-step
(terminal)将返回一个TraversalExplanation。遍历解释详细说明了给定已注册的遍历策略如何编译遍历(在explain()之前)。TraversalExplanation 的toString()返回三列。第一列是应用的遍历策略。第二列是遍历策略类别:[D]生态化,[O]ptimization,
[P]rovider optimization, [F]inalization,
[V]erification。第三列是遍历公布的策略应用程序的状态。最后的遍历是结果的执行计划。
> gremlin\>
> g.V().hasLabel('person').outE().identity().inV().count().is(gt(5)).explain()
> ==\>Traversal Explanation
> =====================================================================================================================================================================================================
> Original Traversal [GraphStep(vertex,**[]**), HasStep([\~label.eq(person)]),
> VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN), CountGlobalStep,
> IsStep(gt(5))]
> ConnectiveStrategy [D] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> MatchPredicateStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> FilterRankingStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> InlineFilterStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> IncidentToAdjacentStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> RepeatUnrollStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> PathRetractionStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
> CountStrategy [O] [GraphStep(vertex,**[]**), HasStep([\~label.eq(person)]),
> VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN),
> RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
> AdjacentToIncidentStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
> LazyBarrierStrategy [O] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
> TinkerGraphCountStrategy [P] [GraphStep(vertex,**[]**),
> HasStep([\~label.eq(person)]), VertexStep(OUT,edge), IdentityStep,
> EdgeVertexStep(IN), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
> TinkerGraphStepStrategy [P] [TinkerGraphStep(vertex,[\~label.eq(person)]),
> VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN),
> RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
> ProfileStrategy [F] [TinkerGraphStep(vertex,[\~label.eq(person)]),
> VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN),
> RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
> StandardVerificationStrategy [V]
> [TinkerGraphStep(vertex,[\~label.eq(person)]), VertexStep(OUT,edge),
> IdentityStep, EdgeVertexStep(IN), RangeGlobalStep(0,6), CountGlobalStep,
> IsStep(gt(5))]
> Final Traversal [TinkerGraphStep(vertex,[\~label.eq(person)]),
> VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN),
> RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
有关遍历分析信息,请参见profile()步骤。
在某些情况下,遍历流需要一个“barrier”来聚合所有对象并发出一个计算,该计算是聚合的一个函数。fold()-step
(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().next().getClass()
> ==\>**class java**.util.ArrayList
> gremlin\> g.V(1).out('knows').values('name').fold(0) {
a,b -\> a +
> b.length()} \\
> ==\>9
> gremlin\> g.V().values('age').fold(0) {
a,b -\> a + b}
> ==\>123
> gremlin\> g.V().values('age').fold(0, sum)
> ==\>123
> gremlin\> g.V().values('age').sum() \\
> ==\>123
无参的fold()将把所有对象聚合到一个列表中,然后发出这个列表。
对返回的列表类型进行验证。
fold()可以提供两个参数—一个种子值和一个reduce的bi方法(“vadas”是5个字符+“josh”是4个字符)。
图中这些人的总年龄是多少?
功能同上,但是使用内置的bi方法。
功能同上,使用sum()步骤。
其他用法
fold(), fold(Object,BiFunction)
from()-step不是一个实际的步骤,而是一个类似as()和by()的“Step调节器”。如果一个步骤能够接受遍历或字符串,则使用from()来指代要添加的对象。一般模式是step().from()。参考to()。
支持from()-的步骤有:
simplePath(), cyclicPath(), path(),
and addE()
其他用法
from(String), from(Traversal), from(Vertex)
V()步骤通常用于开始graph遍历,但也可以在遍历的中间使用。
> 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]
NOTE | 是否mid-traversal V()是否使用索引,取决于 a)是否存在合适的索引 b)是否graph 系统具备索引功能。 |
---|
> gremlin\> g.V().has('name', within('marko', 'vadas', 'josh')).as('person').
> V().has('name', within('lop',
> 'ripple')).addE('uses').from('person').toString() \\
> ==\>[GraphStep(vertex,**[]**), HasStep([name.within([marko, vadas,
> josh])])\@[person], GraphStep(vertex,**[]**), HasStep([name.within([lop,
> ripple])]), AddEdgeStep({
\~from=[[SelectOneStep(last,person)]],
> label=[uses]})]
> gremlin\> g.V().has('name', within('marko', 'vadas', 'josh')).as('person').
> V().has('name', within('lop',
> 'ripple')).addE('uses').from('person').iterate().toString() \\
> ==\>[TinkerGraphStep(vertex,[name.within([marko, vadas, josh])])\@[person],
> TinkerGraphStep(vertex,[name.within([lop, ripple])]),
> AddEdgeStep({
\~from=[[SelectOneStep(last,person)]], label=[uses]}),
> NoneStep]
通常,V()步骤将遍历所有顶点。然而,graph策略可以折叠 HasContainer’s into a
`GraphStep ,以允许索引查找。
通过检查迭代遍历的toString()输出,可以很容易地确定图形系统提供程序是否支持中间遍历V()索引查找。如果has 条件被折叠到V()步骤中,将使用索引(如果存在)。
其他用法
V(Object…)