第22期 Gremlin Steps:
hasNext()
、next()
、tryNext()
、toList()
、toSet()
、toBulkSet()
、fill()
、iterate()
本系列文章的Gremlin示例均在HugeGraph图数据库上执行,环境搭建可参考准备Gremlin执行环境,本文示例均以其中的“TinkerPop关系图”为初始数据。
说明
Gremlin 中有一类特殊的操作,它能够终止遍历器的“遍历”行为,使其执行并返回结果。在这里要强调的一点:原生的 Gremlin 语句通常都是用遍历器连接起来的,但其实这些连接的过程并不会执行 Gremlin 语句,只有走到了terminal
Step 时才会执行。这个模式类似于 Spark 中对RDD
的map
和action
操作。
-
hasNext
: 判断遍历器是否含有元素(结果),返回布尔值; -
next
: 不传参数时获取遍历器的下一个元素,也可以传入一个整数 n,则获取后面 n 个元素; -
tryNext
:hasNext
和next
的结合版,返回一个Optional
对象,如果有结果还需要调用get()
方法才能拿到; -
toList
: 将所有的元素放到一个List
中返回; -
toSet
: 将所有的元素放到一个Set
中返回,会去除重复元素; -
toBulkSet
: 将所有的元素放到一个能排序的List
中返回,重复元素也会保留; -
fill
: 传入一个集合对象,将所有的元素放入该集合并返回,其实toList
、toSet
和toBulkSet
就是通过fill
Step实现的; -
iterate
: 这个 Step 在终止操作里面有点特殊,它并不完全符合终止操作的定义。它会在内部迭代完整个遍历器但是不返回结果。
那肯定有细心的同学要问了,前面我们介绍了那么多的 Step 很多都没有加terminal
Step 啊,为什么也能返回结果呢?其实这是 Tinkerpop 的 Gremlin 解析引擎对遍历器对象调用了一个IteratorUtils.asList()
方法,又调用了它内部的fill()
方法(注意:不是上面讲到的fill()
Step)。
实例讲解
下面通过实例来深入理解每一个操作。
-
Step
hasNext()
示例1:
// 判断顶点“linary”是否包含“created”出顶点 g.V('linary').out('created').hasNext()
示例2:
// 判断顶点“linary”是否包含“knows”出顶点 g.V('linary').out('knows').hasNext()
-
Step
next()
示例1:
// 获取顶点“javeme”的“knows”出顶点集合的下一个(第1个) g.V('javeme').out('knows').next()
g.V('javeme').out('knows')
返回的是一个遍历器(迭代器),每次执行这句话实际上都是获取的迭代器的第一个元素,那如果想获取第二个元素该怎么写呢?很简单,执行两次next()
即可,但是这里的前提条件是遍历器中确实存在多个元素。示例2:
// 获取顶点“javeme”的“knows”出顶点集合的下一个(第2个) it = g.V('javeme').out('knows') it.next() it.next()
示例3:
// 获取顶点“javeme”的“knows”出顶点集合的前两个 g.V('javeme').out('knows').next(2)
next()
与next(n)
使用中有一点小小的区别,就是当没有元素或者没有足够多的元素时,执行next()
会报错,但是执行next(n)
则是返回一个空集合(List)。 -
Step
tryNext()
示例1:
// 试图获取顶点“javeme”的“created”出顶点集合中的下一个 g.V('javeme').out('created').tryNext()
这里细心的读者会发现结果与前面概述中说的有些不同。概述中说的是返回一个
Optional
对象,要获取Optional
对象里的值是需要调用它的get()
方法的,怎么这里直接就把值给返回了呢?大家先别着急,我们再看一个例子。示例2:
// 试图获取顶点“javeme”的“created”入顶点集合中的下一个 g.V('javeme').in('created').tryNext()
这里更加令人费解,没有“created”入顶点时竟然直接报错了,其实这是
HugeGraph
的实现中关于Optional的序列化所致。HugeGraph
序列化Optional
对象时会判断该对象内的值是否存在,如果存在则取出来序列化该值,否则填入一个null
。详细代码见HugeGraphSONModule.java中关于OptionalSerializer
的实现。本文的重点在于学习
Gremlin
语法本身,下面给出上述两个示例的预期结果:Optional[v[3:HugeGraph]] Optional.empty
-
Step
toList()
示例1:
// 获取所有“person”顶点的“created”出顶点集合,放入List中,允许包含重复结果 g.V().hasLabel('person').out('created').toList()
示例2:
// 获取所有“person”顶点的“created”入顶点集合,放入List中,允许包含重复结果 g.V().hasLabel('person').in('created').toList()
结果与
next(n)
有些类似。 -
Step
toSet()
示例1:
// 获取所有“person”顶点的“created”出顶点集合,放入Set中,不允许包含重复结果 g.V().hasLabel('person').out('created').toSet()
相比于
toList
,toSet
去除了重复元素。示例2:
// 获取所有“person”顶点的“created”入顶点集合,放入Set中,不允许包含重复结果 g.V().hasLabel('person').in('created').toSet()
-
Step
toBulkSet()
示例1:
// 获取所有“person”顶点的“created”出顶点集合,放入BulkSet中,允许包含重复结果,排序 g.V().hasLabel('person').out('created').toBulkSet()
所谓的
BulkSet
虽然名字上带有"Set",但还是更像一个List,对比toList
的结果,它实际上是把所有元素排了个序。 -
Step
fill()
示例1:
// 创建一个List,获取所有“person”顶点的“created”出顶点,并放入该List中 results = [] g.V().hasLabel('person').out('created').fill(results) results
-
Step
iterate()
示例1:
// 迭代所有“person”顶点 it = g.V().hasLabel('person').iterate() it.hasNext()
调用了
iterate()
后遍历器内部的元素就已经全部迭代过了,所以再调用hasNext()
返回false。