这个章节的例子用了一个随JanusGraph发布的诸神图。这个图如下,抽象的数据模型就是属性图模型(Property Graph Model),这个特别的实例描述了罗马神殿了人,神等和地点等的关系。

图例 含义
黑体字key 带索引的key
带星号的黑体字key 带索引,且有唯一值
下划线的key 顶点为中心的索引
空心箭头边 一个不能重复的边
尾部交叉的边 一个单向的边

下载JanusGraph 运行Gremlin Console

JanusGraph可以从这里下载.一旦你获取并解压,就可以打开一个Gremlin的console。Gremlin Console是一个REPL。 它是随着GanusGraph一起发布的。它和标准的Gremlin Console的区别是JanusGraph是预安装和预加载的包。或者一个用户可以选择给一个现有的Gremlin Console安装一个JanusGraph的package。下边的例子使用的是janusgraph.zip,请保证解压这个zip文件。

JanusGraph需要Java8, 最好的Oracle Java8. JanusGraph的shell脚本需要你的系统里指定了$JAVA_HOME的环境变量。

$ unzip janusgraph-0.3.1-hadoop2.zip
Archive:  janusgraph-0.3.1-hadoop2.zip
  creating: janusgraph-0.3.1-hadoop2/
...
$ cd janusgraph-0.3.1-hadoop2
$ bin/gremlin.sh

<pre><code>     \,,,/
     (o o)
</code></pre>

-----oOOo-(3)-oOOo-----
09:12:24 INFO  org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph  - HADOOP_GREMLIN_LIBS is set to: /usr/local/janusgraph/lib
plugin activated: tinkerpop.hadoop
plugin activated: janusgraph.imports
gremlin>

解压后你可以运行bin目录下的Gremlin Console。 Gremlin Console用的是Apache Groovy。Groovy是Java的超集,它有很多方便的语法让交互式编程更加容易。同理Gremlin-Groovy是Groovy的超集。它让图遍历更加容易。下边最基本的例子演示了如何处理数字,字符串和映射。文章剩下部分将讨论图相关的构建。

gremlin> 100-10
==>90
gremlin> "JanusGraph:" + " The Rise of Big Graph Data"
==>JanusGraph: The Rise of Big Graph Data
gremlin> [name:'aurelius', vocation:['philosopher', 'emperor']]
==>name=aurelius
==>vocation=[philosopher, emperor]

把诸神图加载进入JanusGraph

下边的例子会打开一个JanusGraph的graph实例,并且把上边的诸神图加载起来。JanusGraphFactory提供了一组静态的打开方法。每一个是用配置作为参数,返回一个graph的实例。这个例子调用了其中一个打开方法,输入的配置信息是用BerkeleyDB存储,并且用Elasticsearch做索引。用GraphOfTheGodsFactory这个帮助类来加载这个图。这里我们先跳过配置的细节,后边会详细讲。运行前你需要现在安装ElasticSearch。

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()方法对我们新创建的图做了如下的操作:
1. 创建了一组图上的全局的以顶点为中心的索引。
2. 给图上添加顶点,以及他们的属性。
3. 给图上添加边,以及他们的属性。

具体代码在这里

图的全局索引

一种常见的访问图数据库的方式是首先用图的索引定位进入点。进入点是一个或者一组元素,比如是顶点或者边。从入口元素,一个Gremlin路径描述了如何通过明确的图的结构来访问到其他元素。

已知name是一个取值唯一的key。Saturn这个顶点可以被获取,然后就可以获取它的一些属性,比如Saturn这个顶点的名字为”saturn”,age是10000,type是titan。并且获取他的孙子(father的逆关系是child)。结果是Hercules。

gremlin> saturn = g.V().has('name', 'saturn').next()
==>v[256]
gremlin> g.V(saturn).valueMap()
==>[name:[saturn], age:[10000]]
gremlin> g.V(saturn).in('father').in('father').values('name')
==>hercules

place属性也是图的一个索引。place属性是一个边的属性,因此JanusGraph可以在图的索引里对边进行索引。这样就可以去查询在雅典50千米内的事件。然后给出那些顶点都被牵连到这些事件里。

