最近在用Golang作流程引擎,对于流程图的存储,我看到了Google的Cayley图数据库,感受它可能会比较适合个人应用,因而便拿来用了用.node
项目地址在这里:https://github.com/google/cayleygit
系统环境 : Windows 7github
1. 安装web
安装的过程项目文档里写的很明白,有两种安装方式:数据库
2. 使用数组
安装完成以后,你会看到一个【cayley.exe】的文件,它就是咱们要用的程序了。网络
1. 初始化数据库测试
初始化数据库要用到的命令是 init ,你可使用两种方式初始化,一种是命令行,一种是配置文件。google
咱们先来看命令行的形式:spa
- leveldb : cayley.exe init --db=leveldb --dbpath=tmp/testdb —— 你存储数据的目录就是【tmp/testdb】,它会在这个目录下,创建leveldb数据库;
- bolt : cayley.exe init --db=bolt --dbpath=tmp/testdb —— 你存储数据的文件名是【tmp/testdb】,它会在【tmp】目录下,建立一个叫作【testdb】的bolt数据库文件;
- mongo : cayley.exe init --db=mongo --dbpath=”<HOSTNAME>:<PORT>” —— HOSTNAME和PORT指向你的Mongo实例。
在命令行中,--db 和 --dbpath 必须是一块儿出现的。若是你以为输入命令行很麻烦,那么可使用配置文件来初始化。cayley给了一个示例文件【cayley.cfg.example】:
{ "database": "bolt", "db_path": "/tmp/demodb", "read_only": false }这个使用的是bolt数据库,同时配置了路径以及是否只读。关于配置文件如何配置,等一下咱们再说。对我来讲,只须要把这个文件改动一下,就能够为我所用了:
{ "database": "bolt", "db_path": "tmp/testdb", // 我是在【cayley.exe】所在目录下的【tmp】文件夹里建立的数据库文件 "read_only": false }
2. 向数据库加载数据
数据库初始化好了以后,咱们就能够加载数据了。cayley项目里给咱们提供了两个测试数据,都在【data】目录下,一个是【testdata.nq】,它是一个简单的基于好友关注的网络图,一个是【30kmoviedata.nq.gz】,这里存储了30k个电影数据信息。咱们先用简单的【testdata.nq】。
加载数据要用到 load 命令:
cayley.exe load --config=cayley.cfg.example --quads=data/testdata.nq
这样,就把【testdata.nq】里的数据,加载到数据库里了。
3. 链接你的图数据
加载数据以后,咱们就能够查看咱们的图了。cayley提供了两种方式,一种是REPL,一种是HTTP。
先说REPL:
cayley.exe repl --config=cayley.cfg.example
这样,cayley就已经跑起来了,你能够看到命令行已经变成“cayley>”等待你的输入了。
再说HTTP:
cayley.exe http --config=cayley.cfg.example
敲完这行命令以后,你会看到控制台输出“Cayley now listening on 127.0.0.1:64210”,接下来只要访问http://127.0.0.1:64210/就能看到它的web界面了。
4. 使用
在使用以前,咱们先来看一看,cayley里的数据,是什么样的。好比以【testdata.nq】为例:
1 <alice> <follows> <bob> . 2 <bob> <follows> <fred> . 3 <bob> <status> "cool_person" . 4 <charlie> <follows> <bob> . 5 <charlie> <follows> <dani> . 6 <dani> <follows> <bob> . 7 <dani> <follows> <greg> . 8 <dani> <status> "cool_person" . 9 <emily> <follows> <fred> . 10 <fred> <follows> <greg> . 11 <greg> <status> "cool_person" .
它是由一条条四元组构成,其中第一个叫subject,第二个叫predicate,第三个叫object,第四个叫Label(可选),以.结尾。subject和object会转换成有向图的顶点,predicate就是边。label的用法我查了google group,意思是说,你能够在一个数据库里,存多个图,用label来区分不一样的图,可是我没有找到关于它的用法。
这张图的关系以下:
其中箭头指明了follows的关系,而#括起来的人名表示,这些人有status为cool_person。
下来咱们来看对cayley数据库的增删查:
1 cayley> :a
subject
predicate object label .
好比我要添加一我的叫“leon”,他关注了“alice”,而且他也是一个“cool_person”,那么输入这样的命令便可:
1 cayley> :a leon follows alice . 2 cayley> :a leon status cool_person .
1 cayley> :d subject predicate object .
好比我刚才添加的“leon”,如今他不想关注“alice”了,那么这样就能够删除刚才创建的关系了:
1 cayley> :d leon follows alice .
对于查询,cayley提供了两种查询语言,一种是相似于JavaScript的语言,一种是简化的MQL。这里我选用类JavaScript的查询语言。由于它的文档相对完整一些。我会经过介绍对象的方式,把查询方法逐一阐述。
1. graph 对象,简写为 g ,它的存在是惟一的,由它来产生 query 对象,进而返回各类查询结果。它可执行的方法以下所示:
graph.Vertex([nodeId],[nodeId]…) 简写为 g.V
参数:nodeId(可选):一个字符串,或者字符串列表,表明了查询的起始节点
返回:query 对象
从给定的顶点(集)开始一个查询路径,若是没有参数,则认为是图中全部的顶点。
举例:
1 // 我想看目前图中全部的顶点 2 g.V().All() 3 // 我想得到alice这个节点 4 g.V(“alice”).GetLimit(1)
其中All()方法是query对象的方法,用它能够遍历query对象中,全部的数据。GetLimit(number)也是query对象的方法,它得到迭代集里限定数目的数据。
graph.Morphism() 简写为 g.M()
无参数
返回:path 对象
建立一个态射path对象,它自己是不能被查询的,它定义了一类路径映射,能够存储到变量里,在别的查询语句里使用。具体使用以后介绍。主要是和 path.Follow(),path.FollowR()配合使用。
2. path 对象,它是query对象的父类对象。
path对象由 g.V() 和 g.M() 建立,其中 g.V() 建立了query对象,它是path对象的子类。
咱们的查询,主要就是使用这个对象,下面以【testdata.nq】里的数据为例,介绍一下都有哪些查询方法。
path.Out([predicatePath],[tags])
参数:predicatePath(可选)下列其中之一
tags(可选)下列其中之一
这个方法从path对象开始,经过predicate指向其余objects,也就是要查询的数据。
举例:
1 // 查看charlie follows了谁。结果是 bob and dani 2 g.V("charlie").Out("follows") 3 // 查看alice follows的人,他们又follows了谁。结果是 fred 4 g.V("alice").Out("follows").Out("follows") 5 // 从dani出去的路径都指向了哪里。 结果是 bob, greg 和 cool_person 6 g.V("dani").Out() 7 // 找到全部dani经过follows和status指向的节点。 8 // 结果是 bob, greg 和 cool_person 9 g.V("dani").Out(["follows", "status"]) 10 // 找到全部dani经过status指向的节点,并加上tag。 11 // 结果是 {"id": cool_person, "pred": "status"} 12 g.V("dani").Out(g.V("status"), "pred")
path.In([predicatePath],[tags])
和path.Out()用法想同,只不过path.In()查询的是入度,path.Out()查询的是出度。
path.Both([predicatePath],[tags])
用法同上,既查询入度也查询出度。项目文档里说目前这个方法的效率相对来讲比较低,由于它是经过Or方法实现的。可是在须要的状况下,仍是颇有用的。
path.Is(node,[node..])
参数:node:一个或者多个node。
过滤出全部指向参数节点的路径。
举例:
1 // 从图中全部节点出发,找到follows指向bob的路径 2 // 结果显示三个路径指向bob (来自 alice, charlie and dani) 3 g.V().Out("follows").Is("bob")
path.Has(predicate,object)
参数:predicate:指明predicate,也就是哪类路径。
object:指向的节点
过滤出全部经过predicate指向object的节点
举例:
1 // 从全部节点开始,找到谁follows了。结果是 alice, charlie and dani 2 g.V().Has("follows", "bob") 3 // follows charlie的人之中,哪些人follows了fred。结果是 bob 4 g.V("charlie").Out("follows").Has("follows", "fred")
Tagging
path.Tag(tag) 简写为 path.As
参数:tag:为结果集的key赋予一个字符串。
为了保存你的工做,或者了解路径是怎么到达终点的,cayley提供了tags。
举例:
1 // 从全部节点开始,把他们保存到“start”中,找到全部有status的predicate,并返回结果 2 // 结果是 {"id": "cool_person", "start": "bob"}, {"id": "cool_person", "start": "greg"}, {"id": "cool_person", "start": "dani"} 3 g.V().Tag("start").Out("status")
path.Back(tag)
参数:tag:要跳回的在query里保存的以前的tag名
举例:
1 // 从全部节点出发,把它们保存到“start”中,找到有status的predicate的链接,而后跳回到“start”中,看看有谁follows了他们,返回结果 2 // 结果是: 3 // {"id": "alice", "start": "bob"}, 4 // {"id": "charlie", "start": "bob"}, 5 // {"id": "dani", "start": "bob"}, 6 // {"id": "charlie", "start": "dani"}, 7 // {"id": "dani", "start": "greg"} 8 g.V().Tag("start").Out("status").Back("start").In("follows")
path.Save(predicate,tag)
参数:predicate:predicate名
tag:一个tag名用来保存object节点
从当前节点开始做为subject,把它经过predicate链接的节点保存在key为tag指向的value里。
举例:
1 // 从 dani 和 bob 开始,查看他们follows了谁,并把结果保存在 “target”中 2 // 结果: 3 // {"id" : "dani", "target": "bob" }, 4 // {"id" : "dani", "target": "greg" }, 5 // {"id" : "bob", "target": "fred" }, 6 g.V("dani", "bob").Save("follows", "target")
Joining
path.Intersect(query) 简写为 path.And
path.Union(query) 简写为 path.Or
path.Except(query) 简写为 path.Difference
这三个放到一块儿,就是比较两个path对象中,取交集的数据、取并集的数据和取差集的数据。
参数都只有query对象。
举例:
1 var cFollows = g.V("charlie").Out("follows") 2 var dFollows = g.V("dani").Out("follows") 3 // 1. Intersect 4 // charlie follows的人 (bob and dani) 和 dani follows的人 (bob and greg) -- 返回 bob 5 cFollows.Intersect(dFollows) 6 // 或者相同的方式 7 g.V("charlie").Out("follows").And(g.V("dani").Out("follows")) 8 // 2. Union 9 // charlie (bob and dani) 或 dani (bob and greg) follows的人 -- 返回 bob (来自 charlie), bob (来自 dani), dani and greg. 10 cFollows.Union(dFollows) 11 // 3. Except 12 // 从charlie follows的人中,去掉dani follows的人-- 返回 dani 13 cFollows.Except(dFollows) 14 // 或者相同的方式 15 g.V("charlie").Out("follows").Except(g.V("dani").Out("follows"))
使用 Morphisms
path.Follow(morphism)
path.FollowR(morphism)
这两个放到一块儿,由于它们都用到了morphism path对象。
参数:morphism:一个态射路径。
有了graph.morphism,咱们能够准备一个可复用的path。
Follow是从morphism定义的路径,顺序查找节点,而FollowR是逆序查找节点的。
举例:
1 friendOfFriend = g.Morphism().Out("follows").Out("follows") 2 // 定义一个morphism为:从给定节点出发,它follows的节点所follows的节点。 3 // 查找charlie follows的人所follows的人里,谁的有status为cool_person 4 // 结果为 bob 和 greg 5 g.V("charlie").Follow(friendOfFriend).Has("status", "cool_person") 6 // 从全部节点出发,找到谁follows的人里,follows了status为cool_person的人 7 // 结果:emily,bob,charlie(来自 bob),charlie(来自greg) 8 g.V().Has("status", "cool_person").FollowR(friendOfFriend)
关于Query对象
query.All()
没有参数,返回值任意。执行结果是query里全部的数据。
query.GetLimit(size)
参数是size,和All()方法同样, 只不过加上了返回结果数量的限制。
query.ToArray()
没有参数,返回一个Array。
query.ToValue()
没有参数,返回一个字符串。和ToArray()方法同样,可是只返回一个结果,就像Limit(1)
query.TagArray()
和ToArray()同样,只不过返回的不是string数组,而是一个键值对数组。
query.TagValue()
和ToValue()同样,返回一个只包含一个tag-to-string的map。
query.ForEach(callback),query.ForEach(limit,callbaack) 简写为 query.Map
略。
关于web界面的可视化
当你使用http界面时,你会看到cayley提供了可视化效果的展现,一种是QueryShape,一种是Visualize。
QueryShape展现了你要查询的态射是什么样的,Visualize会以图形方式展现你的查询结果。在正确地输出图形以前,尤为是Visualize,须要确保查询结果的JSON里,有“target”和“source”这两个键。也就是说,咱们在查询的结果里,须要经过Tag,为结果添加标签。
举例:
1 var fOf = g.M().Out("follows").Out("follows").Tag("target") 2 g.V("charlie").Tag("source").Follow(fOf).All()
它返回的结果是这样的JSON,包含了“source”和“target”:
1 { 2 "result": [ 3 { 4 "id": "bob", 5 "source": "charlie", 6 "target": "bob" 7 }, 8 { 9 "id": "greg", 10 "source": "charlie", 11 "target": "greg" 12 }, 13 { 14 "id": "fred", 15 "source": "charlie", 16 "target": "fred" 17 } 18 ] 19 }
这样才能在web页面中,看到可视化的图形展现。
其中QueryShape以下:
Visualize以下:
以上就是我初步尝试cayley数据库的内容,在它项目的doc文件夹下,还有更多更详细的说明,感兴趣的读者能够看一看。
因为我的水平有限,若是哪里有错误或者你有更好的经验,欢迎在评论区留言,我会认真和你交流的。谢谢!