最近在用Golang做流程引擎,對於流程圖的存儲,我看到了Google的Cayley圖數據庫,感覺它可能會比較適合我的應用,於是便拿來用了用.
項目地址在這里:https://github.com/google/cayley
系統環境 : Windows 7
1. 安裝
安裝的過程項目文檔里寫的很明白,有兩種安裝方式:
- 直接下載二進制版本.解壓到你喜歡的位置.
- 從源碼安裝,遵照項目文檔一步一步來即可.
2. 使用
安裝完成之后,你會看到一個【cayley.exe】的文件,它就是我們要用的程序了。
1. 初始化數據庫
初始化數據庫要用到的命令是 init ,你可以使用兩種方式初始化,一種是命令行,一種是配置文件。
我們先來看命令行的形式:
- 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(可選)下列其中之一
-
- 空或者undifined:所有從這個節點出去的predicate。
- 一個字符串:從這個節點出去的predicate名字。
- 字符串列表:從這個節點出去的多個predicate名字。
- 一個query path對象:
tags(可選)下列其中之一
-
- 空或者undifined:沒有tags
- 一個字符串:向輸出集使用的指明predicate的標簽
- 字符串列表:添加多個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文件夾下,還有更多更詳細的說明,感興趣的讀者可以看一看。
由於個人水平有限,如果哪里有錯誤或者你有更好的經驗,歡迎在評論區留言,我會認真和你交流的。謝謝!