Germlin
简介
Gremlin
是Apache TinkerPop的图形查询语言,是一种函数式数据流语言,可以使得用户使用简洁的方式表述复杂的属性图(property graph)的遍历或查询。每个Gremlin遍历由一系列步骤(可能存在嵌套)组成,每一步都在数据流(data stream)上执行一个原子操作。
Gremlin 语言包括三个基本的操作:
- map-step:对数据流中的对象进行转换;
- filter-step:对数据流中的对象就行过滤;
- sideEffect-step:对数据流进行计算统计;
Tinkerpop3 模型核心概念
- Graph: 维护节点&边的集合,提供访问底层数据库功能,如事务功能
- Element: 维护属性集合,和一个字符串label,表明这个element种类
- Vertex: 继承自Element,维护了一组入度,出度的边集合
- Edge: 继承自Element,维护一组入度,出度vertex节点集合.
- Property: kv键值对
- VertexProperty: 节点的属性,有一组健值对kv,还有额外的properties 集合。同时也继承自element,必须有自己的id, label.
- Cardinality: 「single, list, set」 节点属性对应的value是单值,还是列表,或者set。
Gremlin 查询示例
下面先介绍下本文中所用到的图的 Schema
(Lable):
顶点:
- person
- software
边:person
- uses(person -> software)
- develops(person -> software)
- knows(person -> person)
下面结合上面的 Schema
给出一些常用的图操作。完整的语法见下一部分。
查询点
|
|
查询边
|
|
查询属性
|
|
新增点
|
|
新增边
|
|
删除点
|
|
删除边
|
|
Gremlin 语法特性
基础
V()
:查询顶点,一般作为图查询的第1步,后面可以续接的语句种类繁多。例,g.V(),g.V('v_id')
,查询所有点和特定点;E()
:查询边,一般作为图查询的第1步,后面可以续接的语句种类繁多;id()
:获取顶点、边的id。例:g.V().id()
,查询所有顶点的id;label()
:获取顶点、边的 label。例:g.V().label()
,可查询所有顶点的label。key() / values()
:获取属性的key/value的值。properties()
:获取顶点、边的属性;可以和 key()、value()搭配使用,以获取属性的名称或值。例:g.V().properties('name')
,查询所有顶点的 name 属性;valueMap()
:获取顶点、边的属性,以Map的形式体现,和properties()
比较像;values()
:获取顶点、边的属性值。例,g.V().values()
等于g.V().properties().value()
遍历
顶点为基准:
out(label)
:根据指定的 Edge Label 来访问顶点的 OUT 方向邻接点(可以是零个 Edge Label,代表所有类型边;也可以一个或多个 Edge Label,代表任意给定 Edge Label 的边,下同);in(label)
:根据指定的 Edge Label 来访问顶点的 IN 方向邻接点;both(label)
:根据指定的 Edge Label 来访问顶点的双向邻接点;outE(label)
: 根据指定的 Edge Label 来访问顶点的 OUT 方向邻接边;inE(label)
:根据指定的 Edge Label 来访问顶点的 IN 方向邻接边;bothE(label)
:根据指定的 Edge Label 来访问顶点的双向邻接边;
边为基准的:
outV()
:访问边的出顶点,出顶点是指边的起始顶点;inV()
:访问边的入顶点,入顶点是指边的目标顶点,也就是箭头指向的顶点;bothV()
:访问边的双向顶点;otherV()
:访问边的伙伴顶点,即相对于基准顶点而言的另一端的顶点;
过滤
在众多Gremlin的语句中,有一大类是filter类型,顾名思义,就是对输入的对象进行条件判断,只有满足过滤条件的对象才可以通过filter进入下一步。
has
has语句是filter类型语句的代表,能够以顶点和边的属性作为过滤条件,决定哪些对象可以通过。常用的有下面几种:
has(key,value)
: 通过属性的名字和值来过滤顶点或边;has(label, key, value)
: 通过label和属性的名字和值过滤顶点和边;has(key,predicate)
: 通过对指定属性用条件过滤顶点和边,例:g.V().has('age', gt(20))
,可得到年龄大于20的顶点;hasLabel(labels…)
: 通过 label 来过滤顶点或边,满足label列表中一个即可通过;hasId(ids…)
: 通过 id 来过滤顶点或者边,满足id列表中的一个即可通过;hasKey(keys…)
: 通过 properties 中的若干 key 过滤顶点或边;hasValue(values…)
: 通过 properties 中的若干 value 过滤顶点或边;has(key)
: properties 中存在 key 这个属性则通过,等价于hasKey(key);hasNot(key)
: 和 has(key) 相反;
例:
|
|
路径
在使用Gremlin对图进行分析时,关注点有时并不仅仅在最终达到顶点、边或者属性,通过什么样的路径到达最终的顶点、边和属性同样重要。此时可以借助path()
来获取经过的路径信息。
path()
返回当前遍历过的所有路径。有时需要对路径进行过滤,只选择没有环路的路径或者选择包含环路的路径,Gremlin针对这种需求提供了两种过滤路径的step:simplePath()
和cyclicPath()
。
path()
:获取当前遍历过的所有路径;simplePath()
:过滤掉路径中含有环路的对象,只保留路径中不含有环路的对象;cyclicPath()
:过滤掉路径中不含有环路的对象,只保留路径中含有环路的对象。
例1:寻找4跳以内从 andy 到 jack 的最短路径。
|
|
例2:“Titan” 顶点到与其有两层关系的顶点的不含环路的路径(只包含顶点)
|
|
迭代
repeat()
:指定要重复执行的语句;times()
: 指定要重复执行的次数,如执行3次;
3.until()
:指定循环终止的条件,如一直找到某个名字的朋友为止;emit()
:指定循环语句的执行过程中收集数据的条件,每一步的结果只要符合条件则被收集,不指定条件时收集所有结果;
5.loops()
:当前循环的次数,可用于控制最大循环次数等,如最多执行3次。
repeat()
和 until()
的位置不同,决定了不同的循环效果:
repeat()
+until()
:等同 do-while;until()
+repeat()
:等同 while-do。
repeat()
和 emit()
的位置不同,决定了不同的循环效果:
repeat()
+emit()
:先执行后收集;emit()
+repeat()
:表示先收集再执行。
注意:
emit()与times()搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。
emit()与until()搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。
例1:根据出边进行遍历,直到抵达叶子节点(无出边的顶点),输出其路径顶点名:
|
|
例2:查询顶点’1’的3度 OUT 可达点路径
|
|
转换
- map():可以接受一个遍历器 Step 或 Lamda 表达式,将遍历器中的元素映射(转换)成另一个类型的某个对象(一对一),以便进行下一步处理
- flatMap():可以接受一个遍历器 Step 或 Lamda 表达式,将遍历器中的元素映射(转换)成另一个类型的某个对象流或迭代器(一对多)。
稍微了解函数式编程的同学都比较熟悉这两个操作了,在这里不再赘述。
例:
|
|
排序
order()
order().by()
排序比较简单,直接看例子吧。
|
|
逻辑
is()
:可以接受一个对象(能判断相等)或一个判断语句(如:P.gt()、P.lt()、P.inside()等),当接受的是对象时,原遍历器中的元素必须与对象相等才会保留;当接受的是判断语句时,原遍历器中的元素满足判断才会保留,其实接受一个对象相当于P.eq();and()
:可以接受任意数量的遍历器(traversal),原遍历器中的元素,只有在每个新遍历器中都能生成至少一个输出的情况下才会保留,相当于过滤器组合的与条件;or()
:可以接受任意数量的遍历器(traversal),原遍历器中的元素,只要在全部新遍历器中能生成至少一个输出的情况下就会保留,相当于过滤器组合的或条件;not()
:仅能接受一个遍历器(traversal),原遍历器中的元素,在新遍历器中能生成输出时会被移除,不能生成输出时则会保留,相当于过滤器的非条件。
例1:
|
|
例2:获取所有最多只有一条“created”边并且年龄不等于28的“person”顶点
|
|
统计
- sum():将 traversal 中的所有的数字求和;
- max():对 traversal 中的所有的数字求最大值;
- min():对 traversal 中的所有的数字求最小值;
- mean():将 traversal 中的所有的数字求均值;
- count():统计 traversal 中 item 总数。
例:
|
|
分支
choose()
:分支选择, 常用使用场景为:choose(predicate, true-traversal, false-traversal)
,根据 predicate 判断,当前对象满足时,继续 true-traversal,否则继续 false-traversal;optional()
:类似于 by() 无实际意义,搭配 choose() 使用;
例1:if-else
|
|
例2:if-elseif-else
|
|