首頁 > 軟體

TinkerPop框架查詢Gremlin圖實現過程詳解

2022-11-27 14:01:42

前言

本文記錄了筆者摸索圖資料庫過程中遇到的問題、一些思考(閒談)與實現思路。

做此記錄的目的是沉澱經驗、完善鞏固知識體系,同時以此為始,培養撰寫文字、輸出內容的能力與習慣。

本文內容源自筆者自身見識,僅為一家之言,不足之處望諸位批評指正。

基於筆者當時的情況,對讀者做以下假設:

  • 瞭解圖資料庫相關知識。
  • 想要實現支援 Gremlin 查詢的圖處理框架。
  • 讀過 TinkerPop 檔案卻不知從何下手。

肇始於 TinkerPop 檔案

Apache TinkerPop™ is a graph computing framework for both graph databases (OLTP) and graph analytic systems (OLAP).

Apache TinkerPop 框架為圖資料庫 ( OLTP ) 和圖分析系統 ( OLAP ) 提供了一套標準的操作介面 Gremlin 。

GQL 之於圖資料庫,如同 SQL 之於關係型資料庫。在 GQL 標準落地之前,圖查詢語言的事實標準大概只有 Gremlin 與 Cypher,至於 Sparql ,僅支援 RDF 格式資料,不在更工程化的屬性圖模型討論範疇。

圖查詢語言 Gremlin 是 TinkerPop 框架為 圖服務使用者 ( User ) 提供的資料操作介面,而對於 圖服務提供商 ( Provider ) 來說,需要了解 TinkerPop 框架的屬性圖模型與介面。

TinkerPop 將其介面粗略劃分為 Structure 和 Process 兩部分。Structure 部分定義了圖的拓撲結構與功能,包括 Vertex、Edge、Property 等,由 圖服務提供商 ( Provider ) 實現其介面,以填充資料;Process 部分定義了遍歷圖資料的 DSL ,並提供呼叫 Structure 介面的預設實現。

Structure 介面結構與實現思路

作為剛接觸 TinkerPop 沒多久的 Provider,只需實現 Structure 介面即可完成 Gremlin 圖查詢功能的接入。

這也意味著沒有圖計算與事務功能,僅支援 Gremlin 查詢。

TinkerPop Provider 檔案中將 Provider 細分為:

  • 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 介面在整個 Structure 體系中具有核心地位,是整個圖服務的入口與出口。

TinkerPop 的 Gremlin 執行過程依靠 Process 體系裡的各種類 ( TraversalSource, Strategy, Traversal, Step, Traverser, etc. ) 實現,最終呼叫 Graph 介面,從儲存層輸入輸出對應資料。

該介面作為圖遍歷的入口,在 Gremlin 指令碼執行過程中位於起始位置,從儲存層獲取資料後,交給後續操作執行進一步處理。

按照慣例,使用者將自己的 Graph 實現類命名為 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 實現 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 介面提供了許多帶有預設實現的方法,僅留下如下幾個方法需要 Provider 自行實現:

新增節點方法 addVertex

Vertex addVertex(final Object... keyValues);

該方法對應著 g.addV() 與 graph.addVertex() 的呼叫方式,即為 Gremlin 語言 addV 功能提供支援。

實現思路為將傳入的引數處理為自己的 Vertex 介面實現類的物件,將該物件持久化到儲存層,並返回該物件。

獲取節點方法 vertices

Iterator<Vertex> vertices(final Object... vertexIds);

該方法支撐 Gremlin 語言 g.V() 呼叫。

實現思路為根據傳入的節點 ID 引數,到儲存層查詢節點資料,最終根據所查到的節點生成一個 Vertex 介面迭代器,並將其作為返回值。

獲取邊方法 edges

Iterator<Edge> edges(final Object... edgeIds);

該方法支撐 Gremlin 語言 g.E() 呼叫。

實現思路類似 vertices 方法。

圖退出方法 close

void close() throws Exception;

該方法提供了圖服務退出時,儲存持久化層與關閉事務等工作的呼叫勾子。

讀取使用者設定方法 variables 與 configuration

Variables variables();

Configuration configuration();

這倆方法可以隨意應付,不影響支撐 Gremlin 語言功能。

啟動事務方法 tx

Transaction tx();

可在實現中直接丟擲異常,表明該圖資料庫不支援事務功能,不影響支援 Gremlin 語言功能。

啟動圖計算方法 compute

GraphComputer compute() throws IllegalArgumentException;

實現思路類似事務。

Element 介面

屬性圖模型的基礎型別介面,Vertex、Edge、VertexProperty 均繼承該介面,表示圖中的元素。