gremlin> g.E().has('place', geoWithin(Geoshape.circle(37.97, 23.72, 50)))
==>e[a9x-co8-9hx-39s][16424-battled->4240]
==>e[9vp-co8-9hx-9ns][16424-battled->12520]
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:hydra]
==>[god1:hercules, god2:nemean]

图的索引是JanusGraph中的一种索引。图索引是自动被JanusGraph选择来回答所有顶点或者边的问题的。g.V或者g.E,并且满足一个或者多个约束。(比如has或者interval)。第二种索引是以顶点为中心的索引。以顶点为中心的索引是用来加速在图例的遍历。后边我们会讲如何使用以顶点为中心的索引。

图遍历

Hercules,是Jupiter和Alcmene的儿子,天生神力。他是个半神,因为他爸是神,他妈是人。Juno,Jupiter的妻子。他对Jupiter的出轨大怒,为了报复,她把Hercules弄瞎了,导致Hercules杀了自己的妻儿。作为惩罚,Hercules被指定为Eurystheus服役。

上边我们演示了Saturn的孙子是Hercules。这个可以用一个loop来表达。事实上,Hercules是一个离Saturn两步远的in(‘father’)关系。

gremlin> hercules = g.V(saturn).repeat(__.in('father')).times(2).next()
==>v[1536]

Hercules是一个半神,为了证明Hercules是一个半人半神,需要检查他父母的属性。可以通过从Hercules去遍历他的父母,最后检查他们的类型,–“god”和“human”

gremlin> g.V(hercules).out('father', 'mother')
==>v[1024]
==>v[1792]
gremlin> g.V(hercules).out('father', 'mother').values('name')
==>jupiter
==>alcmene
gremlin> g.V(hercules).out('father', 'mother').label()
==>god
==>human
gremlin> hercules.label()
==>demigod

到目前为止的例子都是关于罗马万神殿中不同角色的遗传线。属性图模型具有足够的表达能力,可以表示多种类型的事物和关系。这样,众神的图形也识别了大力神的各种英雄壮举——他著名的12次历练。在前一节中,我们发现大力神参与了雅典附近的两场战役。这可以通过一Hercules为顶点,以battled为边来发现这些事件。

gremlin> g.V(hercules).out('battled')
==>v[2304]
==>v[2560]
==>v[2816]
gremlin> g.V(hercules).out('battled').valueMap()
==>[name:[nemean]]
==>[name:[hydra]]
==>[name:[cerberus]]
gremlin> g.V(hercules).outE('battled').has('time', gt(1)).inV().values('name')
==>cerberus
==>hydra

在battled边上的边属性time是被一个顶点的vertex-centric索引的。根据约束/过滤器及时检索与大力神相关的战斗边缘,比对所有边进行线性扫描和过滤(通常是O(log n),其中n是事件的边数)要快。JanusGraph足够智能,可以在可用时使用以顶点为中心的索引。Gremlin表达式的toString()显示分解到各个步骤的过程。

gremlin> g.V(hercules).outE('battled').has('time', gt(1)).inV().values('name').toString()
==>[GraphStep([v[24744]],vertex), VertexStep(OUT,[battled],edge), HasStep([time.gt(1)]), EdgeVertexStep(IN), PropertiesStep([name],value)]

更复杂的图遍历的例子

Pluto住在塔耳塔鲁斯的深处。他和Hercules的关系因为Hercules与他的宠物Cerberus战斗而变得紧张。然而,Hercules是他的侄子——他该如何让Hercules为他的傲慢付出代价呢?

Gremlin在下边的例子里提供了更多的关于诸神图的遍历。

查找同住的人

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> g.V(pluto).as('x').out('lives').in('lives').where(neq('x')).values('name')
==>cerberus

Pluto的兄弟

gremlin> // where do pluto's brothers live?
gremlin> g.V(pluto).out('brother').out('lives').values('name')
==>sky
==>sea
gremlin> // which brother lives in which place?
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> // what is the name of the brother and the name of the place?
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]

最后,Pluto 住在Tartarus ,因为他不关心死亡。另一方面,他的兄弟们选择他们的位置是基于他们对这些地点的某些特质的热爱。

gremlin> g.V(pluto).outE('lives').values('reason')
==>no fear of death
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]

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: