1. 图库的基本用法
这里以JanusGraph自带的“众神图”为例,介绍图库的基本用法。
![](https://img.haomeiwen.com/i22933483/2210a3542f8a3bff.png)
图表中的特殊文字和符号修饰符(例如,粗体、下划线等)表示特定含义:
符号 | 含义 |
---|---|
粗体key | 图索引键 |
带有星号的粗体key | 图形索引键必须具有唯一值 |
带下划线的key | 以顶点为中心的索引键 |
空心头边 | 功能/唯一边关系(没有重复) |
尾部交叉的边 | 单向边关系(只能在一个方向上移动,即关系推断不可逆) |
1.1. 加载众神图到JanusGraph
1.1.1 使用索引加载图
conf/janusgraph-berkeleyje-es.properties
使用的是es后端索引,所以需要先启动es服务,然后在配置文件末尾加上es服务的ip:
index.search.backend=elasticsearch
index.search.hostname=192.168.200.101,192.168.200.102,192.168.200.103
这里后端数据存储,默认使用的是Berkeleyje,如果要使用Cassandra/HBase,加载对应的配置即可 conf/janusgraph-cql-es.properties
conf/janusgraph-hbase-es.properties
gremlin> graph = JanusGraphFactory.open('conf/janusgraph-berkeleyje-es.properties')
==>standardjanusgraph[berkeleyje:../db/berkeley]
gremlin> GraphOfTheGodsFactory.load(graph)
==>null
gremlin> g = graph.traversal()
==>graphtraversalsource[standardjanusgraph[berkeleyje:../db/berkeley], standard]
JanusGraphFactory.open()
GraphOfTheGodsFactory.load()
这两个方法执行完后,内部完成了以下操作:
- 在图上创建全局的,以顶点中心的索引集合。
- 将所有顶点及其属性添加到图中。
- 将所有边线及其属性添加到图中。
![](https://img.haomeiwen.com/i22933483/c675f6e42071c78b.png)
![](https://img.haomeiwen.com/i22933483/234329324f7e7683.png)
![](https://img.haomeiwen.com/i22933483/5aba8e66c996e7b3.png)
1.1.2. 不使用索引加载图
不使用索引后端支持加载图时,用到下面配置:conf/janusgraph-cql.properties
, conf/janusgraph-berkeleyje.properties
, conf/janusgraph-hbase.properties
, conf/janusgraph-inmemory.properties
。
这时不能使用load,要使用 GraphOfTheGodsFactory.loadWithoutMixedIndex(graph, true)
。
注意:conf/janusgraph-inmemory.properties
使用的是内存存储,即除了没使用索引后端,也没使用数据库后端
1.2. 全局图索引
图数据库访问的典型模式是:首先使用图的索引定位出入口点,这个入口点可以是一个元素或一组元素(元素可以是顶点或边),然后从入口点,根据Gremlin路径遍历其他元素,得出查询的子图来
通过唯一索引name找到saturn,然后就可得到它的属性以及相关的节点:
gremlin> saturn = g.V().has('name', 'saturn').next()
==>v[4272]
gremlin> g.V(saturn).valueMap()
==>[name:[saturn],age:[10000]]
gremlin> g.V(saturn).in('father').in('father').values('name')
==>hercules
属性place是一个边的索引,我们可以在众神图上查询发生在雅典(lat:37.97, lon:23.72)50公里内的所有事件,还可以进一步锁定这些事件涉及的人物:
gremlin> g.E().has('place', geoWithin(Geoshape.circle(37.97, 23.72, 50)))
==>e[2rz-394-9hx-6go][4216-battled->8376]
==>e[367-394-9hx-cs0][4216-battled->16560]
gremlin> g.E().has('place', geoWithin(Geoshape.circle(37.97, 23.72, 50))).as('source').inV().as('god2').select('source').outV().as('god1').select('god1', 'god2').by('name')
==>[god1:hercules,god2:nemean]
==>[god1:hercules,god2:hydra]
Graph indices(图索引,可以简单理解为顶点索引+边索引)是JanusGraph中的一种索引结构类型。当我们查询时(例如 has
或 interval
),JanusGraph会自动使用图索引查出符合的所有顶点/边。JanusGraph第二部分索引是以顶点为中心的索引,这种索引查询效率比较高。
1.2.1. 图遍历样例
多次重复循环遍历可以简写
hercules = g.V(saturn).in('father').in('father').values('name')
# 上面 等效于 下面
hercules = g.V(saturn).repeat(__.in('father')).times(2).next()
查询 Hercules 的父母,以及其父母的身份
gremlin> g.V(hercules).out('father', 'mother')
==>v[4128]
==>v[4232]
gremlin> g.V(hercules).out('father', 'mother').values('name')
==>jupiter
==>alcmene
gremlin> g.V(hercules).out('father', 'mother').label()
==>god
==>human
查询 Hercules 参与的战斗,和战斗对象、战斗次数:
gremlin> g.V(hercules).out('battled')
==>v[8376]
==>v[8416]
==>v[16560]
gremlin> g.V(hercules).out('battled').valueMap()
==>[name:[nemean]]
==>[name:[cerberus]]
==>[name:[hydra]]
gremlin> g.V(hercules).outE('battled').has('time', gt(1)).inV().values('name')
==>cerberus
==>hydra
这里 has
遍历过滤 time>1 时,因为time构建了以顶点为中心的索引,所以这里JanusGraph智能的选择这个索引过滤,加快了了查询效率(时间复杂度 O(log n) n为边条数)。可以使用 toString()
查看 Gremlin 分解步骤
gremlin> g.V(hercules).outE('battled').has('time', gt(1)).inV().values('name').toString()
==>[GraphStep(vertex,[v[4216]]), VertexStep(OUT,[battled],edge), HasStep([time.gt(1)]), EdgeVertexStep(IN), PropertiesStep([name],value)]
1.2.2. 更多样例
gremlin> pluto = g.V().has('name', 'pluto').next()
==>v[2048]
gremlin> // 谁是 pluto 的同居者
gremlin> g.V(pluto).out('lives').in('lives').values('name')
==>pluto
==>cerberus
gremlin> // 不包含自己pluto的同居者
gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name')
==>cerberus
gremlin> // 起别名
gremlin> g.V(pluto).as('x').out('lives').in('lives').where(neq('x')).values('name')
==>cerberus
gremlin> // pluto 兄弟的居住地
gremlin> g.V(pluto).out('brother').out('lives').values('name')
==>sky
==>sea
gremlin> // pluto 的兄弟分别住在哪里
gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god', 'place')
==>[god:v[1024], place:v[512]]
==>[god:v[1280], place:v[768]]
gremlin> // pluto 的兄弟分别住在哪里,显示名字
gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god', 'place').by('name')
==>[god:jupiter, place:sky]
==>[god:neptune, place:sea]
gremlin> // pluto 选择居住地的原因
gremlin> g.V(pluto).outE('lives').values('reason')
==>no fear of death
gremlin> // 因为喜欢选择居住地
gremlin> g.E().has('reason', textContains('loves'))
==>e[6xs-sg-m51-e8][1024-lives->512]
==>e[70g-zk-m51-lc][1280-lives->768]
gremlin> g.E().has('reason', textContains('loves')).as('source').values('reason').as('reason').select('source').outV().values('name').as('god').select('source').inV().values('name').as('thing').select('god', 'reason', 'thing')
==>[god:neptune, reason:loves waves, thing:sea]
==>[god:jupiter, reason:loves fresh breezes, thing:sky]
2. Gremlin 查询语言
JG的图查询语言使用的Gremlin 语言
2.1. 遍历过程介绍
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
上面的查询理解:
-
g
: 通过graph.traversal()
获取的当前图遍历的句柄 -
V
: 对于图中的所有顶点 -
has('name', 'hercules')
: 向下过滤顶点得到属性name=hercules的顶点(name是唯一索引,这里只有一个) -
out('father')
: 从hercules顶点沿着out方向的father边遍历 -
out('father')
: 从hercules父亲的顶点(即Jupiter)沿着out方向的father边遍历 -
name
: 获取hercules爷爷顶点的name属性
查看hercules的家谱,及out('father')
多次重复
gremlin> g.V().has('name', 'hercules').repeat(out('father')).emit().values('name')
==>jupiter
==>saturn
2.2. 迭代遍历
Gremlin Console 是一个REPL(Read-eval-print-loop交互式解析器)环境,结果以字符串显示。但是过渡到Gremlin应用程序时就需要显示遍历,因为应用程序不会自动遍历。
这些是迭代遍历的一些常用方法:
-
iterate()
-预期结果为零或可以忽略。 -
next()
- 获得一个结果。 -
next(int n)
- 获取下n个结果,首先检查hasNext()
。 -
toList()
- 以列表的形式获取所有结果,如果没有返回一个空列表。
一个Java迭代遍历样例:
Traversal t = g.V().has("name", "pluto"); // 定义一个遍历t,注意遍历尚未执行/迭代
Vertex pluto = null;
if (t.hasNext()) { // 检查结果是否还有
pluto = g.V().has("name", "pluto").next(); // 得到一个结果
g.V(pluto).drop().iterate(); // 执行遍历以从graph中删除pluto
}
// 遍历可以被克隆以重用
Traversal tt = t.asAdmin().clone();
if (tt.hasNext()) {
System.err.println("pluto was not dropped!");
}
List<Vertex> gods = g.V().hasLabel("god").toList(); // 查找所有的god
网友评论