命名慣例同 Graph 介面,實現類為 XXXElement。

該介面宣告了圖元素共有的屬性與方法,其中部分方法具有預設實現,需 Provider 自行實現的方法包括:

  • ID

Object id();

圖元素唯一識別符號 id 的 Getter 方法。

此處 id 型別為 Object,但在具體實現時又會根據實際需要將 id 型別限制為數位或字串,又或是不限制型別。

  • Label

String label();

圖元素標籤 label 的 Getter 方法。

標籤即為元素型別,在資料庫中被稱為 Meta,在知識圖譜中被稱為本體/概念。

TinkerPop 屬性圖模型似乎僅支援單標籤,而 Neo4j 屬性圖模型可支援多標籤,這點在 cypher-to-gremlin 專案的解析器中有所體現。

  • Graph

Graph graph();

圖元素所屬的圖範例的 Getter 方法。

此處暗示了 Element 實現類僅為記憶體中的物件,即資料庫中的外模式,並非持久層物件。因此,要在 Graph 介面實現類的 vertices()、edges()、addVertex() 中,為相應的圖元素設定 graph 屬性,供後續遍歷方法呼叫。

該方法支撐了其他關聯查詢介面,比如從已有圖元素出發,繼續查詢相關聯的節點或邊的方法。

g.V().has('person', 'name', 'marko').out("knows")

在該 Gremlin 語句中 g.V().has() 從 Graph 介面中獲得了起始節點,接著使用 out() 方法請求該節點的出邊關聯節點,而 out() 方法的實現中呼叫了該 graph() 方法。

此外,還需注意,對於一個圖資料庫來說,應該支援多個圖範例管理,這意味著同一個 Gremlin 語句,目標 graph 不同,得到的結果也不同,該方法返回的物件也是不同的。

  • 新增屬性方法 property

<V> Property<V> property(final String key, final V value);

將屬性的 Key,Value 傳遞給當前圖元素。

實現思路為根據傳入的引數構建自己的 Property 物件,接著對圖元素的內模式做相應修改並持久化,最後返回該 Property 物件。

獲取屬性方法 properties

<V> Iterator<? extends Property<V>> properties(final String... propertyKeys);

根據傳入的屬性 Key 列表,從當前圖元素的屬性 Map 中過濾出相應的 Property 列表。

元素刪除方法 remove

void remove();

可在實現中丟擲異常,表示當前圖資料庫不支援刪除資料。

或者直接返回,假裝完成了刪除操作。

Vertex 介面

圖模型中的節點元素,該介面繼承 Element 介面,表示圖資料庫的節點外模式。

命名慣例同 Graph 介面,實現類為 XXXVertex,該類需繼承上述 XXXElement 類。

在介面設計上,TinkerPop 要求每個 Vertex 範例可以自身為起點,找到相關聯的入邊 ( Incoming Edges ) 和出邊 ( Outgoing Edges),以及連邊的另一端 Vertex 範例。

此設計天然適用於 無索引近鄰 式的圖處理結構。因此在實現 Vertex 介面時,可考慮在儲存層之上構建該處理結構,用以加速查詢。

該介面宣告了一些需要 Provider 實現的重要方法,這些方法在 Gremlin 查詢過程中起到了基石的作用。

新增出邊 addEdge

Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues);

實現思路為根據傳入的邊標籤 label 和邊屬性 keyValues 構建 Edge 範例,並將其持久化。

如果實現了無索引近鄰結構,需進一步更新與該邊相關聯的兩點的索引內容。

如果實現了屬性索引,還需為相應屬性值與該 Edge 構建索引。

新增屬性 property

<V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues);

引數 cardinality 表示屬性基數,包括 single、list、set。

引數 key、value 無需多言。

此處需要理解 TinkerPop 中 VertexProperty 與 Property 的差異。

TinkerPop 將這種帶有屬性的屬性表示為 VertexProperty,歸屬於 Element。而僅有屬性值的屬性表示為 Property。VertexProperty 的屬性也是 Property 物件。

引數 keyValues 表示了該屬性的屬性,舉個例子:張三 ( Vertex ) 的學歷 ( Key ) 有小學、初中、高中 ( Value ),而每個學歷值都有入學時間、畢業時間、學校名稱等屬性 ( keyValues ) 。

奇怪的是,TinkerPop 將 Edge 的屬性表示為 Property,Property 物件沒有下一級屬性,這點可在 addEdge 方法與 Edge 介面中體會到。同時,與 Edge 有關的 Gremlin 處理步驟均無法設定屬性的屬性。

