摘要:Gremlin是图数据库查询使用最普遍的基础查询语言。Gremlin的图灵完备性,使其能够编写非常复杂的查询语句。对于复杂的问题,我们该如何编写一个复杂的查询?以及我们该如何理解已有的复杂查询?本文带你逐步抽丝剥茧,完成复杂查询的调试。

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)前一步骤的结果。

  1. map:将传入的遍历对象转换为另一个对象(S→E)。
  2. flatMap:将传入的遍历对象转换为其他对象的迭代器(S\subseteq E^*SE∗)。
  3. filter:允许或禁止遍历器进行下一步(S→S∪∅)。
  4. sideEffect:允许遍历器保持不变,但在过程中产生一些计算上的副作用(S↬S)。
  5. 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. 拆分分析步骤,划大为小,逐步求证;
  2. 输出分步骤的结果,明确步骤的具体输出内容;
  3. 对输出结果进行推导和检验。依据结果扩大或缩小分析步骤,回到步骤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?

本文分享自华为云社区《复杂Gremlin查询的调试方法》,原文作者:Uncle_Tom。

点击关注,第一时间了解华为云新鲜技术~

一文抽丝剥茧带你掌握复杂Gremlin查询的调试方法的更多相关文章

  1. Android绘图机制(四)——使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美

    Android绘图机制(四)--使用HelloCharts开源框架搭建一系列炫酷图表,柱形图,折线图,饼状图和动画特效,抽丝剥茧带你认识图表之美 这里为什么不继续把自定义View写下去呢,因为最近项目 ...

  2. win7自带wifi win7无线网络共享设置图文方法

    win7自带wifi win7无线网络共享设置图文方法 点评:开启windows 7的隐藏功能:虚拟WiFi和SoftAP(即虚拟无线AP),就可以让电脑变成无线路由器,实现共享上网,节省网费和路由器 ...

  3. (转)MySQL数据表中带LIKE的字符匹配查询

    MySQL数据表中带LIKE的字符匹配查询 2014年07月15日09:56    百科369 MySQL数据表中带LIKE的字符匹配查询 LIKE关键字可以匹配字符串是否相等. 如果字段的值与指定的 ...

  4. mysql进阶(五)数据表中带OR的多条件查询

    MySQL数据表中带OR的多条件查询 OR关键字可以联合多个条件进行查询.使用OR关键字时: 条件 1) 只要符合这几个查询条件的其中一个条件,这样的记录就会被查询出来. 2) 如果不符合这些查询条件 ...

  5. mysql 数据操作 多表查询 子查询 带IN关键字的子查询

    1 带IN关键字的子查询 #查询平均年龄在25岁以上的部门名关键点部门名 以查询员工表的dep_id的结果 当作另外一条sql语句查询条件使用 in (sql语句) mysql ; +-------- ...

  6. mysql 数据操作 多表查询 子查询 带比较运算符的子查询

    带比较运算符的子查询 #比较运算符:=.!=.>.>=.<.<=.<> #查询大于所有人平均年龄的员工名与年龄 思路 先拿到所有人的平均年龄然后 再用另外一条sql ...

  7. mysql 数据操作 多表查询 子查询 带EXISTS关键字的子查询

    带EXISTS关键字的子查询 EXISTS关字键字表示存在. EXISTS 判断某个sql语句的有没有查到结果 有就返回真  true 否则返回假 False 如果条件成立 返回另外一条sql语句的返 ...

  8. VS2013中带命令行参数的调试方法---C++

    今天先记录一下(也是传说中大神喜欢装逼的comment line)c++中向主函数int main(int argc,char** argv )传递4中方法,欢迎添加新方法, 然后可以参考别人写的很好 ...

  9. MFC单文档带窗体创建

    我用的vs05.先随便起个名字qwerty. 确定以后在左边最下面有一个生成的类,点击生成的类,把基类改成CFormView 最后点击完成就创建好了. 单文档的窗口不是后来创建后插入的,是在创建后就自 ...

随机推荐

  1. wxWidgets源码分析(7) - 窗口尺寸

    目录 窗口尺寸 概述 窗口Size消息的处理 用户调整Size消息的处理 调整窗口大小 程序调整窗口大小 wxScrolledWindow设置窗口大小 获取TextCtrl控件最合适大小 窗口尺寸 概 ...

  2. Ext.Net一般处理程序上传文件

    引言 最近公司项目全部转向前端化,故所有aspx页面业务逻辑尽可能的转到用户控件前台页面完成.以方便每次发布项目时只是替换前端页面不会影响客户体验. 既然转到前台逻辑,那么必须走后台的业务也就单独封装 ...

  3. postman接口测试之设置全局变量和设置环境变量和全局变量

    一.概念 1.环境变量 就是接口的域名或IP地址. 2.全局变量 就是一个作用域为整个postman的变量. 二.使用场景 1.环境变量 在测试的过程中,经常会频繁切换环境,本地环境验证.发布到测试环 ...

  4. Centos mini系统下的Hadoop集群搭建

    1.事前了解 1.1 Hadoop 百度百科:https://baike.baidu.com/item/Hadoop/3526507?fr=aladdin Hadoop是一个由Apache基金会所开发 ...

  5. 微服务网关Zuul过滤器Filter

    Zuul本质 Zuul是一个网关,关于网关的介绍参考:亿级流量架构之网关设计思路.常见网关对比, 可知Zuul是一个业务网关, 而深入了解Zuul, 基本就是一系列过滤器的集合: Zuul的过滤器 下 ...

  6. Apache配置 8.配置防盗链

    (1)介绍 防盗链,通俗讲,就是不让别人盗用你网站上的资源.这个资源,通常指的是图片.视频.歌曲.文档等. (2)配置 配置防盗链先编辑主机配置文件: #vim /usr/local/apache2. ...

  7. 微服务分布式事务之LCN、TCC

    在亿级流量架构之分布式事务解决方案对比中, 已经简单阐明了从本机事务到分布式事务的演变过程, 文章的最后简单说明了TCC事务, 这儿将会深入了解TCC事务是原理, 以及理论支持, 最后会用Demo举例 ...

  8. 2.pandas常用读取

    一.文本读写 名称 接收 代表(含义) 默认 filepath string 文件路径 无 sep string 分割符 ',' header Int/sequence 某行做列名 infer自动寻找 ...

  9. java实现一个点餐系统

    转载于blog.csdn.net/weixin_44219955 项目大体框架 菜品类(菜品id,菜品名,菜品类型,上架时间,单价,月销售,总数量) 管理员类(管理员id,账号,密码) 客户类(客户i ...

  10. python课程设计--学生管理系统

    系统要求 1.添加学生 2.删除学生 3.修改学生信息 4.查询学生 5.查看所有学生信息 6.学生信息数据的存储与读取 源码:student.py #coding:utf-8 2 #定义学员类 3 ...