图基本概念
- 图Graph:指关系图。比如:同学及朋友关系图、银行转账图等。
- 顶点Vertex:一般指实体。比如:人、账户等。
- 边Edge:一般指关系。比如:朋友关系、转账动作等。
- 属性Property:顶点或边可以包含属性,比如:人的姓名、人的年龄、转账的时间。
图基本操作
- V()、E()、id()、label()、properties()、valueMap()、values()
graph = JanusGraphFactory.open('conf/gremlin-server/socket-janusgraph-hbase-server.properties') 打开数据库连接
g=graph.traversal() 的到实列
1.V():查询顶点,一般作为图查询的第1步,后面可以续接的语句种类繁多。例:g.V(),g.V(‘v_id’),查询图中所有顶点和特定点;
2.E():查询边,一般作为图查询的第1步,后面可以续接的语句种类繁多 例:g.E(),g.E(‘S3:TinkerPop>4>>S4:Gremlin’);
3.id():获取顶点、边的id。例:g.V().id(),查询所有顶点的id;
4.label():获取顶点、边的 label。例:g.V().label(),可查询所有顶点的label。
5.key() / value():获取属性的key/value的值。例:g.V().peoperites().key(), g.V().properties().value()//获取所有定点的key/value值
6.properties():获取顶点、边的属性;可以和 key()、value()搭配使用,以获取属性的名称或值。例:g.V().properties(‘name’),查询所有顶点的 name 属性,若无name属性则调过。g.V().properties().key(),查询所有顶点属性名称;
7.valueMap():获取顶点、边的属性,以Map的形式体现,和properties()比较像
8.values():获取顶点、边的属性值。例,g.V().values() 等于 g.V().properties().value()
遍历操作
- out()、in()、both()、outE()、inE()、bothE()、outV()、inV()、bothV()、otherV()
遍历(以定点为基础)
1.out(label):根据指定的 Edge Label 来访问顶点的 OUT 方向邻接点(可以是零个 Edge Label,代表所有类型边;也可以一个或多个 Edge Label,代表任意给定 Edge Label 的边,下同);
2.in(label):根据指定的 Edge Label 来访问顶点的 IN 方向邻接点;
3.both(label):根据指定的 Edge Label 来访问顶点的双向邻接点;
4.outE(label): 根据指定的 Edge Label 来访问顶点的 OUT 方向邻接边;
5.inE(label):根据指定的 Edge Label 来访问顶点的 IN 方向邻接边;
6.bothE(label):根据指定的 Edge Label 来访问顶点的双向邻接边;
遍历(以边为基础)
1.outV():访问边的出顶点,出顶点是指边的起始顶点;
2.inV():访问边的入顶点,入顶点是指边的目标顶点,也就是箭头指向的顶点;
3.bothV():访问边的双向顶点;
4.otherV():访问边的伙伴顶点,即相对于基准顶点而言的另一端的顶点;
has条件过滤
- hasLabel(labels…)、hasId(ids…)、has(key, value)、has(label, key, value)、has(key, predicate)、hasKey(keys…)、hasValue(values…)、has(key)、hasNot(key)
- has语句是filter类型语句的代表,能够以顶点和边的属性作为过滤条件,决定哪些对象可以通过。has语句包括很多变种:
1.hasLabel(labels…): 通过label来过滤顶点或边,满足label列表中一个即可通过 例 g.V().hasLabel(‘persion’),g.E().hasLabel(‘shopping’)//包含person标签的定点/包含shopping为标签的边
2.hasId(ids…): 通过id来过滤顶点或者边,满足id列表中的一个即可通过。例:g.V().hasId(‘xiaowang’)/g.E().hasId(‘created’)
3.has(key, value): 通过属性的名字和对应的值来过滤顶点或边 例:g.v().has(‘name’,‘xiaoming’)/g.e().has(‘label’,‘shopping’)//筛选顶点属性包含name并且值为xiaoming的顶点,边同理。
3.has(label, key, value): 通过label和属性的名字和值过滤顶点和边 例:g.V().has(‘people’,‘name’,‘xiaoming’)//刷选顶点包含people的标签且name为小明的顶点数据,边同理。
4.has(key, predicate): 通过对指定属性用条件过滤顶点和边,作用于顶点或者边 例:g.v().has(‘age’,gt(20))//筛选属性值大于20的age的顶点,边同理。
5.hasKey(keys…): properties包含所有的key才能通过,例:g.v().porperties.hasKey(‘name’) //筛选属性值的key包含name的顶点,边同理。直接将hasKey()作用于顶点,仅后端是Cassandra时支持如 g.v().hasKey(‘name’)
6.hasValue(values…): properties包含所有的value才能通过,例:
g.v().porperties().hasValue(‘xiaoming’) //属性值的值包含xiaoming字段的顶点,边同理。直接将hasValue()作用于顶点,仅后端是Cassandra时支持 如g.v().hasValue(‘xiaoming’)
7.has(key): 有这个属性的通过,作用于顶点或者边 例:g.v().has(‘name’) //属性值包含name的顶点,边同理
8.hasNot(key): 没有这个属性的通过 ,例 g.v().hasNot(‘name’) 属性值不包含name的顶点,边同理
图查询返回结果数限制
- count()、range()、limit()、tail()、skip()
Gremlin能统计查询结果集中元素的个数,且允许从结果集中做范围截取。假设某个查询操作(如:g.V())的结果集包含8个元素,我们可以从这8个元素中截取指定部分。主要包括: - count(): 统计查询结果集中元素的个数;例:g.v().hasLabel(‘person’).outE(‘shopping_address’).count() //计算标签为person且shopping_address的边个数
- range(m, n): 指定下界和上界的截取,左闭右开。比如range(2, 5)能获取第2个到第4个元素(0作为首个元素,上界为-1时表示剩余全部)例:g.V().hasLabel(‘person’).range(2, 5)//查询类型为“人person”的顶点中的第2个到第5个数据;
- limit(n): 下界固定为0,指定上界的截取,等效于range(0, n),语义是“获取前n个元素”。比如limit(3)能获取前3个元素 例:g.V().limit(2) //获取前俩个顶点信息;
- tail(n): 上界固定为-1,指定下界的截取,等效于range(count - n, -1),语义是“获取后n个元素”。比如tail(2)能获取最后的2个元素 例:g.V().tail(2) //与limit相反查询后两个顶点;
- skip(n): 上界固定为-1,指定下界的截取,等效于range(n, -1),语义是“跳过前n个元素,获取剩余的元素”。比如skip(6)能跳过前6个元素,获取最后2个元素 例:g.V().hasLabel(‘person’).skip(5) //掉过前5个标签为person的数据从第6个开始拿取。
查询路径path
- path()、simplePath()、cyclicPath()
- 路径:就是经过的顶点成为路径
Path说明
- 在使用Gremlin对图进行分析时,关注点有时并不仅仅在最终到达的顶点、边或者属性上,通过什么样的路径到达最终的顶点、边和属性同样重要。此时可以借助path()来获取经过的路径信息。
- path()返回当前遍历过的所有路径。有时需要对路径进行过滤,只选择没有环路的路径或者选择包含环路的路径,Gremlin针对这种需求提供了两种过滤路径的step:simplePath()和cyclicPath()。
实例
- 例1.path(),获取当前遍历过的所有路径
g.V().hasLabel(‘software’).has(‘name’,‘HugeGraph’).both().path()
- 例2 如果想要同时获得经过的边的信息,可以用bothE().otherV()替换both()
g.V().hasLabel(‘software’).has(‘name’,‘HugeGraph’).bothE().otherV().path()
- 例3 输出路径的时候,通过by(property)语句可以指定对象的某个属性代替对象,且连续的多个by()是循环应用到路径中的对象,例如路径中有3个对象[A, B, C],by(X).by(Y)语句指定两个属性[X Y],代表用“用A的X属性代表A,用B的Y属性代表B,用C的X属性代表C”,具体可参考如下例子:
“该HugeGraph”顶点到与其有直接关联的顶点的路径(包含顶点和边)
用“name”属性代表person和software顶点,用“weight”属性代表边
g.V().hasLabel(‘software’).has(‘name’,‘HugeGraph’)
.bothE().otherV().path().by(‘name’).by(‘weight’)
- 例4 路径分为两种:有环路径和无环路径:
- 有环路径是指路径中至少有一个对象出现的次数大于等于两次。
- 无环路径是指路径中所有的对象只出现一次。
path()返回所有路径,包含有环路径和无环路径,例如:
该“HugeGraph”顶点到与其有直接关联的顶点的路径(包含顶点和边)
用“name”属性代表person和software顶点,用“weight”属性代表边
g.V().hasLabel(‘software’).has(‘name’,‘HugeGraph’)
.both().both().path()
图中红框内的是含有环路的路径,其他的是不含有环路的路径
simplePath(),过滤掉路径中含有环路的对象,只保留路径中不含有环路的对象
该“HugeGraph”顶点到与其有两层关系的顶点的不含环路的路径(只包含顶点)
g.V().hasLabel(‘software’).has(‘name’,‘HugeGraph’)
.both().both().simplePath().path()
cyclicPath(),过滤掉路径中不含有环路的对象,只保留路径中含有环路的对象
该“HugeGraph”顶点到与其有两层关系的顶点的包含环路的路径(只包含顶点)
g.V().hasLabel(‘software’).has(‘name’,‘HugeGraph’)
.both().both().cyclicPath().path()
循环操作
- repeat()、times()、until()、emit()、loops()
循环操作说明
循环操作是指多次执行某一部分语句,用于语句需要重复运行的场景,比如“查找朋友的朋友的朋友”,可以直接使用循环操作来完成即“查找3层朋友”,下面对具体的循环相关的Step进行说明:
- 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 根据次数来重复执行语句
例1 repeat+times
okram是顶点id,访问该顶点id的out方向邻接点1次
g.v(‘okram’).repeat(out()).times(1)
例2 访问某个顶点的2度双向邻接点
访问第1个顶点的所有邻接点(第1层)
再访问第1层结果顶点的邻接点(第2层)
g.V(‘okram’).repeat(both()).times(2)
例3 访问某个顶点的3度OUT邻接点
访问第1个顶点的所有邻接点(第1层)
再访问第1层结果顶点的邻接点(第2层)
再访问第2层结果顶点的邻接点(第3层)
g.v(‘okram’).repeat(out()).times(3)
g.V(‘okram’).out().out().out()等价于g.V(‘okram’).repeat(out()).times(3)
repeat() + until() 根据条件来重复执行语句
例1 查询顶点’okram’到顶点’Gremlin’之间的路径
循环的终止条件是遇到名称是’Gremlin’的顶点
g.v(‘okram’).repeat(out()).until(has(‘name’,‘Gremlin’)).path
注意1:这里用到了path()来获取经过的路径,path的讲解请参考上一期。
注意2:until()与 times()是互斥的,两个语句无法同时存在于同一个循环中。
注意3:until()放在repeat()之前或之后的顺序是会影响逻辑的,放前面表示先判断再执行,放后面表示先执行后判断。请对比如下两个语句的执行结果:
g.V(‘okram’).repeat(out()).until(hasLabel(‘person’)).path()
g.V(‘okram’).until(hasLabel(‘person’)).repeat(out()).path()
repeat() + emit() 收集执行过程中的数据
例1 查询顶点’okram’的所有OUT可达点的路径
g.v(‘okram’).repeat(out()).emit().path()
例2 查询顶点’okram’的所有OUT可达点的路径
且必须满足是’person’类型的点
g.v(‘okram’).repeat(out()).emit(haslabel(‘person’)).path()
注意:emit()放在repeat()之前或之后的顺序是会影响结果的,放前面表示先收集再执行,放后面表示先执行后收集。请对比如下两个语句的执行结果:
g.V(‘okram’).repeat(out()).emit(hasLabel(‘person’)).path()
g.V(‘okram’).emit(hasLabel(‘person’)).repeat(out()).path()
例3 查询顶点’okram’到顶点’Gremlin’之间的路径
此外还收集过程中的’person’类型的顶点
g.v(‘okram’)
.repeat(out())
.until(has(‘name’, ‘Gremlin’))
.emit(hasLabel(‘person’)).path()
注意:emit()与until()搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。
例4 查询顶点’okram’的2度OUT可达点的路径
此外还收集’person’类型的顶点
g.v(‘okram’).repeat(out()).times(2).emit(haslabel(‘person’)).path
注意:emit()与times()搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。
repeat() + loops() 根据最大次数限制来重复执行语句
例 1 查询顶点’okram’的3度OUT可达点路径
g.v(‘okram’).repeat(out()).until(loops().is(3)).path()
例2 查询顶点’okram’到顶点’Gremlin’之间的路径
且之间只相差2跳的距离,其中的and()是指两个条件都满足
g.v(‘okram’)
.repeat(out())
.unitl(has(‘name’,‘Gremlin’).and().loops().has(2))
.path()
综合运用
例 :查找子树
查找从一个节点出发,到叶子节点结束的所有路径这些路径的集合为一颗子树(子图)
g.v(‘okram’).repeat(out()).until(outE().count().is(0)).path()
例2 查找两点之间的最短路径
已知两个顶点’okram’和’javeme’, 通过任意关系来找到这两点之间的路径, 且限制了最大深度为3,若存在那么第一条结果即是最短路径
g.v(‘okram’).bothe().otherv().simplepath()
.until(hasid(‘javeme’).and().loops().is(lte(3)))
.hasid(‘javeme’).path()
注意:bothE().otherV()一般等价于both(),但是在这里有一些差别,后者仅仅返回路径中的顶点信息,前者会把路径中的边信息也返回。