Gremlin学习笔记

前言

本文基于HugeGraph提供的HugeGraph-Studio编写示例代码,下图是示例关系图,示例数据在文末
在这里插入图片描述

基本概念

Gremlin是Apache TinkerPop框架下规范的图语言,相当于SQL之于关系型数据库

  • 节点Vertex:一般指实体,如:人员、车辆、列车、航班等
  • 边Edge:一般指关系,如:乘车、转账、同行、同住等
  • 属性Property:节点和边都可以包含属性,如:人的姓名、人的性别等

取值操作

包括:V()、E()、id()、lable()、properties()、valueMap()、values()

V()

查询节点,一般作为图查询的第1步

// 获取图中所有节点
g.V()
// 根据ID查询节点,节点的ID由图数据库自己生成
g.V('1:1001','1:1002')

在这里插入图片描述

E()

查询边,一般作为图查询的第1步

// 获取图中所有的边
g.E()
// 根据ID查询边
g.E('S1:1004>1>>S2:G6246!2020-08-20')

在这里插入图片描述

id()

获取节点或边的id

g.V().id()

在这里插入图片描述

label()

获取节点或边的label

g.V().label()

在这里插入图片描述

properties()

获取节点或边的属性

// 获取全部节点的全部属性
g.V().properties()
// 获取全部边的全部属性
g.E().properties()
// 获取全部节点的 ry_xm 属性
g.V().properties('ry_xm')
// 获取全部边的 cc_zw 属性
g.E().properties('cc_zw')

在这里插入图片描述

valueMap()

获取节点或边的属性,跟properties()返回的结构不同,properties()将所有属性扁平化到一个大列表中,valueMap()保持一个节点或一条边的属性作为一组,每一组由若干属性的键值对组成

// 查询所有节点的属性
g.V().valueMap()

在这里插入图片描述

values()

获取节点或边的属性值

// 等同于 g.V().properties().value()
g.V().values()
g.V().values('ry_xm')

在这里插入图片描述

遍历操作

包括:out()、in()、both()、outE()、inE()、outV()、inV()、bothV()、otherV()

  • out(EdgeLabel): 根据EdgeLabel来访问节点的OUT方向(箭头指向方向)邻接点
    • EdgeLabel为空,表示所有连线;
    • EdgeLabel多值,表示多个指定的连线;
  • in(EdgeLabel): 根据EdgeLabel来访问节点的IN方向邻接点
  • both(EdgeLabel): 根据EdgeLabel来访问节点的双向邻接点
  • outE(EdgeLabel): 根据EdgeLabel来访问节点的OUT方向邻接边
    -inE(EdgeLabel): 根据EdgeLabel来访问节点的IN方向邻接边
  • bothE(EdgeLabel): 根据EdgeLabel来访问节点的双向邻接边
  • outV(): 访问边的OUT节点(注意:这里是以边为基准,上述均以节点为基准),OUT节点是指边的起始节点
  • inV(): 访问边的入节点,入节点是指边的目标节点,也就是箭头指向的节点
  • bothV(): 访问边的双向节点
  • otherV(): 访问边的伙伴节点,即相对于基准节点而言的另一端的节点

out()

// 获取人员1001的所有OUT邻接点
g.V('1:1001').out()
// 获取人员1001的乘车(cc)类型连线的OUT邻接点
g.V('1:1001').out('cc')

在这里插入图片描述

in()

// 获取G6246次202000819发出的列车,所有IN邻接点
g.V('2:G6246!2020-08-19').in()
// 获取G6246次202000819发出的列车,所有乘车类型的IN邻接点
g.V('2:G6246!2020-08-19').in('cc')

在这里插入图片描述

both()

// 获取人员1001的所有邻接点
g.V('1:1001').both()
g.V('1:1001').both('cc')

在这里插入图片描述

outE()

// 获取人员1001的所有OUT邻接边
g.V('1:1001').outE()
g.V('1:1001').outE('cc')

在这里插入图片描述

inE()

// 获取列车G6246!2020-08-19的所有IN邻接边
g.V('2:G6246!2020-08-19').inE()
g.V('2:G6246!2020-08-19').inE('cc')

bothE()

// 获取人员1001的所有邻接边
g.V('1:1001').bothE()
g.V('1:1001').bothE('cc')

在这里插入图片描述

outV()

// 获取边 S1:1004>1>>S2:G6246!2020-08-20 的所有OUT邻接点,即箭头的源端
g.E('S1:1004>1>>S2:G6246!2020-08-20').outV()
g.V('2:G6246!2020-08-19').inE().outV()

在这里插入图片描述

inV()

// 获取边 S1:1004>1>>S2:G6246!2020-08-20 的所有IN邻接点,即箭头的终端
g.E('S1:1004>1>>S2:G6246!2020-08-20').inV()
g.V('1:1001').outE().inV()

在这里插入图片描述

bothV()

// 获取边 S1:1004>1>>S2:G6246!2020-08-20 的所有邻接点
g.E('S1:1004>1>>S2:G6246!2020-08-20').bothV()