如此區分 Vertex 與 Edge 的屬性,總讓人覺得缺少對稱的美感,也不相容實際建模的需求。如果想要修改此行為,又將不可避免地入侵 TinkerPop 設計中未暴露介面的部分。若把屬性的屬性用 Map 儲存或序列化為字串作為 Edge 的屬性,似乎也有不少問題,至少在標準 Gremlin 語法上無法查詢 Edge 的屬性的屬性。

獲取相鄰邊 edges

Iterator<Edge> edges(final Direction direction, final String... edgeLabels);

實現思路:將引數中的方向和邊標籤作為過濾條件,從無索引近鄰結構或儲存層中查詢相關邊。

獲取相鄰節點 vertices

Iterator<Vertex> vertices(final Direction direction, final String... edgeLabels);

實現思路類似 edges 方法。

獲取節點屬性 properties

<V> Iterator<VertexProperty<V>> properties(final String... propertyKeys);

實現思路為從節點的詳細資訊中獲取屬性列表,然後根據引數 propertyKeys 過濾出對應屬性值。

Edge 介面

圖模型中的邊元素,該介面繼承 Element 介面,表示圖資料庫的邊外模式。

命名慣例同 Graph 介面,實現類為 XXXEdge,該類需繼承上述 XXXElement 類。

在介面設計上,TinkerPop 要求每個 Edge 範例可以自身為起點,找到相關聯的起始節點和終止節點。

該介面提供了一些方法的預設實現,僅需 Provider 提供以下兩個方法的具體實現。

獲取相關節點 vertices

Iterator<Vertex> vertices(final Direction direction);

實現思路為根據當前 Edge 的資訊以及傳入的方向引數,從索引中獲儲存層查詢相關聯節點。

獲取相關屬性 properties

<V> Iterator<Property<V>> properties(final String... propertyKeys);

實現思路類似獲取節點屬性方法。

Property 介面

圖模型中的屬性元素,表示圖資料庫的屬性外模式。

命名慣例同 Graph 介面,實現類為 XXXProperty 。

A Property denotes a key/value pair associated with an Edge.

如上文討論的那樣,TinkerPop 在其檔案中明確寫道:屬性是與邊相關的 K/V 對,Key 只能是 String 型別,Value 只能是 Java 型別。

該介面提供了一些方法的預設實現,僅需 Provider 提供以下方法的具體實現。

獲取屬性鍵 key

String key();

該方法被呼叫時,往往已經獲取了 Provider 實現的 XXXProperty 物件,只需將該物件 key 值返回即可。

獲取屬性值 value

V value() throws NoSuchElementException;

實現思路同上。

獲取關聯物件 element

Element element();

實現思路同上。

判斷屬性值是否存在

boolean isPresent();

當前 XXXProperty 的 value 屬性非空時返回 true,否則返回 false。

刪除當前屬性 remove

void remove();

可在實現中丟擲異常,表示當前圖資料庫不支援刪除資料。

或者直接返回,假裝完成了刪除操作。

VertexProperty 介面

圖模型中的可攜帶屬性的節點屬性元素,表示圖資料庫的屬性外模式。

命名慣例同 Graph 介面,實現類為 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 在其檔案中明確寫道:節點屬性是與節點相關的 K/V 對,同時,節點屬性也是圖元素的一種,可以攜帶自己的屬性。

VertexProperty 介面提供了一些方法的預設實現,但由於該介面繼承了 Property 介面,因此需要 Provider 提供上述 Propery 介面方法和以下方法的具體實現。

獲取所屬節點 element

Vertex element();

該方法覆寫了 Property 介面的 element 方法,返回當前節點屬性所屬的 Vertex 物件。

獲取屬性 properties

<U> Iterator<Property<U>> properties(final String... propertyKeys);

返回當前節點屬性的屬性。

實現自己的 Structure

讀者可參照 TinkerPop 給的 TinkerGraph 原始碼和上一節所講的思路去嘗試實現自己的 Structure 體系。

圖 2 TinkerGraph Structure

更進一步

當讀者完成上述實踐,成功在自己的 Graph 上執行 Gremlin 查詢後,需要認識到 TinkerGraph 中的圖資料全在記憶體中,沒有儲存層,查詢索引也不太正常,其結構與真實圖資料庫相比缺少代表性。

TinkerPop 原始碼中的另外幾個 Graph 實現例子,如 neo4jGraph、hadoopGraph 等,均有其各自的代表性,但仍不能覆蓋完整的圖資料庫功能。

想要了解更多圖資料庫實現細節,可通過閱讀 JanusGraph 、NebulaGraph (C++) 原始碼來學習相關知識。

以上就是TinkerPop框架查詢Gremlin圖實現過程詳解的詳細內容,更多關於TinkerPop框架查詢Gremlin圖的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com