GraphQL
原是 Facebook 內部的開發計畫,現已獨立出來成為 GraphQL 基金會。相較於 RESTful API,GraphQL 前端應用可以自定義每次查詢時,僅需要的部分資料欄位和範圍,後端也只需要回傳一小部分資料,藉此讓資料存取更有彈性、靈活和高效率。
GraphQL 的出現,並不是為了完全取代 REST,目前各式應用服務仍以 RESTful API 為最大宗,但是在前後端的資料存取組合愈趨複雜與多變時,RESTful API 便會不敷使用。
舉例來說,假設有一支 API 可以取得使用者的留言,那麼這就可以是一支 RESTful API。再來,除了留言本身圖文資料,我還想要看到有哪些人對它按讚(User LIKE Comment)、有哪些人分享了這則留言(User Share Comment)、有哪些人回覆了這一則留言(User Reply Comment)、以及這些回覆的留言內容(User Write Comment Reply Comment),以上的需求,如果要以 RESTful API 實作,就可能有幾種做法
有注意到上面的粗體字嗎?如果我們用 Cypher 語法來表達,就變成了
(u:User)-[:LIKE]->(c:Comment)
(u:User)-[:Share]->(c:Comment)
(u:User)-[:Reply]->(c:Comment)
(u:User)-[:Write]->(r:Comment)-[:Reply]->(c:Comment)
是不是覺得在這種多層次的複雜查詢應用上,用 Graph 來表達特別強大呢!
相較於 Cypher 是 Neo4j 資料庫的專屬查詢語言,GraphQL 則是開放性的查詢語言,其後端可以是關聯式資料庫。然而從上述的範例,應該隱約可以感受到,GraphQL + GraphDB 應該會是天作之合吧!
概念上來說,以之前的電影資料庫為例,以 Cypher 查詢某一部電影的演員如下
MATCH (m:Movie { title: 'The Matrix' })<-[:ACTION]-(p:Person)
RETURN m, p
以 GraphQL 來表達大概像是這樣
query {
Movie(title:"The Matrix") {
title
released
tagline
actors {
name
born
}
}
}
對應的查詢結果
{
"Movie": [{
"title": "The Matrix",
"released": 1999,
"tagline": "Welcome to the Real World",
"actors": [{
"name": "Keanu Reeves",
"born": 1964
}, {
"name": "Carrie-Anne Moss",
"born": 1967
}, {
"name": "Laurence Fishburne",
"born": 1961
}
]
}]
}
從上述語法可以看出,GraphQL 可以非常彈性且直覺的操作 GraphDB,且客製化回傳的做資料範圍。
如果你使用的是 Neo4j 3.5 或以前的版本,有個 GraphQL plugin 可以直接操作查詢 Neo4j 資料庫,如圖
(請注意 Neo4j 4.0 不支援 GraphQL plugin,這個 plugin 之後也不會再繼續維護和升級)
Neo4j Server 版的話,就是直接把下載的 jar 檔案複製到 plugins 資料夾底下,並重新啟動 Neo4j 即可
因為我想體驗一下 GraphQL plugin,所以用 Docker 的方式跑舊版的 Neo4j
docker run \
--name egg_neo4j_3.5 \
-p7474:7474 -p7687:7687 \
-d --env NEO4J_AUTH=neo4j/mypassword \
neo4j:3.5
docker cp neo4j-graphql-3.5.21.5.jar egg_neo4j_3.5:/var/lib/neo4j/plugins/
此外,還要修改 neo4j.conf
dbms.unmanaged_extension_classes=org.neo4j.graphql=/graphql
或是在上述的 docker run 指令加上參數
--env NEO4J_dbms_unmanaged__extension__classes=org.neo4j.graphql=/graphql
Neo4j 服務啟動後,記得在 browser 重新產生資料庫
:play movie graph
在操作 GraphQL 查詢之前,得先上傳 GraphQL Schema,有兩種做法
先假設我們的 Schema 如下
type Movie {
title: String!
released: Int
actors: [Person] @relation(name:"ACTED_IN",direction:IN)
}
type Person {
name: String!
born: Int
movies: [Movie] @relation(name:"ACTED_IN")
}
第一種做法是在 GraphiQL POST http://localhost:7474/graphql/idl 並輸入上述的 Schema 內容,如果有設定密碼,記得也是要設定 HTTP Authorization Header
第二種做法是打開 Neo4j browser,呼叫預存程序 graphql.idl
CALL graphql.idl('
type Movie {
title: String!
released: Int
tagline: String
actors: [Person] @relation(name:"ACTED_IN",direction:IN)
}
type Person {
name: String!
born: Int
movies: [Movie] @relation(name:"ACTED_IN")
}
')
上傳 GraphQL schema 後,確定一下目前的 Schema
call graphql.schema()
接著就可以開始用 GraphQL 查詢 Neo4jDB
{ Person(name:"Kevin Bacon") {
name
born
movies {
title
released
tagline
}
}
}
GraphQL plugin 還可以進行許多進階查詢或修改資料,但 Neo4j 官方已不再支援維護,所以打算先體驗到這裡即可,之後再看看其他 GraphQL 的解決方案~
參考資源
https://github.com/neo4j-graphql/neo4j-graphql
https://grandstack.io/docs/neo4j-graphql-database-plugin/