在这里插入图片描述

otherV()

一般情况下,bothE().otherV()等价于both()

// 获取边的伙伴节点
g.V('1:1001').outE().otherV()
g.V('1:1001').bothE().otherV()

在这里插入图片描述

综合运用

查询与人员1001乘坐过同一班列车的人员

// 其中as、where、dedup将在下面的章节中介绍
g.V('1:1001').as('n1').out().in().where(neq('n1')).dedup()

在这里插入图片描述

Has过滤

包括:hasLabel(labels…​)、hasId(ids…​)、has(key, value)、has(label, key, value)、has(key, predicate)、hasKey(keys…​)、hasValue(values…​)、has(key)、hasNot(key)

  • hasLabel(labels…​): 节点或边的label与labels列表中任何一个匹配就可以通过
  • hasId(ids…​): 节点或边的id满足ids列表中的任何一个就可以通过
  • has(key): 包含键为key的属性的节点或边通过
  • has(key, value): 包含属性“key=value”的节点或边通过
  • has(label, key, value): 包含属性“key=value”且label值匹配的节点或边通过
  • has(key, predicate): 包含键为key且对应的value满足predicate的节点或边通过
  • hasKey(keys…​): 节点的属性键包含所有的keys列表成员才能通过
  • hasValue(values…​): 节点的属性值包含所有的values列表成员才能通过
  • hasNot(key): 不包含键为key的属性的节点或边通过

!!!在HugeGraph中,按property的值查询之前,应该对property建立索引,否则将无法查到结果并引发异常。(Vertex的PrimaryKeys和Edge的SortKeys例外,可以无须建立索引)

// 对'ry'的'ry_xm'属性建立secondary索引,可以对Text类型property按值进行查询
graph.schema().indexLabel('ryByXm').onV('ry').by('ry_xm').secondary().ifNotExist().create()
// 对'ry'的'ry_nl'属性建立range索引,可以对Number类型property进行范围查询
graph.schema().indexLabel('ryByNl').onV('ry').by('ry_nl').range().ifNotExist().create()

hasLabel()

g.V().hasLabel('ry')
g.V().hasLabel('ry','lc')

在这里插入图片描述

hasId()

g.V().hasId('1:1001')

has()

g.V().has('ry_xm')
g.V().has('ry_xm','张三')
g.V().has('ry','ry_xm','张三')
g.V().has('ry_xm', eq('张三'))

在这里插入图片描述

hasKey()

g.V().properties().hasKey('ry_xm')

在这里插入图片描述

hasValue()

g.V().properties().hasValue('张三')

在这里插入图片描述

hasNot()

g.V().hasNot('ry_xm')

在这里插入图片描述

结果集操作

包括:count()、range()、limit()、tail()、skip()

  • count(): 统计查询结果集中元素的个数;
  • range(m, n): 指定下界和上界的截取,左闭右开。(0作为首个元素,上界为-1时表示剩余全部);
  • limit(n): 获取前n个元素
  • tail(n): 获取后n个元素
  • skip(n): 跳过前n个元素,获取剩余的元素

查询路径

包括:path()、simplePath()、cyclicPath()、shortestPath()

  • path():返回当前遍历过的所有路径
  • simplePath():返回没有环路的路径
  • cyclicPath():返回包含环路的路径
  • shortestPath():返回最短非环路的路径

path()

// HugeGraph节点到与其有直接关联的节点的路径(仅包含节点)
g.V('1:1001').both().path()

在这里插入图片描述

// HugeGraph节点到与其有直接关联的节点的路径(包含节点和边)
g.V('1:1001').bothE().otherV().path()

在这里插入图片描述

simplePath()

g.V('1:1001').as('n1').out().in().simplePath().path()

在这里插入图片描述

cyclicPath()

g.V('1:1001').as('n1').out().in().cyclicPath().path()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rGwYavPb-1613789346881)(_v_images/20200904175047136_15194.png =1138x)]
在这里插入图片描述

shortestPath()

TODO 待补充

循环操作

包括:repeat()、times()、until()、emit()、loops()

  • repeat(): 指定要重复执行的语句,如repeat(out(‘friend’))
  • times(): 指定要重复执行的次数,如执行3次repeat(out(‘friend’)).times(3)
  • until(): 指定循环终止的条件,如一直找到某个名字的朋友为止repeat(out(‘friend’)).until(has(‘name’,‘xiaofang’))
  • emit(): 指定循环语句的执行过程中收集数据的条件,每一步的结果只要符合条件则被收集,不指定条件时收集所有结果
  • loops(): 当前循环的次数,可用于控制最大循环次数等,如最多执行3次repeat(out(‘friend’)).until(loops().is(3))

repeat()+times()

// 访问 1001 节点的2度邻接点
// 1. 访问1001的所有邻接点(假设结果集为A)
// 2. 再访问A的所有邻接点
g.V('1:1001').repeat(both()).times(2)

在这里插入图片描述

repeat()+until()

