Chapter 6. Gremlin Query Languag
Gremlin是janusgraph的查询语言,用来获取/变更图数据。Gremlin是一个面向path的语言,能够简单快速的完成图遍历和变化操作。Gremlin是一个功能性语言,因此遍历操作被声明到类path的表达式表单。例如,from Hercules, traverse to his father and then his father’s father and return the grandfather’s name。
Gremlin是Apache TinkerPop的组件。它独立于janusgraph发展,并且被支持于大多数图数据库。建在janusgraph上的应用程序通过Gremlin查询语言,用户避免被发行商锁在一个图数据库上。
这章是Gremlin查询语言的简要概述。更多信息可以参考以下资源:
- Complete Gremlin Manual: Gremlin全部步骤的手册.
- Gremlin Console Tutorial: 学习如何用Gremlin Console高效图遍历,和交互式图分析.
- Practical Gremlin Book: 图数据库用户和Gremlin查询语言的起步教程。
- Gremlin Recipes: Gremlin的最佳实践集合和常见的图遍历语法。
- Gremlin Language Drivers: 用不同的语言连接到Gremlin server,如 Go, JavaScript, .NET/C#, PHP, Python, Ruby, Scala, and TypeScript.
- Gremlin Language Variants: 学习如何内嵌Gremlin到一个主机编程语言。
- Gremlin for SQL developers: 学习用SQL查询数据,Gremlin的经典语法。
6.1 图遍历的介绍
一次Gremlin查询是一串从左到右的操作/函数链。下面是一个上帝图里举过的祖父查询例子:
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
上面的查询可以分成以下步骤:
- g: 当前graph。
- V: graph中所有顶点。
- has('name', 'hercules'): 过滤出顶点有属性name = "hercules" (这里只有一个)。
- out('father'): 从Hercules遍历出边(outgoing edge)为father的顶点。
- out('father'): 从Hercules的father (Jupiter)遍历出边为father的顶点。
- name: 拿出顶点的name属性的value。
放到一起,这些步骤构成了类似遍历查询的路径。每一步都能分解且得到结果。当构造大型、复杂的查询链,这种图遍历/查询的风格是很有用的。
gremlin> g
==>graphtraversalsource[janusgraph[cql:127.0.0.1], standard]
gremlin> g.V().has('name', 'hercules')
==>v[24]
gremlin> g.V().has('name', 'hercules').out('father')
==>v[16]
gremlin> g.V().has('name', 'hercules').out('father').out('father')
==>v[20]
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
正常情况,通常看到返回的属性更好,而不是分配的id。
gremlin> g.V().has('name', 'hercules').values('name')
==>hercules
gremlin> g.V().has('name', 'hercules').out('father').values('name')
==>jupiter
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
下面例子显示了完整的father家族树。这个复杂些的例子说明了这个语言的灵活性和便利性。熟练掌握Gremlin使得用户使用junasgraph图结构如鱼得水。
gremlin> g.V().has('name', 'hercules').repeat(out('father')).emit().values('name')
==>jupiter
==>saturn
下面是更多图遍历例子:
gremlin> hercules = g.V().has('name', 'hercules').next()
==>v[1536]
gremlin> g.V(hercules).out('father', 'mother').label()
==>god
==>human
gremlin> g.V(hercules).out('battled').label()
==>monster
==>monster
==>monster
gremlin> g.V(hercules).out('battled').valueMap()
==>{name=nemean}
==>{name=hydra}
==>{name=cerberus}
每一步(.分开)是一个函数,操作前一步结果的对象,Gremlin有很多步骤Gremlin steps。简单的改变一个步骤或步骤的顺序,会执行不同的遍历语义,下面的例子是和Hercules打过同样的怪兽,但不是Hercules的所有人的name。(类似战友、盟友)
给定的上帝图只有一个和怪兽战斗的,所以这里增加一个战士到图里。
gremlin> theseus = graph.addVertex('human')
==>v[3328]
gremlin> theseus.property('name', 'theseus')
==>null
gremlin> cerberus = g.V().has('name', 'cerberus').next()
==>v[2816]
gremlin> battle = theseus.addEdge('battled', cerberus, 'time', 22)
==>e[7eo-2kg-iz9-268][3328-battled->2816]
gremlin> battle.values('time')
==>22
当增加顶点时,顶点的label是选填。增加边时label必须提供。顶点或边都可以设置kv对的属性。当一个属性key的种类设置为SET或LIST,这个key的属性每一个必须用addProperty增加到顶点。
gremlin> g.V(hercules).as('h').out('battled').in('battled').where(neq('h')).values('name')
==>theseus
这个例子有4个链的函数,out, in, except, 和values,每个函数的输入输出类型如下表,V是顶点,U是Object,V是U的子集。
- out: V -> V
- in: V -> V
- except: U -> U
- values: V -> U
当把函数连起来,输入类型必须等于输出类型,U匹配所有。
注意
这样的Gremlin说的是在Gremlin
Console用的Gremlin-Groovy。Gremlin也有其他的语言支持drivers,variants。
6.2 执行遍历
Gremlin Console的一个方便的特性是它自动生成所有结果,从终端执行查询。它在REPL环境工作很好,返回String类型的结果。当你过渡到Gremlin应用程序,理解如何明确的执行遍历很重要,因为不会自动的执行。下面是常用的方法来执行遍历:
- iterate() - 不期待结果或可以被忽略。
- next() - 拿到一个结果,确保用hasNext()检查。
- next(int n) - 拿到n个结果,确保用hasNext()检查。
- toList() - 获得所有结果作为list,如果没有则返回空的list。
下面是一端java代码说明这个概念:
Traversal t = g.V().has("name", "pluto"); // Define a traversal
// Note the traversal is not executed/iterated yet
Vertex pluto = null;
if (t.hasNext()) { // Check if results are available
pluto = g.V().has("name", "pluto").next(); // Get one result
g.V(pluto).drop().iterate(); // Execute a traversal to drop pluto from graph
}
// Note the traversal can be cloned for reuse
Traversal tt = t.asAdmin().clone();
if (tt.hasNext()) {
System.err.println("pluto was not dropped!");
}
List<Vertex> gods = g.V().hasLabel("god").toList(); // Find all the gods