动态

详情 返回 返回

當 GraphQL 遇上圖數據庫,便有了更方便查詢數據的方式 - 动态 详情

人之初,性本鴿。

大家好,我叫儲惠龍(實名上網),你可以叫我小龍人,00 後一枚。目前從事後端開發工作。

今天給大家帶來一個簡單的為 NebulaGraph 提供 GraphQL 查詢支持的 DEMO,為什麼是簡單的,因為本來想完成更多工作再給大家介紹的,但是上個月太忙加上下個月更忙,但是我又很想白嫖一下 Nebula 官方的獎品,所以就趕緊端上來了。

pic1|284x177

體驗 NebulaGraphQL

先上項目地址:https://github.com/Dragonchu/NebulaGraphQL

GraphQL 是什麼

先簡單介紹一下 GraphQL,https://graphql.cn/ 詳細的信息官方介紹得都很清晰。説一下我的理解,GraphQL 並不是對標 Cypher 這種查詢語言,而是對標 REST 的一種 API 設計風格

所以,嚴格意義上,不是説使用 GraphQL 查詢圖數據庫,而是使用一種 GraphQL 風格的 API 查詢圖數據庫,或者説是將 Cypher 封裝了一樣。這個本質工作和大家做應用開發時,基於 NebulaGraph 寫一些通過的 REST 接口是一樣的。

API 查詢示例

本文的測試數據集使用的 NebulaGraph 官方的 basketballplayer 數據集https://docs.nebula-graph.io/2.0/basketballplayer-2.X.ngql

舉個例子,如果我想“根據科比的名字得到科比的全部信息”,可能會使用下面這樣的 nGQL 語句:

LOOKUP ON player WHERE player.name == "Kobe Bryant" YIELD id(vertex) as vertexId | FETCH PROP ON player $-.vertexId YIELD properties(vertex);

雖然説 nGQL 已經很方便閲讀了,但是如果讓一個完全 0 基礎的萌新來看也是看不懂的,並且這個語句的返回值是不明確的,至少沒有辦法從查詢看到結果。而返回值的解析一直也是很多人的痛苦

那麼,來看看使用 GraphQL 查詢同一場景會是什麼情況。

查詢語句會是:

{
    players(name:"Kobe Bryant"){
        name
        age
    }
}

返回結果是:

{
    players=[{name=Kobe Bryant, age=40}]
}

看看這優雅的查詢和返回結果,想必我不多説,大家也都看得懂。這真的是

pic2|220x230

其實上面説了那麼多,就是官方對 GraphQL 的總結:描述你的數據、請求你所要的數據、得到可預測的結果

上述的查詢在 NebulaGraphQL 中已經實現了,同時還支持通過 VertexID 查詢數據(好吧,我也就實現了這兩種)。

NebulaGraphQL 簡單入門

NebulaGraphQL 是一個 Java 庫,旨在應用層提供使用 GraphQL 語法查詢 NebulaGraph 圖數據庫中數據的能力。

在項目中使用 NebulaGraphQL 非常簡單,因為 NebulaGraphQL 本身只想做一個簡單的工具庫,未來如果想直接集成到 MVC 框架可能會再起一個 NebulaGraphQL-Spring 之類的項目(畫大餅中……)。所以 NebulaGraphQL 的使用和 nebula-java 是幾乎完全一致的。

使用示例:

//創建一個config
GraphqlSessionPoolConfig graphqlSessionPoolConfig = new GraphqlSessionPoolConfig(
                Lists.newArrayList(graphdAddress),
                Lists.newArrayList(metadAddress),
                spaceName, username, password);
//創建一個連接池
GraphqlSessionPool pool = new GraphqlSessionPool(graphqlSessionPoolConfig);
//執行語句
ExecutionResult executionResult = pool.execute("{players(age:32){name\nage}}");
//獲取結果
System.out.println(executionResult.getData().toString());

其實 GraphSessionPool 內部就是使用的 nebula-java 的 SessionPool,所有配置都和使用官方提供的連接池一致,唯一的區別是需要額外提供 metad 的連接地址。這是因為 NebulaGraphQL 是通過 metad 來自動構建 Schema 的,NebulaGraphQL 會在創建連接池時自動創建 Schema。

NebulaGraphQL 的實現

NebulaGraphQL 主要是基於 graphql-java實現的。而使用 graphql-java,大家可以根據自己的項目定義自己的 GraphQL 的 Schema。不過,NebulaGraphQL 想盡可能地提供一些通用功能,並且一定是根據 NebulaGraph 的 Schema 自動構建的。