// 从1001开始循环查找双向邻接点,直到节点包含属性 ry_xm 且 值为 '赵六',输出路径
g.V('1:1001').repeat(both()).until(has('ry_xm','赵六')).path()

在这里插入图片描述

repeat()+emit()

// 查询1001的所有BOTH可达路径,且满足是'ry'类型的点
g.V('1:1001').repeat(both()).emit(hasLabel('ry')).path()

在这里插入图片描述

repeat()+loops()

// 从1001开始循环查找双向邻接点,直到循环次数为2
g.V().repeat(both()).until(loops().is(2)).path()

在这里插入图片描述

排序操作

包括:order()、by()

order()

// 以默认排序(升序)输出所有节点的 ry_sfzhm 属性,要求属性实现`java.lang.Comparable`接口
g.V().values('ry_sfzhm').order()

在这里插入图片描述

by()

// 升序
g.V().values('ry_sfzhm').by(incr)
// 降序
g.V().values('ry_sfzhm').by(decr)
// 随机排序
g.V().values('ry_sfzhm').by(shuffle)
// 升序
g.V().hasLabel('ry').order().by('ry_sfzhm')
// 升序,取 ry_sfzhm 属性值
g.V().hasLabel('ry').order().by('ry_sfzhm').values('ry_sfzhm')
// 升序
g.V().hasLabel('ry').order().by('ry_sfzhm',incr)

在这里插入图片描述

分组去重操作

包括:group()、groupCount()、dedup()、by()、mean()

  • group(): 对结果集进行分组,可通过by(property)来指定根据什么维度进行分组,如果不指定维度则以元素id进行分组。也可对每一组的元素列表进行reduce操作,依然使用by()语句,如by(count())对组内元素计数。
  • groupCount(): 对结果集进行分组,并统计每一组的元素个数
  • dedup(): 去除结果集中相同的元素,可通过by(property)来指定根据什么维度进行去重。
  • by(): 语义上一般指“根据什么维度”,与上述语句配合使用,如group().by()、dedup().by()等。也可与其它语句配合,如前面讲到的排序order().by()及路径path().by()等。

group()

// 对ry节点,基于 ry_xb 属性分组,并统计每个组的节点数
g.V().hasLabel('ry').group().by('ry_xb').by(count())
// 按节点类型分组统计
g.V().group().by(label).by(count())

在这里插入图片描述

groupCount()

// 对ry节点,基于 ry_xb 属性分组,并统计每个组的节点数
g.V().hasLabel('ry').groupCount().by('ry_xb')
// 按节点类型分组统计
g.V().groupCount().by(label)

在这里插入图片描述

dedup()

// 查询与1001乘坐过同一班列车的人员
g.V('1:1001').as('n1').out().in().where(neq('n1')).dedup()
// 查询ry的ry_xb属性值,并去重
g.V().hasLabel('ry').values('ry_xb').dedup()

在这里插入图片描述

过滤操作

包括:where()、filter()

predicate列表

逻辑运算and()、or()或者not()作用于predicate会产生一个新的predicate

PredicateDescription
eq(object)传入的对象等于目标object
neq(object)传入的对象不等于目标object
lt(number)传入的数字小于目标number
lte(number)传入的数字小于或等于目标number
gt(number)传入的数字大于目标number?
gte(number)传入的数字大于或等于目标number
inside(low,high)传入的数字大于low且小于high?
outside(low,high)传入的数字小于low或者大于high?
between(low,high)传入的数字大于等于low且小于high?
within(objects…​)传入的对象等于目标对象列表objects中的任意一个?
without(objects…​)传入的对象不等于目标对象列表objects中的任何一个?

predicate可以通过test()来获得boolean值

// 测试3=2
eq(2).test(3)
// 测试d在[a,b,c]集合内
within('a','b','c').test('d')
// 测试2在1-5的集合内
inside(1,5).test(2)
// 测试2=2
not(neq(2)).test(2)
// 测试3在[1,2,3]集合内,且3!=2
within(1,2,3).and(not(eq(2))).test(3)
// 测试3在1-4集合内,或者3=5
inside(1,4).or(eq(5)).test(3)

在这里插入图片描述

where()

where()有三种模式

  • where§
  • where(String,P)
  • where(Traversal)
// 查询跟1001坐过同一车次的人员列表,排除了自身
// where(P)
g.V('1:1001').as('n1').out().in().where(neq('n1'))
// where(String,P)
g.V('1:1001').as('n1').out().in().as('n2').where('n1',neq('n2'))
// where(Traversal)
g.V('1:1001').as('n1').out().in().where(has('ry_xm',neq('张三')))

在这里插入图片描述

filter()

filter()有三种用法:
lambda方式,filter {it.get()…}
Traversal方式,filter(Traversal)
特定filter step 方式

// 过滤人员节点
g.V().filter {it.get().label()=='ry'}
g.V().filter(label().is('ry'))
g.V().hasLabel('person')

在这里插入图片描述

逻辑运算

