第22期 Gremlin Steps:
hasNext()、next()、tryNext()、toList()、toSet()、toBulkSet()、fill()、iterate()
本系列文章的Gremlin示例均在HugeGraph图数据库上执行,环境搭建可参考准备Gremlin执行环境,本文示例均以其中的“TinkerPop关系图”为初始数据。

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