这章节的这个例子扩了一个玩具图的扩展使用,是建在janusgraph叫 The Graph of the Gods。这个图如下图形。这个抽象的数据模型被认为是一个标准的图模型,这个特的例子描述了罗马众神的关系、类型和位置。此外,图形中的特殊文字和信号标识(如加粗、下划线等)表示图中不同的含义/类型。
3.1 下载janusgraph,并运行gremlin控制台
janusgraph可以在项目repo的releases页面下载。一旦拿到并解压缩,就能打开gremlin的控制台。这个gremlin控制台是一个REPL(交互shell界面)的,并与janusgraph一起建立,和标准版的gremlin控制台唯一的区别是janusgraph是一个预安装和预加载的包。相应的,用户也可以选择install和激活janusgraph到一个已存在的gremlin console,从中央库下载janusgraph包。下面的例子里,janusgraph.zip被使用,要记得unzip这个压缩文件。
janusgraph需要java 8.建议是oracle java 8.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
\,,,/
(o o)
-----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>
这个gremlin终端解释命令使用apache groovy。groovy是一个java的超集,有多样的速记声明,能够更容易的互动式编程。同样的,gremlin-groovy是一个groovy的超集,有多种速记声明使得图事务更容易。下面的基础例子示范了处理数字、字符串和map。这个教程的剩下部门将讨论图特有的结构。
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]
tip:可以参考 Apache TinkerPop, SQL2Gremlin, and Gremlin Recipes,有更多信息使用gremlin。
3.2 load到janusgraph(The Graph of the Gods)
下面的例子将打开janusgraph实例,load众神图数据集。 JunasGraphFactory 提供一个静态打开方法的集合,每一个将需要配置作为参数,返回图实例。这个教程用了那些open方法中的一个,后端存储用的是BerkeleyDB和es索引,然后load图用一个帮助类GraphOfTheGodsFactory。这章跳过了配置细节,但是额外的存储、索引和配置在 Part III, “Storage Backends”, Part IV, “Index Backends”, and Chapter 15, Configuration Reference.
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()做了下面新建图结构:
- 创建全局和中央顶点索引的集合在图里。
- 根据配置增加所有的顶点到图里。
- 根据配置增加所有的边到图里。
可以看GraphOfTheGodsFactory source code源码了解细节
对于使用cassandra或hbase,请确保使用了conf/janusgraph-cql-es.properties (or conf/janusgraph-hbase-es.properties)和GraphOfTheGodsFactory.load()。
gremlin> graph = JanusGraphFactory.open('conf/janusgraph-cql-es.properties')
==>standardjanusgraph[cql:[127.0.0.1]]
gremlin> GraphOfTheGodsFactory.load(graph)
==>null
gremlin> g = graph.traversal()
==>graphtraversalsource[standardjanusgraph[cql:[127.0.0.1]], standard]
也可以使用conf/janusgraph-cql.properties, conf/janusgraph-berkeleyje.properties, or conf/janusgraph-hbase.properties配置文件,没有配置索引情况下来打开图。这样的话讲需要使用另一个load方法*GraphOfTheGodsFactory.loadWithoutMixedIndex() *,这样讲不会尝试使用后端索引。
gremlin> graph = JanusGraphFactory.open('conf/janusgraph-cql.properties')
==>standardjanusgraph[cql:[127.0.0.1]]
gremlin> GraphOfTheGodsFactory.loadWithoutMixedIndex(graph, true)
==>null
gremlin> g = graph.traversal()
==>graphtraversalsource[standardjanusgraph[cql:[127.0.0.1]], standard]
3.3 全局图索引
图数据库访问数据的典型方法是使用图索引首先定位图的入口点。入口点是一个元素或一组元素,例如一个顶点或边。从入口的元素,一个gremlin路径解释器通过详细的图结构描述如何构图出其它元素。
根据这里有的唯一索引name属性,Saturn顶点可以被取出。详细的map(Saturn的kv对)能够被查出。Saturn的顶点有name=saturn,age=10000和type=titan。他的孙子(father的inverse是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也是一个图索引,是一个边属性。因而,janusgraph可以通过边进行索引。可以查询Ahtens(37.97,23.72)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),满足1个或多个限制条件(例如has or interval)。索引的第二个部分是中央顶点索引,中央顶点索引用来加速构图。中央顶点索引将在后面描述。
3.3.1 构图例子
Hercules,Jupiter和Alcmene的儿子,天生神力。Hercules是一个Demigod因为他父亲是神而母亲是人。Juno是Jupiter的妻子,为Jupiter的不忠而愤怒。为了报复,她使Hercules精神失常杀了自己的妻子和孩子。....
在前面章节,示范了Saturn的孙子是Hercules。这能用一个环表述,本质上,Hercules是一个顶点举例Saturn有2步远(father路径)
gremlin> hercules = g.V(saturn).repeat(__.in('father')).times(2).next()
==>v[1536]
证明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
上面例子是众神基因线。属性图模型扩展可以体现多种类型和关系。也体现了Hercules的多个英雄事迹。前面了解到他有两次事件在Athens附近,可以查出这些事件通过Hercules顶点构图battle边。
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
边属性time次数在battle边被索引一个顶点的中央顶点索引。查出battle边发生于Hercules根据限制/过滤time,会比线性查询所有边和过滤更快(O(logn),n是关系边的次数)。当可用时,janusgraph足够智能去用中央顶点索引。加一个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)]
3.3.2 更多复杂图构建例子
gremlin构建下面的更复杂例子,每一个图构建的说明在上一行的注释。
gremlin> pluto = g.V().has('name', 'pluto').next()
==>v[2048]
gremlin> // who are pluto's cohabitants?
gremlin> g.V(pluto).out('lives').in('lives').values('name')
==>pluto
==>cerberus
gremlin> // pluto can't be his own cohabitant
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
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]
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]