包括:is()、and()、or()、not()

  • is():可以接受一个对象(能判断相等)或一个判断语句(如:P.gt()、P.lt()、P.inside()等),当接受的是对象时,原遍历器中的元素必须与对象相等才会保留;当接受的是判断语句时,原遍历器中的元素满足判断才会保留,其实接受一个对象相当于P.eq();
  • and():可以接受任意数量的遍历器(traversal),原遍历器中的元素,只有在每个新遍历器中都能生成至少一个输出的情况下才会保留,相当于过滤器组合的与条件;
  • or():可以接受任意数量的遍历器(traversal),原遍历器中的元素,只要在全部新遍历器中能生成至少一个输出的情况下就会保留,相当于过滤器组合的或条件;
  • not():仅能接受一个遍历器(traversal),原遍历器中的元素,在新遍历器中能生成输出时会被移除,不能生成输出时则会保留,相当于过滤器的非条件。

is()

// 查找节点ry_xm属性值为'张三'的数量
g.V().values('ry_xm').is('张三').count()
// 查找乘坐人数大于2的列车车次
g.V().where(__.in().count().is(gt(2))).values('lc_cc')

在这里插入图片描述

and()

// 查找所有出边CC的人员姓名
g.V().and(outE('cc')).values('ry_xm')
// 查找出边CC,ry_xm 为 '张三'的节点
g.V().and(outE('cc'),values('ry_xm').is('张三'))
g.V().where(outE('cc').and().values('ry_xm').is('张三'))

在这里插入图片描述

or()

用法等同于and,略

not()

// 查找非ry节点
g.V().not(hasLabel('ry')).label()
// 查找没有乘坐过 K9059 车次的人员
g.V().hasLabel('ry').not(out('cc').values('lc_cc').is('K9059')).values('ry_xm')

在这里插入图片描述

统计运算

包括:sum()、max()、min()、mean()

  • sum():将流上的所有的数字求和;
  • max():对流上的所有的数字求最大值;
  • min():对流上的所有的数字求最小值;
  • mean():将流上的所有的数字求均值;

数学运算

math()支持by(),其中多个by() 按照在math()运算表达式中首次引用变量的顺序应用
保留变量 _ 是指传入math()的当前遍历器对象
运算符:+-*/%^
内嵌函数

函数名描述
abs绝对值
acos反余弦
asin反正弦
atan反正切
cbrt立方根
cell向上取整
cos余弦
cosh双曲余弦
exp以e为底的指数
floor向下取整
log以e为底的对数
log10以10为底的对数
log2以2为底的对数
sin正弦
sinh双曲正弦
sqrt平方根
tan正切
tanh双曲正切
signum签名功能
g.V().groupCount().by('ry_xb').math(`floor(_/3)*2`)

在这里插入图片描述

路径选取

包括:as()+select()、as()+where()、as()+match()、as()+dedup()

  • as()+select(): 对路径中结果进行选取,还可通过select().by(property)来指定根据什么维度进行选取。
  • as()+where(): 以条件匹配的方式进行路径结果选取
  • as()+match(): 以模式匹配的方式进行路径结果选取
  • as()+dedup(): 根据路径中的若干步骤的结果进行去重,只有首次出现的路径段才能被选取出来

as()+select()

// 返回结果是n1和n3的笛卡尔积
g.V('1:1001').as('n1').out().as('n2').in().as('n3').select('n1','n3')
// 从结果集中获取第一个路径
g.V('1:1001').as('n1').out().as('n1').select(first,'n1')
// 从结果集中获取最后一个路径
g.V('1:1001').as('n1').out().as('n1').select(last,'n1')
// 从结果集中获取全部路径
g.V('1:1001').as('n1').out().as('n1').select(all,'n1')
// 返回n1.ry_xm 与n3.ry_xm 的笛卡尔积
g.V('1:1001').as('n1').out().as('n2').in().as('n3').select('n1','n3').by('ry_xm').by('ry_xm')
// 从map中选择指定key的值
g.V().valueMap().select('ry_xm','ry_xb').dedup()

在这里插入图片描述

as()+where()

// 查出n1集合和n2集合,并通过where从n2中过滤掉n1,返回剩下的ry_xm属性
g.V('1:1001').as('n1').out().in().as('n2').where('n1',neq('n2')).select('n2').by('ry_xm')

在这里插入图片描述

as()+match()

// 从1001出发,查出1001关联的所有节点n2(列车)
// 查出所有指向n2的节点n3(人员)
// 查出n3同时指向同一节点(列车)的所有节点(人员),并要求该节点不包含n2
g.V('1:1001').as('n1').out().as('n2').match(
    __.as('n2').in().as('n3'),
    __.not(__.as('n3').out().in().as('n3'))
).select('n2').dedup()

在这里插入图片描述

as()+dedup()

// 查询与人员1001乘坐过同一班列车的人员
g.V('1:1001').as('n1').out().in().where(neq('n1')).dedup()

分支

包括:choose()、branch()

choose()