在創建 GraphqlSessionPool 時,NebulaGraphQL 通過連接 NebulaGraphQL 的 metad 將 NebulaGraph 中的元數據信息構造成 GraphQL 的 Schema 信息。這一部分是關鍵難題。目前,我僅僅做了如下的變換:

  1. 對於 NebulaGraph 中所有的 Tag,都會構造一個對應的 GraphQL 的可查詢對象。
  2. 每一個 Tag 都會有一個同名的根據 ID 獲取信息的查詢。舉例來説,對於 player 這個 tag,會生成一個查詢 player,這個查詢的參數是 vertexID,會根據 vertexID 獲取到信息。
  3. 每一個 Tag 都會有一個在名稱後加 -s 的查詢。舉例來説,對於 player 這個 tag,會生成一個查詢 players,這個查詢的參數是任意的屬性。如果 player 上有 age,name,country 這些屬性,在查詢參數中可以傳入這三種屬性的任意組合,NebulaGraphQL 查詢時會將這些參數進行“與” AND 語義的構造,再獲取相關頂點。對於用户沒有指定的參數,默認為 null(這是一個已知的問題,如果目的就是查 null 會有問題)。

測試數據集上自動生成的 GraphQL 的 Schema 示例:

type Query {
 player(
 "Vertex ID"
 ID: ID
): player
players(age: Int = null, name: String = null): [player]!
team(
"Vertex ID"
ID: ID
): team
 teams(name: String = null): [team]!
}
type player {
age: Int
name: String
}
type team {
name: String
}

簡單講解一下,Query 是 GraphQL 的查詢入口,其中的 player, players, team, teams 都是自動生成的查詢,可以當作查詢語句。

player 是根據 VertexID 查詢並返回一個 player,player 後面沒有 ! 標識符,説明可能查詢結果為空。players 查詢有兩個參數,對應着 player 這個 tag 的兩個屬性 age 和 name,這兩個參數的類型都從 NebulaGraph 中的數據類型映射到了 GraphQL 的數據類型,默認值都為 null,返回值是一個列表。列表後的 !,説明一定返回一個列表,但是其中的 player 後沒有 ! 標識符,指的是可能返回一個空列表。

使用 players 查詢,參數可以指定 age 或者 name,或者 age 和 name 一起指定。

下面的 player 和 team 兩個 type 就表示了這兩個對象有什麼屬性,可以在查詢時指定返回的屬性,NebulaGraphQL 在返回結果時就只會提供查詢需要的屬性。

每一個 GraphQL 的查詢會有一個綁定的 DataFetcher 對象,該對象中實現的就是如何將 GraphQL 語法映射成 nGQL 語句,並執行插敍返回結果。而返回結果會映射到自定義對象上,這裏使用了我另一個小工具NebulaResultBoot 將執行結果映射到自定的對象上後,我們就可以在未來實現應用層的緩存,當然這裏也有一個潛在的問題:每一次查詢都要求獲取到每一個點邊的所有屬性,這部分未來需要考慮優化。

當然,NebulaGraphQL 的目標不只是簡單將 nGQL 映射到 GraphQL 這麼簡單,因為 GraphQL 除了查詢簡單這個很明朗的特點,還可以更輕鬆的支持權限管理,以及通過 DataLoader 機制在應用層實現一層緩存。不過,我這邊目前也沒有研究的很透徹,如果有大佬願意加入一起實現那就最好不過了。

為了方便大家貢獻(主要是我懶),NebulaGraphQL 的開發測試已經完成了一部分的容器化了,將代碼庫克隆到本地後,本地只需要有 Docker,然後在倉庫根目錄下運行

docker-compose -f docker-compose.dev.yml up --build

就可以看到在自動跑測試了,會在本地啓動 NebulaGraph 集羣並自動插入測試的數據,後續會繼續優化這方面的流程。

小結

NebulaGraphQL 提供了更簡單的查詢語句,這個查詢語句的構造應該是讓前端直接提供的,GraphQL 的優勢之一就是可以讓前端選擇自己需要的數據從而避免“接口地獄”,可能會有人認為這相當於讓前端直接訪問數據庫了。是的,我覺得這個理解也確實沒問題,這也是有人反對 GraphQL 的理由,不過這裏就不繼續討論了。

但是使用 GraphQL 有一個潛在優勢,也就是可以更輕鬆的將圖數據庫和關係型數據庫整合在一起。當然如果只是使用圖數據庫的話,那使用 NebulaGraphQL 至少也能方便做一些簡單的數據查詢與測試。

pic3|225x225

對於 NebulaGraphQL 在實際生產中的價值目前我也沒有體驗過,因為純屬興趣寫着玩兒,如果大家有想法,歡迎在評論區交流。

user avatar xiaoyongyong 头像 sayornottt 头像 sorra 头像
点赞 3 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.