TinkerPop Gremlin 图遍历用法词典步骤A-M 点击标题免费查看全文
文章目录
- [TinkerPop Gremlin 图遍历用法词典步骤A-M](https://zhuanlan.zhihu.com/p/677990445) 点击标题免费查看全文
- 通用步骤
- 终端步骤
- AddE 步骤
- AddV 步骤
- Aggregate 步骤
- All 步骤
- And 步骤
- Any 步骤
- As 步骤
- AsString 步骤
- AsDate 步骤
- Barrier Step
- Branch Step
- By Step
- Call Step
- Cap Step
- Choose Step
- Coalesce Step
- Coin Step
- Combine Step
- Concat Step
- Conjoin Step
- ConnectedComponent Step
- Constant Step
- Count Step
- CyclicPath Step
- Cap Step
- Choose Step
- DateAdd步骤
- DateDiff步骤
- Dedup步骤
- Difference步骤
- Disjunct步骤
- Drop步骤
- E步骤
- Element步骤
- ElementMap步骤
- Emit步骤
- Explain步骤
- Fail步骤
- Filter 步骤
- FlatMap 步骤
- Format 步骤
- Fold 步骤
- From 步骤
- Group 步骤
- GroupCount 步骤
- Has 步骤
- Id 步骤
- Identity 步骤
- Index步骤
- Inject步骤
- Intersect步骤
- IO步骤
- GraphML
- GraphSON
- Gryo
- Is步骤
- Key步骤
- Label步骤
- Length Step
- Limit Step
- Local Step
- Loops Step
- LTrim Step
- Map Step
- Match Step
- 使用Where与Match
- 使用Where与Match
Gremlin步骤被链接在一起以生成实际的遍历,并通过
GraphTraversalSource
上的
开始步骤触发。
重要提示 | 关于Gremlin语言的更多详细信息可以在Gremlin语义部分中的提供程序文档中找到。 |
---|---|
通用步骤
有五个通用步骤,每个步骤都有一个遍历和一个lambda表示,后面描述的所有其他具体步骤都是基于这些通用步骤扩展的。
步骤 | 描述 |
---|---|
map(Traversal<S, E>) map(Function<Traverser<S>, E>) |
将traverser映射到下一步要处理的某个类型为E 的对象。 |
flatMap(Traversal<S, E>) flatMap(Function<Traverser<S>, Iterator<E>>) |
将traverser映射到一个E 对象的迭代器,该迭代器将流式传递给下一步。 |
filter(Traversal<?, ?>) filter(Predicate<Traverser<S>>) |
将traverser映射为true或false,其中false不会将traverser传递给下一步。 |
sideEffect(Traversal<S, S>) sideEffect(Consumer<Traverser<S>>) |
对traverser执行某些操作,并将其传递给下一步。 |
branch(Traversal<S, M>) branch(Function<Traverser<S>,M>) |
将traverser拆分为由M 标记索引的所有遍历。 |
警告 | 为了教育目的,演示了lambda步骤,因为它们代表了Gremlin语言的基本构造。在实践中,应避免使用lambda步骤,而应使用它们的遍历表示,并且存在遍历验证策略来禁止使用它们,除非明确"关闭"。有关lambda的问题的更多信息,请阅读A Note on Lambdas。 |
---|---|
Traverser<S>
对象提供对以下内容的访问:
- 当前遍历的
S
对象 -Traverser.get()
。 - 遍历器当前遍历的路径 -
Traverser.path()
。- 获取特定路径历史记录对象的快捷方式 -
Traverser.path(String) == Traverser.path().get(String)
。
- 获取特定路径历史记录对象的快捷方式 -
- 遍历器已经经过当前循环的次数 -
Traverser.loops()
。 - 由该遍历器表示的对象数量 -
Traverser.bulk()
。 - 与此遍历器关联的局部数据结构 -
Traverser.sack()
。 - 与遍历相关的副作用 -
Traverser.sideEffects()
。- 获取特定副作用的快捷方式 -
Traverser.sideEffect(String) == Traverser.sideEffects().get(String)
。
- 获取特定副作用的快捷方式 -
gremlin> g.V(1).out().values('name') //// (1)
==>lop
==>vadas
==>josh
gremlin> g.V(1).out().map {it.get().value('name')} //// (2)
==>lop
==>vadas
==>josh
gremlin> g.V(1).out().map(values('name')) //// (3)
==>lop
==>vadas
==>josh
g.V(1).out().values('name') //// (1)
g.V(1).out().map {it.get().value('name')} //// (2)
g.V(1).out().map(values('name')) //3
- 从顶点1出发,获取相邻顶点的名称值。
- 相同的操作,但使用lambda来访问名称属性值。
- 再次相同的操作,但使用
map()
的遍历表示。
gremlin> g.V().filter {it.get().label() == 'person'} //// (1)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().filter(label().is('person')) //// (2)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().hasLabel('person') //// (3)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
g.V().filter {it.get().label() == 'person'} //// (1)
g.V().filter(label().is('person')) //// (2)
g.V().hasLabel('person') //3
- 只有带有"person"标签的顶点才能通过过滤器。
- 相同的操作,但使用
filter()
的遍历表示。 - 更具体的
has()
-step实际上是使用相应谓词的filter()
。
gremlin> g.V().hasLabel('person').sideEffect(System.out.&println) //// (1)
v[1]
==>v[1]
v[2]
==>v[2]
v[4]
==>v[4]
v[6]
==>v[6]
gremlin> g.V().sideEffect(outE().count().aggregate(local,"o")).
sideEffect(inE().count().aggregate(local,"i")).cap("o","i") //// (2)
==>[i:[0,0,1,1,1,3],o:[3,0,0,0,2,1]]
g.V().hasLabel('person').sideEffect(System.out.&println) //// (1)
g.V().sideEffect(outE().count().aggregate(local,"o")).
sideEffect(inE().count().aggregate(local,"i")).cap("o","i") //2
sideEffect()
中的任何内容都会传递到下一步,但可能会发生一些干预操作。- 计算每个顶点的出度和入度。两个
sideEffect()
接收相同的顶点。
gremlin> g.V().branch {it.get().value('name')}.
option('marko', values('age')).
option(none, values('name')) //// (1)
==>29
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().branch(values('name')).
option('marko', values('age')).
option(none, values('name')) //// (2)
==>29
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().choose(has('name','marko'),
values('age'),
values('name')) //// (3)
==>29
==>vadas
==>lop
==>josh
==>ripple
==>peter
g.V().branch {it.get().value('name')}.
option('marko', values('age')).
option(none, values('name')) //// (1)
g.V().branch(values('name')).
option('marko', values('age')).
option(none, values('name')) //// (2)
g.V().choose(has('name','marko'),
values('age'),
values('name')) //3
- 如果顶点是"marko",则获取他的年龄,否则获取顶点的名称。
- 相同的操作,但使用
branch()
的遍历表示。 - 更具体的基于布尔值的
choose()
-step实际上是branch()
的实现。
终端步骤
通常,当一个步骤连接到一个遍历中时,将返回一个新的遍历。通过这种方式,遍历可以以流畅、单子的方式逐步构建起来。然而,有些步骤并不返回遍历,而是执行遍历并返回结果。这些步骤被称为**终端步骤(terminal)**并通过下面的示例进行解释。
gremlin> g.V().out('created').hasNext() //// (1)
==>true
gremlin> g.V().out('created').next() //// (2)
==>v[3]
gremlin> g.V().out('created').next(2) //// (3)
==>v[3]
==>v[5]
gremlin> g.V().out('nothing').tryNext() //// (4)
==>Optional.empty
gremlin> g.V().out('created').toList() //// (5)
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.V().out('created').toSet() //// (6)
==>v[3]
==>v[5]
gremlin> g.V().out('created').toBulkSet() //// (7)
==>v[3]
==>v[3]
==>v[3]
==>v[5]
gremlin> results = ['blah',3]
==>blah
==>3
gremlin> g.V().out('created').fill(results) //// (8)
==>blah
==>3
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.addV('person').iterate() //// (9)
g.V().out('created').hasNext() //// (1)
g.V().out('created').next() //// (2)
g.V().out('created').next(2) //// (3)
g.V().out('nothing').tryNext() //// (4)
g.V().out('created').toList() //// (5)
g.V().out('created').toSet() //// (6)
g.V().out('created').toBulkSet() //// (7)
results = ['blah',3]
g.V().out('created').fill(results) //// (8)
g.addV('person').iterate() //9
hasNext()
确定是否有可用的结果(在gremlin-javascript
中不支持)。next()
返回下一个结果。next(n)
返回列表中的下一个n
个结果(在gremlin-javascript
或 Gremlin.NET 中不支持)。tryNext()
返回一个Optional
,因此是hasNext()
/next()
的组合(仅支持 JVM 语言)。toList()
返回所有结果的列表。toSet()
返回所有结果的集合,并且会去除重复项(在gremlin-javascript
中不支持)。toBulkSet()
返回所有结果的加权集合,并且通过加权保留重复项(仅支持 JVM 语言)。fill(collection)
将所有结果放入提供的集合中,并在完成时返回集合(仅支持 JVM 语言)。iterate()
不完全符合终端步骤的定义,因为它不返回结果,但仍然返回一个遍历。然而,它作为终端步骤的行为是迭代遍历并生成副作用,而不返回实际结果。
还有一个名为 promise()
的终端步骤,只能与连接到 Gremlin Server 或 RGPs 的远程遍历一起使用。它启动了一个承诺,以在将来执行当前 Traversal
上的函数。
最后,explain()
步骤也是一个终端步骤,并在自己的部分中进行了描述。
AddE 步骤
Reasoning 是将数据中的隐含信息变为显式的过程。图中的对象,即顶点和边,是显式的。图中的遍历是隐含的。换句话说,遍历通过定义暴露了意义。例如,考虑"co-developer"的概念。如果两个人共同参与了同一个项目,那么他们就是共同开发者。这个概念可以表示为遍历,并且因此"co-developers"的概念可以得出。而且,通过addE()
步骤(map/sideEffect)可以将隐含的东西变为显式的。
gremlin> g.V(1).as('a').out('created').in('created').where(neq('a')).
addE('co-developer').from('a').property('year',2009) //// (1)
==>e[0][1-co-developer->4]
==>e[13][1-co-developer->6]
gremlin> g.V(3,4,5).aggregate('x').has('name','josh').as('a').
select('x').unfold().hasLabel('software').addE('createdBy').to('a') //// (2)
==>e[14][3-createdBy->4]
==>e[15][5-createdBy->4]
gremlin> g.V().as('a').out('created').addE('createdBy').to('a').property('acl','public') //// (3)
==>e[16][3-createdBy->1]
==>e[17][5-createdBy->4]
==>e[18][3-createdBy->4]
==>e[19][3-createdBy->6]
gremlin> g.V(1).as('a').out('knows').
addE('livesNear').from('a').property('year',2009).
inV().inE('livesNear').values('year') //// (4)
==>2009
==>2009
gremlin> g.V().match(
__.as('a').out('knows').as('b'),
__.as('a').out('created').as('c'),
__.as('b').out('created').as('c')).
addE('friendlyCollaborator').from('a').to('b').
property(id,23).property('project',select('c').values('name')) //// (5)
==>e[23][1-friendlyCollaborator->4]
gremlin> g.E(23).valueMap()
==>[project:lop]
gremlin> vMarko = g.V().has('name','marko').next()
==>v[1]
gremlin> vPeter = g.V().has('name','peter').next()
==>v[6]
gremlin> g.V(vMarko).addE('knows').to(vPeter) //// (6)
==>e[22][1-knows->6]
gremlin> g.addE('knows').from(vMarko).to(vPeter) //// (7)
==>e[24][1-knows->6]
g.V(1).as('a').out('created').in('created').where(neq('a')).
addE('co-developer').from('a').property('year',2009) //// (1)
g.V(3,4,5).aggregate('x').has('name','josh').as('a').
select('x').unfold().hasLabel('software').addE('createdBy').to('a') //// (2)
g.V().as('a').out('created').addE('createdBy').to('a').property('acl','public') //// (3)
g.V(1).as('a').out('knows').
addE('livesNear').from('a').property('year',2009).
inV().inE('livesNear').values('year') //// (4)
g.V().match(
__.as('a').out('knows').as('b'),
__.as('a').out('created').as('c'),
__.as('b').out('created').as('c')).
addE('friendlyCollaborator').from('a').to('b').
property(id,23).property('project',select('c').values('name')) //// (5)
g.E(23).valueMap()
vMarko = g.V().has('name','marko').next()
vPeter = g.V().has('name','peter').next()
g.V(vMarko).addE('knows').to(vPeter) //// (6)
g.addE('knows').from(vMarko).to(vPeter) //7
- 在 Marko 和他的合作者之间添加一个带有 year 属性的 co-developer 边。
- 从 josh-顶点到 lop-和 ripple-顶点添加 createdBy 边。
- 对于所有创建的边,添加一个相反的 createdBy 边。
- 新创建的边是可遍历对象。
- 将两个任意绑定在遍历中通过
from()
→to()
进行连接,其中对于支持用户提供的 id 的图可以提供id
。 - 在 Marko 和 Peter 之间添加一个边,给定定向(分离的)顶点引用。
- 在 Marko 和 Peter 之间添加一个边,给定定向(分离的)顶点引用。
其他参考资料
AddV 步骤
addV()
-步骤用于向图中添加顶点(map/sideEffect)。对于每个传入的对象,都会创建一个顶点。此外,GraphTraversalSource
维护了一个 addV()
方法。
gremlin> g.addV('person').property('name','stephen')
==>v[0]
gremlin> g.V().values('name')
==>stephen
==>marko
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().outE('knows').addV().property('name','nothing')
==>v[13]
==>v[15]
gremlin> g.V().has('name','nothing')
==>v[13]
==>v[15]
gremlin> g.V().has('name','nothing').bothE()
g.addV('person').property('name','stephen')
g.V().values('name')
g.V().outE('knows').addV().property('name','nothing')
g.V().has('name','nothing')
g.V().has('name','nothing').bothE()
其他参考资料
addV()
, addV(String)
, addV(Traversal)
Aggregate 步骤
aggregate()
-步骤(sideEffect)用于将遍历的特定点上的所有对象聚合到一个 Collection
中。此步骤使用 Scope
来帮助确定聚合行为。对于 global
范围,这意味着该步骤将使用急切求值,即在继续之前不会有任何对象,直到所有之前的对象都已完全聚合。急切求值模型在需要将某个特定点的所有内容用于未来计算的情况下至关重要。默认情况下,当没有提供 Scope
的 aggregate()
重载调用时,默认为 global
。以下是一个示例。
gremlin> g.V(1).out('created') //// (1)
==>v[3]
gremlin> g.V(1).out('created').aggregate('x') //// (2)
==>v[3]
gremlin> g.V(1).out('created').aggregate(global, 'x') //// (3)
==>v[3]
gremlin> g.V(1).out('created').aggregate('x').in('created') //// (4)
==>v[1]
==>v[4]
==>v[6]
gremlin> g.V(1).out('created').aggregate('x').in('created').out('created') //// (5)
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.V(1).out('created').aggregate('x').in('created').out('created').
where(without('x')).values('name') //// (6)
==>ripple
g.V(1).out('created') //// (1)
g.V(1).out('created').aggregate('x') //// (2)
g.V(1).out('created').aggregate(global, 'x') //// (3)
g.V(1).out('created').aggregate('x').in('created') //// (4)
g.V(1).out('created').aggregate('x').in('created').out('created') //// (5)
g.V(1).out('created').aggregate('x').in('created').out('created').
where(without('x')).values('name') //6
- Marko 创建了什么?
- 聚合他的所有创建。
- 与上一行完全相同。
- Marko 的合作者是谁?
- Marko 的合作者创建了什么?
- Marko 的合作者创建的项目中,Marko 尚未创建的有哪些?
在推荐系统中,使用了上述模式:
"用户 A 喜欢什么?其他人喜欢什么?其他人喜欢的东西中,A 还没有喜欢过的有哪些?"
最后,aggregate()
-步骤可以通过 by()
-投影进行调节。
gremlin> g.V().out('knows').aggregate('x').cap('x')
==>[v[2],v[4]]
gremlin> g.V().out('knows').aggregate('x').by('name').cap('x')
==>[vadas,josh]
gremlin> g.V().out('knows').aggregate('x').by('age').cap('x') //// (1)
==>[27,32]
g.V().out('knows').aggregate('x').cap('x')
g.V().out('knows').aggregate('x').by('name').cap('x')
g.V().out('knows').aggregate('x').by('age').cap('x') //1
- 对于不是所有顶点都具有生产性(productive)的 “age” 属性,这些值不会包含在聚合中。
对于 local
范围,聚合将以惰性方式进行。
注意 | 在 3.4.3 之前,local 聚合(即惰性)求值由 store() -步骤处理。 |
---|---|
gremlin> g.V().aggregate(global, 'x').limit(1).cap('x')
==>[v[1],v[2],v[3],v[4],v[5],v[6]]
gremlin> g.V().aggregate(local, 'x').limit(1).cap('x')
==>[v[1]]
gremlin> g.withoutStrategies(EarlyLimitStrategy).V().aggregate(local,'x').limit(1).cap('x')
==>[v[1],v[2]]
g.V().aggregate(global, 'x').limit(1).cap('x')
g.V().aggregate(local, 'x').limit(1).cap('x')
g.withoutStrategies(EarlyLimitStrategy).V().aggregate(local,'x').limit(1).cap('x')
需要注意的是,在 3.3.5 中引入的 EarlyLimitStrategy
改变了 aggregate(local)
的行为。如果没有该策略(默认安装),即使间隔选择为 1 个对象,也会有两个结果在 aggregate()
副作用中,因为第二个对象在传递给 range()
过滤器之前(即 [0..1]
),它通过了 aggregate()
并且被存储。
gremlin> g.E().aggregate(local,'x').by('weight').cap('x')
==>[0.5,1.0,1.0,0.4,0.4,0.2]
g.E().aggregate(local,'x').by('weight').cap('x')
其他参考资料
aggregate(String)
, aggregate(Scope,String)
All 步骤
可以使用 all()
-步骤(filter)来过滤列表遍历器。列表中的每个项都会根据提供的谓词进行测试,如果所有项都通过,则遍历器将继续传递,否则将被过滤。空列表会被传递,但 null 或非可迭代遍历器将被过滤掉。
PYTHON | 在 Python 中,all 是一个保留字,因此在 Gremlin 中必须使用 all_() 来引用。 |
---|---|
gremlin> g.V().values('age').fold().all(gt(25)) //// (1)
==>[29,27,32,35]
g.V().values('age').fold().all(gt(25)) //1
- 仅当所有人的年龄都大于 25 岁时,才返回年龄列表。
其他参考资料
And 步骤
and()
-步骤确保所有提供的遍历器都产生结果(filter)。有关或语义,请参见 or()
。
PYTHON | 在 Python 中,and 是一个保留字,因此在 Gremlin 中必须使用 and_() 来引用。 |
---|---|
gremlin> g.V().and(
outE('knows'),
values('age').is(lt(30))).
values('name')
==>marko
g.V().and(
outE('knows'),
values('age').is(lt(30))).
values('name')
and()
-步骤可以接受任意数量的遍历器。只有当所有遍历器都至少产生一个输出时,原始遍历器才会传递到下一步。
也可以使用中缀表示法。
gremlin> g.V().where(outE('created').and().outE('knows')).values('name')
==>marko
g.V().where(outE('created').and().outE('knows')).values('name')
其他参考资料
Any 步骤
可以使用 any()
-步骤(filter)来过滤列表遍历器。列表中的所有项都将根据提供的谓词进行测试,如果有任何一项通过,则遍历器将继续传递,否则将被过滤。空列表、null 遍历器和非可迭代遍历器也将被过滤掉。
PYTHON | 在 Python 中,any 是一个保留字,因此在 Gremlin 中必须使用 any_() 来引用。 |
---|---|
gremlin> g.V().values('age').fold().any(gt(25)) //// (1)
==>[29,27,32,35]
g.V().values('age').fold().any(gt(25)) //1
- 如果有任何人的年龄大于 25 岁,则返回年龄列表。
其他参考资料
As 步骤
as()
-步骤不是一个真正的步骤,而是一个“步骤调节器”,类似于 by()
和 option()
。使用 as()
,可以为步骤提供一个标签,这个标签可以在后续的步骤和使用这些标签的数据结构中进行访问,例如 select()
、match()
和 path。
在 Groovy 中,as 是一个保留字,因此在匿名遍历中使用时,在 Gremlin 中必须用双下划线 __.as() 来引用。 |
|
---|---|
PYTHON | 在 Python 中,as 是一个保留字,因此在 Gremlin 中必须使用 as_() 来引用。 |
---|---|
gremlin> g.V().as('a').out('created').as('b').select('a','b') //// (1)
==>[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().as('a').out('created').as('b').select('a','b').by('name') //// (2)
==>[a:marko,b:lop]
==>[a:josh,b:ripple]
==>[a:josh,b:lop]
==>[a:peter,b:lop]
g.V().as('a').out('created').as('b').select('a','b') //// (1)
g.V().as('a').out('created').as('b').select('a','b').by('name') //2
- 从路径中选择标记为 “a” 和 “b” 的对象。
- 从路径中选择标记为 “a” 和 “b” 的对象,并对每个对象投影其名称值。
一个步骤可以有任意数量的与之关联的标签。这对于在将来的步骤中多次引用相同的步骤非常有用。
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]]
g.V().hasLabel('software').as('a','b','c').
select('a','b','c').
by('name').
by('lang').
by(__.in('created').values('name').fold())
其他参考资料
AsString 步骤
asString()
-步骤(map)将传入的遍历器的值转换为字符串。空值将保持不变。
gremlin> g.V().hasLabel('person').values('age').asString() //// (1)
==>29
==>27
==>32
==>35
gremlin> g.V().hasLabel('person').values('age').asString().concat(' years old') //// (2)
==>29 years old
==>27 years old
==>32 years old
==>35 years old
gremlin> g.V().hasLabel('person').values('age').fold().asString(local) //// (3)
==>[29,27,32,35]
g.V().hasLabel('person').values('age').asString() //// (1)
g.V().hasLabel('person').values('age').asString().concat(' years old') //// (2)
g.V().hasLabel('person').values('age').fold().asString(local) //3
- 将年龄作为字符串返回。
- 将年龄作为字符串返回,并使用 concat 生成短语。
- 使用
Scope.local
在传入的列表中操作单个字符串元素,这将返回一个列表。
其他参考资料
AsDate 步骤
asDate()
-步骤(map)将输入的字符串或数值转换为日期。
对于字符串输入,仅支持 ISO-8601 格式。对于数值,该值被视为自 “纪元”(1970 年 1 月 1 日 00:00:00 GMT)以来的毫秒数。日期输入保持不变。
如果传入的遍历器不是字符串、数值或日期,则会抛出 IllegalArgumentException
。
gremlin> g.inject(1690934400000).asDate() //// (1)
==>Tue Aug 01 17:00:00 PDT 2023
gremlin> g.inject("2023-08-02T00:00:00Z").asDate() //// (2)
==>Tue Aug 01 17:00:00 PDT 2023
gremlin> g.inject(datetime("2023-08-24T00:00:00Z")).asDate() //// (3)
==>Wed Aug 23 17:00:00 PDT 2023
g.inject(1690934400000).asDate() //// (1)
g.inject("2023-08-02T00:00:00Z").asDate() //// (2)
g.inject(datetime("2023-08-24T00:00:00Z")).asDate() //3
- 将数字转换为日期
- 将 ISO-8601 字符串转换为日期
- 不做任何修改地传递日期
Additional References
Barrier Step
barrier()
步骤(barrier)将延迟遍历流水线转换为批处理同步流水线。这个步骤在以下情况下非常有用:
- 当
barrier()
之前的所有操作都需要在继续执行barrier()
之后的步骤之前执行(即有序性)。 - 当“暂停”遍历可能导致对重复访问许多相同元素的遍历进行“批量优化”时(即优化)。
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]
g.V().sideEffect{println "first: ${it}"}.sideEffect{println "second: ${it}"}.iterate()
g.V().sideEffect{println "first: ${it}"}.barrier().sideEffect{println "second: ${it}"}.iterate()
“批量优化”的理论很简单。如果在顶点1有一百万个遍历器,则没有必要计算一百万个both()
操作。相反,将这一百万个遍历器表示为一个Traverser.bulk()
等于一百万的单个遍历器,并执行一次both()
操作。在更大的图中,批量优化示例更加明显。因此,下面的示例利用了Grateful Dead图。
gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> g = traversal().withEmbe