单独使用:choose(predicate, true-traversal, false-traversal)

  • 如果 false-traversal 为空或者是identity(),则不满足 predicate的对象直接通过
// 姓名为张三的输出lc标签,其他的输出ry标签
g.V().hasLabel('ry').choose(values('ry_xm').is(eq('张三')), __.out(), __.out().in()).label()
// 性别为男性的,输出其关联的lc,女性输出ry
g.V().hasLabel('ry').choose(values('ry_xb').is(eq('男')), __.out()).label()

在这里插入图片描述

配合option:choose(traversal).option(value1, traversalA).option(value2, traversalB)…

  • option提供了一个none,相当于switch的default
// 男性输出姓名,女性输出身份证号码
g.V().hasLabel('ry').choose(values('ry_xb')).option('男',__.values('ry_xm')).option('女',__.values('ry_sfzhm'))
g.V().hasLabel('ry').choose(values('ry_xb')).option('男',__.values('ry_xm')).option(none,__.values('ry_sfzhm'))

在这里插入图片描述

branch()

branch有三种使用方式:

  • lambda方式,branch{it.get()…}
  • Traversal方式,branch(Traversal)
  • 特定的branch step方式
// 除了张三,其他人输出身份证号码
// lambda方式
g.V().hasLabel('ry').branch{it.get().value('ry_xm')}.option('张三', values('ry_xm')).option(none, values('ry_sfzhm'))
// Tranversal方式
g.V().hasLabel('ry').branch(values('ry_xm')).option('张三', values('ry_xm')).option(none, values('ry_sfzhm'))
// step方式
g.V().hasLabel('ry').choose(has('ry_xm','张三'), values('ry_xm'), values('ry_sfzhm'))

在这里插入图片描述

合并

包括:coalesce()、optional()、union()

  • coalesce: 可以接受任意数量的遍历器(traversal),按顺序执行,并返回第一个能产生输出的遍历器的结果;
  • optional: 只能接受一个遍历器(traversal),如果该遍历器能产生一个结果,则返回该结果,否则返回调用optionalStep的元素本身。当连续使用.optional()时,如果在某一步返回了调用元素本身,则后续的.optional()不会继续执行;
  • union: 可以接受任意数量的遍历器(traversal),并能够将各个遍历器的输出合并到一起;

coalesce()

// 由于 1:1001 节点没有入边,所以inE()不产生输出
g.V('1:1001').coalesce(inE(), out()).path()

在这里插入图片描述

optional()

// 同上
g.V('1:1001').optional(inE()).optional(out())

在这里插入图片描述

union()

// 找出节点 2:G6246!2020-08-19 的邻接点和入边
g.V('2:G6246!2020-08-19').union(both(),inE()).path()

在这里插入图片描述

聚集与展开

包括:aggregate()、store()、unfold()、fold()

  • aggregate(): 聚集路径中指定步骤的所有结果,通过aggregate(label)对任意步骤打上标签,在此之前的步骤的结果均会被收集到此标签所代表的集合中(但并不会影响路径的游走),可配合by及cap一起使用,通过cap(label)来获取该结果集合,此外还可通过select(label)或without(label)等其它方式读取。
  • store(): 类似aggregate(),只是以Lazy的方式来收集。
  • unfold(): 将集合展开平铺,路径将扩张。
  • fold(): 将多个元素折叠为一个集合,路径将收缩。

aggregate()

// 收集out到x中
g.V('1:1001').out().aggregate('x')
// .aggregate('x').cap('x') 等同于 as('x')
g.V('1:1001').out().aggregate('x').cap('x')
// 仅收集 lc_cc 属性
g.V('1:1001').out().aggregate('x').by('lc_cc').cap('x')

在这里插入图片描述

store()

// store 是lazy方式收集结果的,后续使用limit限制,因此只返回一个元素
g.V().hasLabel('ry').store('x').by('ry_xm').limit(1).cap('x')
// aggregate 不受limit限制,返回所有元素
g.V().hasLabel('ry').aggregate('x').by('ry_xm').limit(1).cap('x')

unfold()

// 注意添加unfold()后,data的变化
g.V('1:1001').out().aggregate('x').cap('x').unfold()

在这里插入图片描述

fold()

// 注意添加fold()后,data的变化
g.V('1:1001').out().aggregate('x').cap('x').fold()

在这里插入图片描述

模式匹配

match()语句通过多个模式片段traversal 来进行模式匹配。这些traversal 中会定义一些变量,只有满足所有用变量表示的约束的对象才能够通过,并被放到一个Map<String, Object>中,其中map的key为变量名(label),value为顶点、边、路径或者属性。

// 查询与 1001 有过搭乘同一车次经历的,姓名叫 李四 的所有乘坐过的列车信息
g.V('1:1001').match(
    __.as('n1').out().in().as('n3'),
    __.as('n3').has('ry_xm','李四').as('n2'),
    __.as('n2').out().as('n4')
).dedup().select('n4')

在这里插入图片描述

随机过滤

