TinkerPop框架查詢Gremlin圖實(shí)現(xiàn)過程詳解
前言
本文記錄了筆者摸索圖數(shù)據(jù)庫過程中遇到的問題、一些思考(閑談)與實(shí)現(xiàn)思路。
做此記錄的目的是沉淀經(jīng)驗(yàn)、完善鞏固知識體系,同時(shí)以此為始,培養(yǎng)撰寫文本、輸出內(nèi)容的能力與習(xí)慣。
本文內(nèi)容源自筆者自身見識,僅為一家之言,不足之處望諸位批評指正。
基于筆者當(dāng)時(shí)的情況,對讀者做以下假設(shè):
- 了解圖數(shù)據(jù)庫相關(guān)知識。
- 想要實(shí)現(xiàn)支持 Gremlin 查詢的圖處理框架。
- 讀過 TinkerPop 文檔卻不知從何下手。
肇始于 TinkerPop 文檔
Apache TinkerPop™ is a graph computing framework for both graph databases (OLTP) and graph analytic systems (OLAP).
Apache TinkerPop 框架為圖數(shù)據(jù)庫 ( OLTP ) 和圖分析系統(tǒng) ( OLAP ) 提供了一套標(biāo)準(zhǔn)的操作接口 Gremlin 。
GQL 之于圖數(shù)據(jù)庫,如同 SQL 之于關(guān)系型數(shù)據(jù)庫。在 GQL 標(biāo)準(zhǔn)落地之前,圖查詢語言的事實(shí)標(biāo)準(zhǔn)大概只有 Gremlin 與 Cypher,至于 Sparql ,僅支持 RDF 格式數(shù)據(jù),不在更工程化的屬性圖模型討論范疇。
圖查詢語言 Gremlin 是 TinkerPop 框架為 圖服務(wù)用戶 ( User ) 提供的數(shù)據(jù)操作接口,而對于 圖服務(wù)提供商 ( Provider ) 來說,需要了解 TinkerPop 框架的屬性圖模型與接口。
TinkerPop 將其接口粗略劃分為 Structure 和 Process 兩部分。Structure 部分定義了圖的拓?fù)浣Y(jié)構(gòu)與功能,包括 Vertex、Edge、Property 等,由 圖服務(wù)提供商 ( Provider ) 實(shí)現(xiàn)其接口,以填充數(shù)據(jù);Process 部分定義了遍歷圖數(shù)據(jù)的 DSL ,并提供調(diào)用 Structure 接口的默認(rèn)實(shí)現(xiàn)。
Structure 接口結(jié)構(gòu)與實(shí)現(xiàn)思路
作為剛接觸 TinkerPop 沒多久的 Provider,只需實(shí)現(xiàn) Structure 接口即可完成 Gremlin 圖查詢功能的接入。
這也意味著沒有圖計(jì)算與事務(wù)功能,僅支持 Gremlin 查詢。
TinkerPop Provider 文檔中將 Provider 細(xì)分為:
- Graph System Provider
- Graph Database Provider
- Graph Processor Provider
- Graph Driver Provider
- Graph Language Provider
- Graph Plugin Provider
本文中的 Provider 僅代表 Graph Database Provider 。
同樣地,Structure 接口僅包括 Gremlin-Core 模塊 ( 源碼 structure/ 文件夾下 ) 的下述接口:
圖 1 Structure 接口
Graph 接口
Graph 接口在整個(gè) Structure 體系中具有核心地位,是整個(gè)圖服務(wù)的入口與出口。
TinkerPop 的 Gremlin 執(zhí)行過程依靠 Process 體系里的各種類 ( TraversalSource, Strategy, Traversal, Step, Traverser, etc. ) 實(shí)現(xiàn),最終調(diào)用 Graph 接口,從存儲(chǔ)層輸入輸出對應(yīng)數(shù)據(jù)。
該接口作為圖遍歷的入口,在 Gremlin 腳本執(zhí)行過程中位于起始位置,從存儲(chǔ)層獲取數(shù)據(jù)后,交給后續(xù)操作執(zhí)行進(jìn)一步處理。
按照慣例,用戶將自己的 Graph 實(shí)現(xiàn)類命名為 XXXGraph,如官方樣例提供的 TinkerGraph、Neo4jGraph 等。
public final class TinkerGraph implements Graph {...} public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {...}
又如 JanusGraph 源碼中的 Graph 繼承體系:
// StandardJanusGraph 繼承 JanusGraphBlueprintsGraph public class StandardJanusGraph extends JanusGraphBlueprintsGraph {...} // JanusGraphBlueprintsGraph 實(shí)現(xiàn) JanusGraph 接口 public abstract class JanusGraphBlueprintsGraph implements JanusGraph {...} // JanusGraph 接口繼承 Transaction 接口 public interface JanusGraph extends Transaction {...} // Transaction 接口繼承 Tinkerpop Graph 接口 public interface Transaction extends Graph, SchemaManager {...}
Tinkerpop Graph 接口提供了許多帶有默認(rèn)實(shí)現(xiàn)的方法,僅留下如下幾個(gè)方法需要 Provider 自行實(shí)現(xiàn):
添加節(jié)點(diǎn)方法 addVertex
Vertex addVertex(final Object... keyValues);
該方法對應(yīng)著 g.addV() 與 graph.addVertex() 的調(diào)用方式,即為 Gremlin 語言 addV 功能提供支持。
實(shí)現(xiàn)思路為將傳入的參數(shù)處理為自己的 Vertex 接口實(shí)現(xiàn)類的對象,將該對象持久化到存儲(chǔ)層,并返回該對象。
獲取節(jié)點(diǎn)方法 vertices
Iterator<Vertex> vertices(final Object... vertexIds);
該方法支撐 Gremlin 語言 g.V() 調(diào)用。
實(shí)現(xiàn)思路為根據(jù)傳入的節(jié)點(diǎn) ID 參數(shù),到存儲(chǔ)層查詢節(jié)點(diǎn)數(shù)據(jù),最終根據(jù)所查到的節(jié)點(diǎn)生成一個(gè) Vertex 接口迭代器,并將其作為返回值。
獲取邊方法 edges
Iterator<Edge> edges(final Object... edgeIds);
該方法支撐 Gremlin 語言 g.E() 調(diào)用。
實(shí)現(xiàn)思路類似 vertices 方法。
圖退出方法 close
void close() throws Exception;
該方法提供了圖服務(wù)退出時(shí),保存持久化層與關(guān)閉事務(wù)等工作的調(diào)用勾子。
讀取用戶配置方法 variables 與 configuration
Variables variables();
Configuration configuration();
這倆方法可以隨意應(yīng)付,不影響支撐 Gremlin 語言功能。
啟動(dòng)事務(wù)方法 tx
Transaction tx();
可在實(shí)現(xiàn)中直接拋出異常,表明該圖數(shù)據(jù)庫不支持事務(wù)功能,不影響支持 Gremlin 語言功能。
啟動(dòng)圖計(jì)算方法 compute
GraphComputer compute() throws IllegalArgumentException;
實(shí)現(xiàn)思路類似事務(wù)。
Element 接口
屬性圖模型的基礎(chǔ)類型接口,Vertex、Edge、VertexProperty 均繼承該接口,表示圖中的元素。
命名慣例同 Graph 接口,實(shí)現(xiàn)類為 XXXElement。
該接口聲明了圖元素共有的屬性與方法,其中部分方法具有默認(rèn)實(shí)現(xiàn),需 Provider 自行實(shí)現(xiàn)的方法包括:
- ID
Object id();
圖元素唯一標(biāo)識符 id 的 Getter 方法。
此處 id 類型為 Object,但在具體實(shí)現(xiàn)時(shí)又會(huì)根據(jù)實(shí)際需要將 id 類型限制為數(shù)字或字符串,又或是不限制類型。
- Label
String label();
圖元素標(biāo)簽 label 的 Getter 方法。
標(biāo)簽即為元素類型,在數(shù)據(jù)庫中被稱為 Meta,在知識圖譜中被稱為本體/概念。
TinkerPop 屬性圖模型似乎僅支持單標(biāo)簽,而 Neo4j 屬性圖模型可支持多標(biāo)簽,這點(diǎn)在 cypher-to-gremlin 項(xiàng)目的解析器中有所體現(xiàn)。
- Graph
Graph graph();
圖元素所屬的圖實(shí)例的 Getter 方法。
此處暗示了 Element 實(shí)現(xiàn)類僅為內(nèi)存中的對象,即數(shù)據(jù)庫中的外模式,并非持久層對象。因此,要在 Graph 接口實(shí)現(xiàn)類的 vertices()、edges()、addVertex() 中,為相應(yīng)的圖元素設(shè)置 graph 屬性,供后續(xù)遍歷方法調(diào)用。
該方法支撐了其他關(guān)聯(lián)查詢接口,比如從已有圖元素出發(fā),繼續(xù)查詢相關(guān)聯(lián)的節(jié)點(diǎn)或邊的方法。
g.V().has('person', 'name', 'marko').out("knows")
在該 Gremlin 語句中 g.V().has() 從 Graph 接口中獲得了起始節(jié)點(diǎn),接著使用 out() 方法請求該節(jié)點(diǎn)的出邊關(guān)聯(lián)節(jié)點(diǎn),而 out() 方法的實(shí)現(xiàn)中調(diào)用了該 graph() 方法。
此外,還需注意,對于一個(gè)圖數(shù)據(jù)庫來說,應(yīng)該支持多個(gè)圖實(shí)例管理,這意味著同一個(gè) Gremlin 語句,目標(biāo) graph 不同,得到的結(jié)果也不同,該方法返回的對象也是不同的。
- 添加屬性方法 property
<V> Property<V> property(final String key, final V value);
將屬性的 Key,Value 傳遞給當(dāng)前圖元素。
實(shí)現(xiàn)思路為根據(jù)傳入的參數(shù)構(gòu)建自己的 Property 對象,接著對圖元素的內(nèi)模式做相應(yīng)修改并持久化,最后返回該 Property 對象。
獲取屬性方法 properties
<V> Iterator<? extends Property<V>> properties(final String... propertyKeys);
根據(jù)傳入的屬性 Key 列表,從當(dāng)前圖元素的屬性 Map 中過濾出相應(yīng)的 Property 列表。
元素刪除方法 remove
void remove();
可在實(shí)現(xiàn)中拋出異常,表示當(dāng)前圖數(shù)據(jù)庫不支持刪除數(shù)據(jù)。
或者直接返回,假裝完成了刪除操作。
Vertex 接口
圖模型中的節(jié)點(diǎn)元素,該接口繼承 Element 接口,表示圖數(shù)據(jù)庫的節(jié)點(diǎn)外模式。
命名慣例同 Graph 接口,實(shí)現(xiàn)類為 XXXVertex,該類需繼承上述 XXXElement 類。
在接口設(shè)計(jì)上,TinkerPop 要求每個(gè) Vertex 實(shí)例可以自身為起點(diǎn),找到相關(guān)聯(lián)的入邊 ( Incoming Edges ) 和出邊 ( Outgoing Edges),以及連邊的另一端 Vertex 實(shí)例。
此設(shè)計(jì)天然適用于 無索引近鄰 式的圖處理結(jié)構(gòu)。因此在實(shí)現(xiàn) Vertex 接口時(shí),可考慮在存儲(chǔ)層之上構(gòu)建該處理結(jié)構(gòu),用以加速查詢。
該接口聲明了一些需要 Provider 實(shí)現(xiàn)的重要方法,這些方法在 Gremlin 查詢過程中起到了基石的作用。
添加出邊 addEdge
Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues);
實(shí)現(xiàn)思路為根據(jù)傳入的邊標(biāo)簽 label 和邊屬性 keyValues 構(gòu)建 Edge 實(shí)例,并將其持久化。
如果實(shí)現(xiàn)了無索引近鄰結(jié)構(gòu),需進(jìn)一步更新與該邊相關(guān)聯(lián)的兩點(diǎn)的索引內(nèi)容。
如果實(shí)現(xiàn)了屬性索引,還需為相應(yīng)屬性值與該 Edge 構(gòu)建索引。
添加屬性 property
<V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues);
參數(shù) cardinality 表示屬性基數(shù),包括 single、list、set。
參數(shù) key、value 無需多言。
此處需要理解 TinkerPop 中 VertexProperty 與 Property 的差異。
TinkerPop 將這種帶有屬性的屬性表示為 VertexProperty,歸屬于 Element。而僅有屬性值的屬性表示為 Property。VertexProperty 的屬性也是 Property 對象。
參數(shù) keyValues 表示了該屬性的屬性,舉個(gè)例子:張三 ( Vertex ) 的學(xué)歷 ( Key ) 有小學(xué)、初中、高中 ( Value ),而每個(gè)學(xué)歷值都有入學(xué)時(shí)間、畢業(yè)時(shí)間、學(xué)校名稱等屬性 ( keyValues ) 。
奇怪的是,TinkerPop 將 Edge 的屬性表示為 Property,Property 對象沒有下一級屬性,這點(diǎn)可在 addEdge 方法與 Edge 接口中體會(huì)到。同時(shí),與 Edge 有關(guān)的 Gremlin 處理步驟均無法設(shè)置屬性的屬性。
如此區(qū)分 Vertex 與 Edge 的屬性,總讓人覺得缺少對稱的美感,也不兼容實(shí)際建模的需求。如果想要修改此行為,又將不可避免地入侵 TinkerPop 設(shè)計(jì)中未暴露接口的部分。若把屬性的屬性用 Map 存儲(chǔ)或序列化為字符串作為 Edge 的屬性,似乎也有不少問題,至少在標(biāo)準(zhǔn) Gremlin 語法上無法查詢 Edge 的屬性的屬性。
獲取相鄰邊 edges
Iterator<Edge> edges(final Direction direction, final String... edgeLabels);
實(shí)現(xiàn)思路:將參數(shù)中的方向和邊標(biāo)簽作為過濾條件,從無索引近鄰結(jié)構(gòu)或存儲(chǔ)層中查詢相關(guān)邊。
獲取相鄰節(jié)點(diǎn) vertices
Iterator<Vertex> vertices(final Direction direction, final String... edgeLabels);
實(shí)現(xiàn)思路類似 edges 方法。
獲取節(jié)點(diǎn)屬性 properties
<V> Iterator<VertexProperty<V>> properties(final String... propertyKeys);
實(shí)現(xiàn)思路為從節(jié)點(diǎn)的詳細(xì)信息中獲取屬性列表,然后根據(jù)參數(shù) propertyKeys 過濾出對應(yīng)屬性值。
Edge 接口
圖模型中的邊元素,該接口繼承 Element 接口,表示圖數(shù)據(jù)庫的邊外模式。
命名慣例同 Graph 接口,實(shí)現(xiàn)類為 XXXEdge,該類需繼承上述 XXXElement 類。
在接口設(shè)計(jì)上,TinkerPop 要求每個(gè) Edge 實(shí)例可以自身為起點(diǎn),找到相關(guān)聯(lián)的起始節(jié)點(diǎn)和終止節(jié)點(diǎn)。
該接口提供了一些方法的默認(rèn)實(shí)現(xiàn),僅需 Provider 提供以下兩個(gè)方法的具體實(shí)現(xiàn)。
獲取相關(guān)節(jié)點(diǎn) vertices
Iterator<Vertex> vertices(final Direction direction);
實(shí)現(xiàn)思路為根據(jù)當(dāng)前 Edge 的信息以及傳入的方向參數(shù),從索引中獲存儲(chǔ)層查詢相關(guān)聯(lián)節(jié)點(diǎn)。
獲取相關(guān)屬性 properties
<V> Iterator<Property<V>> properties(final String... propertyKeys);
實(shí)現(xiàn)思路類似獲取節(jié)點(diǎn)屬性方法。
Property 接口
圖模型中的屬性元素,表示圖數(shù)據(jù)庫的屬性外模式。
命名慣例同 Graph 接口,實(shí)現(xiàn)類為 XXXProperty 。
A Property denotes a key/value pair associated with an Edge.
如上文討論的那樣,TinkerPop 在其文檔中明確寫道:屬性是與邊相關(guān)的 K/V 對,Key 只能是 String 類型,Value 只能是 Java 類型。
該接口提供了一些方法的默認(rèn)實(shí)現(xiàn),僅需 Provider 提供以下方法的具體實(shí)現(xiàn)。
獲取屬性鍵 key
String key();
該方法被調(diào)用時(shí),往往已經(jīng)獲取了 Provider 實(shí)現(xiàn)的 XXXProperty 對象,只需將該對象 key 值返回即可。
獲取屬性值 value
V value() throws NoSuchElementException;
實(shí)現(xiàn)思路同上。
獲取關(guān)聯(lián)對象 element
Element element();
實(shí)現(xiàn)思路同上。
判斷屬性值是否存在
boolean isPresent();
當(dāng)前 XXXProperty 的 value 屬性非空時(shí)返回 true,否則返回 false。
刪除當(dāng)前屬性 remove
void remove();
可在實(shí)現(xiàn)中拋出異常,表示當(dāng)前圖數(shù)據(jù)庫不支持刪除數(shù)據(jù)。
或者直接返回,假裝完成了刪除操作。
VertexProperty 接口
圖模型中的可攜帶屬性的節(jié)點(diǎn)屬性元素,表示圖數(shù)據(jù)庫的屬性外模式。
命名慣例同 Graph 接口,實(shí)現(xiàn)類為 XXXVertexProperty,該類需繼承 XXXElement 類。
A VertexProperty is similar to a Property in that it denotes a key/value pair associated with an Vertex, however it is different in the sense that it also represents an entity that it is an Element that can have properties of its own.
如上文討論的那樣,TinkerPop 在其文檔中明確寫道:節(jié)點(diǎn)屬性是與節(jié)點(diǎn)相關(guān)的 K/V 對,同時(shí),節(jié)點(diǎn)屬性也是圖元素的一種,可以攜帶自己的屬性。
VertexProperty 接口提供了一些方法的默認(rèn)實(shí)現(xiàn),但由于該接口繼承了 Property 接口,因此需要 Provider 提供上述 Propery 接口方法和以下方法的具體實(shí)現(xiàn)。
獲取所屬節(jié)點(diǎn) element
Vertex element();
該方法覆寫了 Property 接口的 element 方法,返回當(dāng)前節(jié)點(diǎn)屬性所屬的 Vertex 對象。
獲取屬性 properties
<U> Iterator<Property<U>> properties(final String... propertyKeys);
返回當(dāng)前節(jié)點(diǎn)屬性的屬性。
實(shí)現(xiàn)自己的 Structure
讀者可參照 TinkerPop 給的 TinkerGraph 源碼和上一節(jié)所講的思路去嘗試實(shí)現(xiàn)自己的 Structure 體系。
圖 2 TinkerGraph Structure
更進(jìn)一步
當(dāng)讀者完成上述實(shí)踐,成功在自己的 Graph 上執(zhí)行 Gremlin 查詢后,需要認(rèn)識到 TinkerGraph 中的圖數(shù)據(jù)全在內(nèi)存中,沒有存儲(chǔ)層,查詢索引也不太正常,其結(jié)構(gòu)與真實(shí)圖數(shù)據(jù)庫相比缺少代表性。
TinkerPop 源碼中的另外幾個(gè) Graph 實(shí)現(xiàn)例子,如 neo4jGraph、hadoopGraph 等,均有其各自的代表性,但仍不能覆蓋完整的圖數(shù)據(jù)庫功能。
想要了解更多圖數(shù)據(jù)庫實(shí)現(xiàn)細(xì)節(jié),可通過閱讀 JanusGraph 、NebulaGraph (C++) 源碼來學(xué)習(xí)相關(guān)知識。
以上就是TinkerPop框架查詢Gremlin圖實(shí)現(xiàn)過程詳解的詳細(xì)內(nèi)容,更多關(guān)于TinkerPop框架查詢Gremlin圖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Flink同步Kafka數(shù)據(jù)到ClickHouse分布式表
這篇文章主要為大家介紹了Flink同步Kafka數(shù)據(jù)到ClickHouse分布式表實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Linux下實(shí)現(xiàn)OpenGauss數(shù)據(jù)庫遠(yuǎn)程連接的教程
openGauss是一款開源關(guān)系型數(shù)據(jù)庫管理系統(tǒng),采用木蘭寬松許可證v2發(fā)行,本文主要為大家詳細(xì)介紹了如何在Linux環(huán)境下實(shí)現(xiàn)OpenGauss數(shù)據(jù)庫遠(yuǎn)程連接,需要的可以參考下2023-09-09sql注入報(bào)錯(cuò)之注入原理實(shí)例解析
所謂報(bào)錯(cuò)注入,就是通過運(yùn)行SQL查詢語句回顯查詢結(jié)果,下面這篇文章主要給大家介紹了關(guān)于sql注入報(bào)錯(cuò)之注入原理的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06Access和SQL Server里面的SQL語句的不同之處
做了一個(gè)Winform的營養(yǎng)測量軟件,來回的搗騰著Access數(shù)據(jù)庫,還是那幾句增刪改查,不過用多了,發(fā)現(xiàn)Access數(shù)據(jù)庫下的SQL語句和SQL Server下正宗的SQL還有有很大的不同。2009-12-12設(shè)置CA證書來強(qiáng)化PostgreSQL的安全性的教程
這篇文章主要介紹了設(shè)置CA證書來強(qiáng)化PostgreSQL的安全性的教程,主要用到了CA私鑰,需要的朋友可以參考下2015-04-04淺談一下數(shù)據(jù)庫系統(tǒng)的發(fā)展與組成
這篇文章主要介紹了淺談一下數(shù)據(jù)庫系統(tǒng)的發(fā)展與組成,數(shù)據(jù)庫系統(tǒng),指在計(jì)算機(jī)系統(tǒng)中引入數(shù)據(jù)庫后的系統(tǒng),一般由數(shù)據(jù)庫、數(shù)據(jù)庫管理系統(tǒng)、應(yīng)用系統(tǒng)、數(shù)據(jù)庫管理員(DBA)構(gòu)成,本文就數(shù)據(jù)庫的發(fā)展展開詳細(xì)講解2023-07-07mssql數(shù)據(jù)同步實(shí)現(xiàn)數(shù)據(jù)復(fù)制的步驟
需要用到mssql數(shù)據(jù)同步的朋友可以參考本文和上一篇文章2008-09-09