摘要:Gremlin 是图数据库查问应用最广泛的根底查询语言。Gremlin 的图灵齐备性,使其可能编写非常复杂的查问语句。对于简单的问题,咱们该如何编写一个简单的查问?以及咱们该如何了解已有的简单查问?本文带你逐渐抽丝剥茧,实现简单查问的调试。
本文分享自华为云社区《简单 Gremlin 查问的调试办法》,原文作者:Uncle_Tom。
1. Gremlin 简介
Gremlin 是 Apache TinkerPop 框架下的图遍历语言。Gremlin 是一种函数式数据流语言,能够使得用户应用简洁的形式表述简单的属性图(property graph)的遍历或查问。每个 Gremlin 遍历由一系列步骤(能够存在嵌套)组成,每一步都在数据流(data stream)上执行一个原子操作。
Gremlin 是一种用于形容属性图中行走的语言。图形遍历分两个步骤进行。
1.1. 遍历源(TraversalSource)
开始节点抉择(Start node selection)。所有遍历都从数据库中抉择一组节点开始,这些节点充当图中行走的终点。Gremlin 中的遍历是从 TraversalSource 开始的。GraphTraversalSource 提供了两种遍历办法。
- GraphTraversalSource.V(Object … ids):从图形的顶点开始遍历(如果未提供 id,则为所有顶点)。
- GraphTraversalSource.E(Object … ids):从图形的边缘开始遍历(如果未提供 id,则为所有边)。
1.2. 图遍历(GraphTraversal)
走图(Walking the graph)。从上一步中抉择的节点开始,遍历会沿着图形的边前进,以依据节点和边的属性和类型达到相邻的节点。遍历的最终目标是确定遍历能够达到的所有节点。您能够将图遍历视为子图形容,必须执行该子图形容能力返回节点。
V()和 E()的返回类型是 GraphTraversal。GraphTraversal 保护许多返回 GraphTraversal 的办法。GraphTraversal 反对性能组合。GraphTraversal 的每种办法都称为一个步骤 (step),并且每个步骤都以五种惯例形式之一调制(modulates) 前一步骤的后果。
- map:将传入的遍历对象转换为另一个对象(S→E)。
- flatMap:将传入的遍历对象转换为其余对象的迭代器(S\subseteq E^*S⊆E∗)。
- filter:容许或禁止遍历器进行下一步(S→S∪∅)。
- sideEffect:容许遍历器放弃不变,但在过程中产生一些计算上的副作用(S↬S)。
- branch:拆分遍历器并将其发送到遍历中的任意地位(S→{S1→E^,…,S_n→E^S1→E∗,…,Sn→E∗}→E*)。
- GraphTraversal 中简直每个步骤都从 MapStep,FlatMapStep,FilterStep,SideEffectStep 或 BranchStep 扩大失去。
- 举例:找到 makro 意识的人
gremlin> g.V().has('name','marko').out('knows').values('name')
==>vadas
==>josh
1.3. Gremlin 是图灵齐备的(Turing Complete)
这也就时说任何简单的问题,都能够用 Gremlin 形容。
上面就调试和编写简单的 gremlin 查问,给出领导思路和方法论。
2. 简单 Gremlin 查问的调试
Gremlin 的查问都是由简略的查问组合成简单的查问。所以对于简单 Gremlin 查问能够分为以下三个步骤,并逐渐迭代实现所有语句的验证,此办法同样实用编写简单的 Gremlin 查问。
2.1. 迭代调试步骤
- 拆分剖析步骤,划大为小,逐渐求证;
- 输入分步骤的后果,明确步骤的具体输入内容;
- 对输入后果进行推导和测验。根据后果扩充或放大剖析步骤,回到步骤 1 持续,直到分明所有后果。
注: 此办法参照 Stephen Mallette gremlins-anatomy 的剖析逻辑和用例。
2.2. 用例
2.2.1. 图构造
gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin>g.addV().property('name','alice').as('a').
addV().property('name','bobby').as('b').
addV().property('name','cindy').as('c').
addV().property('name','david').as('d').
addV().property('name','eliza').as('e').
addE('rates').from('a').to('b').property('tag','ruby').property('value',9).
addE('rates').from('b').to('c').property('tag','ruby').property('value',8).
addE('rates').from('c').to('d').property('tag','ruby').property('value',7).
addE('rates').from('d').to('e').property('tag','ruby').property('value',6).
addE('rates').from('e').to('a').property('tag','java').property('value',10).
iterate()
gremlin> graph
==>tinkergraph[vertices:5 edges:5]
2.2.2. 查问语句
gremlin>g.V().has('name','alice').as('v').
repeat(outE().as('e').inV().as('v')).
until(has('name','alice')).
store('a').
by('name').
store('a').
by(select(all, 'v').unfold().values('name').fold()).
store('a').
by(select(all, 'e').unfold().
store('x').
by(union(values('value'), select('x').count(local)).fold()).
cap('x').
store('a').by(unfold().limit(local, 1).fold()).unfold().
sack(assign).by(constant(1d)).
sack(div).by(union(constant(1d),tail(local, 1)).sum()).
sack(mult).by(limit(local, 1)).
sack().sum()).
cap('a')
==>[alice,[alice,bobby,cindy,david,eliza,alice],[9,8,7,6,10],18.833333333333332]
好长,好简单!头大!
看我如何抽丝剥茧,一步步验证后果。
2.3. 调试过程
2.3.1 拆分查问
按执行步骤,拆分成小的查问,如下图:
- 执行第一局部步骤
gremlin> g.V().has('name','alice').as('v').
......1> repeat(outE().as('e').inV().as('v')).
......2> until(has('name','alice'))
==>v[0]
2.3.2 廓清后果
这里通过 valueMap()输入节点信息。
gremlin> g.V().has('name','alice').as('v').
......1> repeat(outE().as('e').inV().as('v')).
......2> until(has('name','alice')).valueMap()
==>[name:[alice]]
2.3.3 验证假如
依据执行语句的语义推导查问过程,如下:
应用 path(), 验证推导过程
g.V().has('name','alice').as('v').
......1> repeat(outE().as('e').inV().as('v')).
......2> until(has('name','alice')).path().next()
==>v[0]
==>e[10][0-rates->2]
==>v[2]
==>e[11][2-rates->4]
==>v[4]
==>e[12][4-rates->6]
==>v[6]
==>e[13][6-rates->8]
==>v[8]
==>e[14][8-rates->0]
==>v[0]
- 输入后果与推导后果统一,扩充查问语句, 回到步骤 1;
- 如不统一或不了解后果, 放大步骤范畴, 能够采纳此步骤的上一层查问步骤, 回到步骤 1;
- 如此循环直到齐全了解整个查问。
gremlin> g.V().has('name','alice').as('v').
......1> repeat(outE().as('e').inV().as('v')).
......2> until(has('name','alice')).
......3> store('a').by('name')
==>v[0]
大家能够本人去细细的剥下笋,此处略去 3000 字。
3. 总结
- 在剖析的过程,采纳划分查问语句的办法,分步了解,采纳漏斗式的办法,逐渐扩充对语句的了解;
- 对每步的查问后果,能够采纳利用 valueMap(), path(), select(), as(), cap() 等函数输入和验证后果;
- 对于不分明后果的步骤或与期望值不统一,放大查问步骤, 能够采纳输入步骤的前一步骤作为输入点,进行输入和验证;
- 对于上一层数据的后果明确的状况下,能够采纳 inject()形式注入下层输入,持续后续的输入和验证;
- 要留神步骤最初的函数,对整个输入后果的影响。
4. 参考
- Introduction to Gremlin
- Gremlin’s Anatomy
- TinkerPop Documentation
- Stephen Mallette gremlins-anatomy
- Practical Gremlin – Why Graph?
点击关注,第一工夫理解华为云陈腐技术~