包括:sample()、coin()

  • sample: 接受一个整数值,从前一步的遍历器中采样(随机)出最多指定数目的结果;
  • coin: 字面意思是抛硬币过滤,接受一个浮点值,该浮点值表示硬币出现正面的概率。coin Step 对前一步的遍历器中的每个元素都抛一次硬币,出现正面则可以通过,反面则被拦截。
g.V().out().sample(2)
g.V().coin(0.5)

注入

包括:constan()、inject()

  • constant: 通常用在choose或coalesce中做辅助输出,为那些不满足条件的元素提供一个默认值;
  • inject: 能够在流(遍历器)的任何位置注入与当前遍历器同输出类型的对象,当然,也可以作为流的起始 Step 产生数据;
// ry节点返回 ry_xm属性,其他节点返回小明
g.V().choose(hasLabel('ry'), values('ry_xm'), constant('小明'))
// 给1001的OUT邻接点的lc_cc属性值集合,注入 A1001
g.V('1:1001').out().values('lc_cc').inject('A1001') 

结果存取口袋

包括:withSack()、sack()

  • withSack(): 创建一个口袋,并给定一个初始结构,比如可以是一个返回Map或者随机数的lambda函数,也可以是一个对象或常数;另外还可以提供lambda分裂函数,以指定当traverser分裂时的行为,比如进行clone操作。
  • sack(): 将数据放入口袋,或者从口袋取出数据。当传入lambda合并函数作为参数时,可指定放入口袋的行为如何执行;当不传入参数时表示读取口袋中的内容。
g.withSack(1).V().sack()
g.withSack{new Random().nextFloat()}.V().sack()

遍历栅栏

包括:barrier()

// 将所有节点打印出来,打印完第一轮再打印第二轮
def list=[]
g.V().sideEffect{list.add("first: "+it)}
     .barrier()
     .sideEffect{list.add("second: "+it)}
     .iterate()
list
// 对比无栅栏
def list=[]
g.V().sideEffect{list.add("first: "+it)}
     .sideEffect{list.add("second: "+it)}
     .iterate()
list

在这里插入图片描述
在这里插入图片描述

局部操作

包括:local()、count()、max()、mean()、min()、sum()、order()、tail()、limit()、range()、sample()、skip()、dedup()

local()

// 加了local之后,.order().by(value).limit(2) 作用于每个节点,其实没有任何意义
// 不加local时,.order().by(value).limit(2) 作用于整个结果集
g.V().hasLabel('ry').as('n1')
    .local(properties('ry_sfzhm').order().by(value).limit(2))
    .value().as('n2')
    .select('n1','n2').by('ry_xm').by()

在这里插入图片描述

count()

max()、min()、mean()、sum()与count()使用local的方法一致

// 返回3
g.V('1:1001').propertyMap().count(local)
// 返回1
g.V('1:1001').propertyMap().count()

在这里插入图片描述

limit()

tail()、range()、skip()与limit()使用local的方法一致

g.V().valueMap().limit(local,1)

dedup()

g.V().both().group().by(label).select('ry').dedup(local)

order()

g.V().groupCount().by(label).order(local).by(values, decr)

sample()

// 所有顶点作为一个集合,从中采样2个
g.V().fold().sample(local,2)

遍历终止

包括:hasNext()、next()、tryNext()、toList()、toSet()、toBulkSet()、fill()、iterate()

  • hasNext: 判断遍历器是否含有元素(结果),返回布尔值;
  • next: 不传参数时获取遍历器的下一个元素,也可以传入一个整数 n,则获取后面 n 个元素;
  • tryNext: hasNext和next的结合版,返回一个Optional对象,如果有结果还需要调用get()方法才能拿到;
  • toList: 将所有的元素放到一个List中返回;
  • toSet: 将所有的元素放到一个Set中返回,会去除重复元素;
  • toBulkSet: 将所有的元素放到一个能排序的List中返回,重复元素也会保留;
  • fill: 传入一个集合对象,将所有的元素放入该集合并返回,其实toList、toSet和toBulkSet就是通过fillStep实现的;
  • iterate: 这个 Step 在终止操作里面有点特殊,它并不完全符合终止操作的定义。它会在内部迭代完整个遍历器但是不返回结果。

hasNext()

g.V('1:1001').out().hasNext()

在这里插入图片描述

next()

next()与next(n)使用中有一点小小的区别,就是当没有元素或者没有足够多的元素时,执行next()会报错,但是执行next(n)则是返回一个空集合(List)。

g.V('1:1001').out().next()
g.V('1:1001').out().next(2)

在这里插入图片描述

tryNext()

g.V('1:1001').out().tryNext()

在这里插入图片描述

toList()

包含重复的元素

g.V().out().toList()

在这里插入图片描述

toSet()

不包含重复元素

g.V().out().toSet()

在这里插入图片描述

toBulkSet()

toList的基础上做了排序

g.V().out().toBulkSet(),推测是按对象的生成时间排序

在这里插入图片描述

fill()

results = []
g.V().out().fill(results)
results

在这里插入图片描述

