
Gremlin是janusgraph的查詢語言,用來獲取/變更圖數(shù)據(jù)。Gremlin是一個面向path的語言,能夠簡單快速的完成圖遍歷和變化操作。Gremlin是一個功能性語言,因此遍歷操作被聲明到類path的表達式表單。例如,from Hercules, traverse to his father and then his father’s father and return the grandfather’s name。
Gremlin是Apache TinkerPop的組件。它獨立于janusgraph發(fā)展,并且被支持于大多數(shù)圖數(shù)據(jù)庫。建在janusgraph上的應(yīng)用程序通過Gremlin查詢語言,用戶避免被發(fā)行商鎖在一個圖數(shù)據(jù)庫上。
這章是Gremlin查詢語言的簡要概述。更多信息可以參考以下資源:
- Complete Gremlin Manual: Gremlin全部步驟的手冊.
- Gremlin Console Tutorial: 學(xué)習(xí)如何用Gremlin Console高效圖遍歷,和交互式圖分析.
- Practical Gremlin Book: 圖數(shù)據(jù)庫用戶和Gremlin查詢語言的起步教程。
- Gremlin Recipes: Gremlin的最佳實踐集合和常見的圖遍歷語法。
- Gremlin Language Drivers: 用不同的語言連接到Gremlin server,如 Go, JavaScript, .NET/C#, PHP, Python, Ruby, Scala, and TypeScript.
- Gremlin Language Variants: 學(xué)習(xí)如何內(nèi)嵌Gremlin到一個主機編程語言。
- Gremlin for SQL developers: 學(xué)習(xí)用SQL查詢數(shù)據(jù),Gremlin的經(jīng)典語法。
6.1 圖遍歷的介紹
一次Gremlin查詢是一串從左到右的操作/函數(shù)鏈。下面是一個上帝圖里舉過的祖父查詢例子:
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
上面的查詢可以分成以下步驟:
- g: 當(dāng)前graph。
- V: graph中所有頂點。
- has('name', 'hercules'): 過濾出頂點有屬性name = "hercules" (這里只有一個)。
- out('father'): 從Hercules遍歷出邊(outgoing edge)為father的頂點。
- out('father'): 從Hercules的father (Jupiter)遍歷出邊為father的頂點。
- name: 拿出頂點的name屬性的value。
放到一起,這些步驟構(gòu)成了類似遍歷查詢的路徑。每一步都能分解且得到結(jié)果。當(dāng)構(gòu)造大型、復(fù)雜的查詢鏈,這種圖遍歷/查詢的風(fēng)格是很有用的。
gremlin> g
==>graphtraversalsource[janusgraph[cql:127.0.0.1], standard]
gremlin> g.V().has('name', 'hercules')
==>v[24]
gremlin> g.V().has('name', 'hercules').out('father')
==>v[16]
gremlin> g.V().has('name', 'hercules').out('father').out('father')
==>v[20]
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
正常情況,通??吹椒祷氐膶傩愿?,而不是分配的id。
gremlin> g.V().has('name', 'hercules').values('name')
==>hercules
gremlin> g.V().has('name', 'hercules').out('father').values('name')
==>jupiter
gremlin> g.V().has('name', 'hercules').out('father').out('father').values('name')
==>saturn
下面例子顯示了完整的father家族樹。這個復(fù)雜些的例子說明了這個語言的靈活性和便利性。熟練掌握Gremlin使得用戶使用junasgraph圖結(jié)構(gòu)如魚得水。
gremlin> g.V().has('name', 'hercules').repeat(out('father')).emit().values('name')
==>jupiter
==>saturn
下面是更多圖遍歷例子:
gremlin> hercules = g.V().has('name', 'hercules').next()
==>v[1536]
gremlin> g.V(hercules).out('father', 'mother').label()
==>god
==>human
gremlin> g.V(hercules).out('battled').label()
==>monster
==>monster
==>monster
gremlin> g.V(hercules).out('battled').valueMap()
==>{name=nemean}
==>{name=hydra}
==>{name=cerberus}
每一步(.分開)是一個函數(shù),操作前一步結(jié)果的對象,Gremlin有很多步驟Gremlin steps。簡單的改變一個步驟或步驟的順序,會執(zhí)行不同的遍歷語義,下面的例子是和Hercules打過同樣的怪獸,但不是Hercules的所有人的name。(類似戰(zhàn)友、盟友)
給定的上帝圖只有一個和怪獸戰(zhàn)斗的,所以這里增加一個戰(zhàn)士到圖里。
gremlin> theseus = graph.addVertex('human')
==>v[3328]
gremlin> theseus.property('name', 'theseus')
==>null
gremlin> cerberus = g.V().has('name', 'cerberus').next()
==>v[2816]
gremlin> battle = theseus.addEdge('battled', cerberus, 'time', 22)
==>e[7eo-2kg-iz9-268][3328-battled->2816]
gremlin> battle.values('time')
==>22
當(dāng)增加頂點時,頂點的label是選填。增加邊時label必須提供。頂點或邊都可以設(shè)置kv對的屬性。當(dāng)一個屬性key的種類設(shè)置為SET或LIST,這個key的屬性每一個必須用addProperty增加到頂點。
gremlin> g.V(hercules).as('h').out('battled').in('battled').where(neq('h')).values('name')
==>theseus
這個例子有4個鏈的函數(shù),out, in, except, 和values,每個函數(shù)的輸入輸出類型如下表,V是頂點,U是Object,V是U的子集。
- out: V -> V
- in: V -> V
- except: U -> U
- values: V -> U
當(dāng)把函數(shù)連起來,輸入類型必須等于輸出類型,U匹配所有。
注意
這樣的Gremlin說的是在Gremlin
Console用的Gremlin-Groovy。Gremlin也有其他的語言支持drivers,variants。
6.2 執(zhí)行遍歷
Gremlin Console的一個方便的特性是它自動生成所有結(jié)果,從終端執(zhí)行查詢。它在REPL環(huán)境工作很好,返回String類型的結(jié)果。當(dāng)你過渡到Gremlin應(yīng)用程序,理解如何明確的執(zhí)行遍歷很重要,因為不會自動的執(zhí)行。下面是常用的方法來執(zhí)行遍歷:
- iterate() - 不期待結(jié)果或可以被忽略。
- next() - 拿到一個結(jié)果,確保用hasNext()檢查。
- next(int n) - 拿到n個結(jié)果,確保用hasNext()檢查。
- toList() - 獲得所有結(jié)果作為list,如果沒有則返回空的list。
下面是一端java代碼說明這個概念:
Traversal t = g.V().has("name", "pluto"); // Define a traversal
// Note the traversal is not executed/iterated yet
Vertex pluto = null;
if (t.hasNext()) { // Check if results are available
pluto = g.V().has("name", "pluto").next(); // Get one result
g.V(pluto).drop().iterate(); // Execute a traversal to drop pluto from graph
}
// Note the traversal can be cloned for reuse
Traversal tt = t.asAdmin().clone();
if (tt.hasNext()) {
System.err.println("pluto was not dropped!");
}
List<Vertex> gods = g.V().hasLabel("god").toList(); // Find all the gods