iterate()

调用了iterate()后遍历器内部的元素就已经全部迭代过了,所以再调用hasNext()返回false。

it = g.V().hasLabel('ry').iterate()
it.hasNext()

在这里插入图片描述

转换操作

包括:map()、flatMap()

  • map: 可以接受一个遍历器 Step 或 Lamda 表达式,将遍历器中的元素映射(转换)成另一个类型的某个对象(一对一)
  • flatMap: 可以接受一个遍历器 Step 或 Lamda 表达式,将遍历器中的元素映射(转换)成另一个类型的某个对象流或迭代器(一对多)。

map()

g.V('1:1001').out().map(values('lc_cc'))
g.V('1:1001').out().map{it.get().value('lc_cc')+"_"+it.get().value('lc_ccrq')}

在这里插入图片描述

flatMap()

g.V('1:1001').out().flatMap(inE())

在这里插入图片描述

附加操作

Gremlin在路径遍历的时候,可以在路径中做一些额外的附加操作,这个附加操作不会改变上一步的结果,会原封不动的传递到下一步去。附加操作看起来就像透明的,但实际上可以将附加操作的处理结果存储到外部变量中去。包括:sideEffect()、withSideEffect()

  • sideEffect(): 在某个位置插入一个附加操作,以执行额外的操作,通常可与store、sack等配合使用。另外如下一些Step本质上也是sideEffect:group(string)、groupCount(string)、subgraph(string)、aggregate(string)、inject(string)、profile(string)等。
  • withSideEffect():绑定初始值到变量上,等价于sideEffect的效果。

sideEffect()

// 打印所有ry节点,下图1是未加list的结果,图2是加了list的结果,可见附加操作仅对list做了修改,而不影响遍历
def list=[]
g.V().hasLabel('ry')
 .sideEffect{list.add("vertex:"+it)}
 .toList()
list

在这里插入图片描述
在这里插入图片描述

withSideEffect()

// 初始化一个变量以供后续条件判断中使用
// 查找与1003搭乘过同一列车,且姓名在 p 中的
g.withSideEffect('p',['张三','李四','赵六'])
 .V('1:1003').out().in()
 .values('ry_xm').where(within('p'))

在这里插入图片描述

执行计划

包括:profile()、explain()

  • explain(),详细描述原始的Gremlin语句在编译期是如何转变为最终要执行的step集合的
  • profile(),统计Gremlin语句执行过程中的每个step消耗的时间和通过的对象等统计信息

遍历策略:

  • Decoration,应用程序级别的策略
  • Optimization,TinkerPop3级别的策略
  • Provider optimization,图数据库实现级别的策略
  • Finalization,遍历执行前的调整和清理策略
  • Verification,判断遍历是否合法的验证策略

explain()

  • original,表示Gremlin语句等价的最初的step列表
  • intermediate,表示original转化在TraversalStrategy作用下的转化过程
    • strategy,表示作用于上一轮的step列表的TraversalStrategy
    • category,表示strategy中的TraversalStrategy所属的级别
    • traversal,表示上一轮的step列表经过strategy中的TraversalStrategy处理之后的新的step列表
  • final,表示经过所有TraversalStrategy处理后的最终要执行的step列表
g.V('1:1001').out().in().explain()

在这里插入图片描述

profile()

  • name是step的名字
  • dur是step执行的时间,单位是毫秒
  • annotations中的percentDur是当前step消耗的时间在总的执行时间中的比例
  • counts中的traverserCount是当前step中的traverser的数目
  • counts中的elementCount是当前step中的element的数目
g.V('1:1001').out().in().profile()
// profile()语句还可以指定一个key,在执行完要统计的Gremlin语句后,通过key获取统计信息
t=g.V().out('created').profile('metrics')
t.iterate()
t.getSideEffects().get('metrics')

在这里插入图片描述

示例数据

schema = graph.schema();
// 创建人员节点属性
schema.propertyKey("ry_sfzhm").asText().ifNotExist().create();
schema.propertyKey("ry_xm").asText().ifNotExist().create();
schema.propertyKey("ry_xb").asText().ifNotExist().create();
// 创建人员节点类型
schema.vertexLabel("ry").properties("ry_sfzhm", "ry_xm", "ry_xb").primaryKeys("ry_sfzhm").ifNotExist().create();
// 人员节点数据
r1=graph.addVertex(T.label, "ry", "ry_sfzhm" ,"1001", "ry_xm" ,"张三", "ry_xb" ,"男");
r2=graph.addVertex(T.label, "ry", "ry_sfzhm" ,"1002", "ry_xm" ,"李四", "ry_xb" ,"男");
r3=graph.addVertex(T.label, "ry", "ry_sfzhm" ,"1003", "ry_xm" ,"王五", "ry_xb" ,"女");
r4=graph.addVertex(T.label, "ry", "ry_sfzhm" ,"1004", "ry_xm" ,"赵六", "ry_xb" ,"女");

// 创建列车节点属性
schema.propertyKey("lc_cc").asText().ifNotExist().create();
schema.propertyKey("lc_rq").asText().ifNotExist().create();
schema.propertyKey("lc_sfz").asText().ifNotExist().create();
schema.propertyKey("lc_zdz").asText().ifNotExist().create();
schema.propertyKey("lc_jtz").asText().ifNotExist().create();
// 创建列车节点类型
schema.vertexLabel("lc").properties("lc_cc", "lc_rq", "lc_sfz", "lc_zdz", "lc_jtz").primaryKeys("lc_cc", "lc_rq").ifNotExist().create();
// 列车节点数据
lc1=graph.addVertex(T.label, "lc", "lc_cc" ,"G6246", "lc_rq" ,"2020-08-19", "lc_sfz" ,"深圳北" , "lc_zdz" ,"广州南", "lc_jtz" ,"深圳北,广州南" );
lc3=graph.addVertex(T.label, "lc", "lc_cc" ,"G6246", "lc_rq" ,"2020-08-20", "lc_sfz" ,"深圳北" , "lc_zdz" ,"广州南", "lc_jtz" ,"深圳北,广州南" );
lc2=graph.addVertex(T.label, "lc", "lc_cc" ,"K9059", "lc_rq" ,"2020-08-20", "lc_sfz" ,"怀化" , "lc_zdz" ,"深圳西", "lc_jtz" ,"怀化,新化,湘乡,广州,东莞,深圳西" );

// 创建乘车边属性
schema.propertyKey("cc_fz").asText().ifNotExist().create();
schema.propertyKey("cc_dz").asText().ifNotExist().create();
schema.propertyKey("cc_cx").asText().ifNotExist().create();
schema.propertyKey("cc_zw").asText().ifNotExist().create();
schema.propertyKey("cc_spsj").asText().ifNotExist().create();
// 创建乘车边类型(多关系边)
schema.edgeLabel("cc").link("ry", "lc").properties("cc_fz","cc_dz","cc_cx","cc_zwh","cc_spsj").sortKeys("cc_spsj").ifNotExist().create();
// 乘车边数据
r1.addEdge("cc", lc1, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "12", "cc_zw", "12a", "cc_spsj", "2020-08-18 12:00:00");
r1.addEdge("cc", lc2, "cc_fz", "广州", "cc_dz", "深圳西", "cc_cx", "6", "cc_zw", "08a", "cc_spsj", "2020-08-19 20:00:00");
r2.addEdge("cc", lc1, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "12", "cc_zw", "12b", "cc_spsj", "2020-08-18 12:00:00");
r2.addEdge("cc", lc2, "cc_fz", "广州", "cc_dz", "深圳西", "cc_cx", "6", "cc_zw", "08b", "cc_spsj", "2020-08-19 20:00:00");
r3.addEdge("cc", lc3, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "10", "cc_zw", "04c", "cc_spsj", "2020-08-19 12:00:00");
r4.addEdge("cc", lc3, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "10", "cc_zw", "04b", "cc_spsj", "2020-08-19 12:00:00");
r4.addEdge("cc", lc2, "cc_fz", "广州", "cc_dz", "深圳西", "cc_cx", "6", "cc_zw", "08c", "cc_spsj", "2020-08-19 20:00:00");

同乘查询

// 查询跟 1:1001 同乘次数超过1次(gt(1))的人
g.V('1:1001').as('n1').out('cc').in('cc').as('n2').where('n1',neq('n2')).group().by('ry_sfzhm').unfold().select(values).where(count(local).is(gt(1))).unfold().dedup()
_zw", "12a", "cc_spsj", "2020-08-18 12:00:00");
r1.addEdge("cc", lc2, "cc_fz", "广州", "cc_dz", "深圳西", "cc_cx", "6", "cc_zw", "08a", "cc_spsj", "2020-08-19 20:00:00");
r2.addEdge("cc", lc1, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "12", "cc_zw", "12b", "cc_spsj", "2020-08-18 12:00:00");
r2.addEdge("cc", lc2, "cc_fz", "广州", "cc_dz", "深圳西", "cc_cx", "6", "cc_zw", "08b", "cc_spsj", "2020-08-19 20:00:00");
r3.addEdge("cc", lc3, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "10", "cc_zw", "04c", "cc_spsj", "2020-08-19 12:00:00");
r4.addEdge("cc", lc3, "cc_fz", "深圳北", "cc_dz", "广州南", "cc_cx", "10", "cc_zw", "04b", "cc_spsj", "2020-08-19 12:00:00");
r4.addEdge("cc", lc2, "cc_fz", "广州", "cc_dz", "深圳西", "cc_cx", "6", "cc_zw", "08c", "cc_spsj", "2020-08-19 20:00:00");

同乘查询

// 查询跟 1:1001 同乘次数超过1次(gt(1))的人
g.V('1:1001').as('n1').out('cc').in('cc').as('n2').where('n1',neq('n2')).group().by('ry_sfzhm').unfold().select(values).where(count(local).is(gt(1))).unfold().dedup()
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值