邊與連結
GraphQL 的威力來自於邊 (edge),也就是資料點之間的連結。當你建構 GraphQL 伺服器時,型態 (type) 通常對應模型 (model)。你可以想像這些型態就像資料被存放在資料庫的資料表 (table) 內,我們可以在那裡用連結 (connections) 來連結型態。接下來我們要來探討可以使用哪種連結來定義型態之間的相互關係。
一對多連結
使用者必須能夠讀取貼過的照片。我們要在一個名為 postedPhotos 的欄位讀取這種資料,它會被解析成使用者貼過的照片清單,而且這些照片會被過濾。
因為一位 User 可貼出多張 Photos,我們將它稱為一對多關係。我們將 User 加入 typeDefs:
1 | type User { |
此時,我們已經建立一個有向圖了。我們可以從 User 型態走到 Photo 型態。要產生無向圖,我們必須提供一條從 Photo 型態走回 User 型態的連結。我們在 Photo 型態加入 postedBy 欄位:
1 | type Photo { |
藉由加入 postedBy 欄位,我們建立一條可返回貼出 Photo 的 User 的連結,建立一個無向圖。這是一對一連結,因為一張照片只能由一位 User 貼出。
為了測試伺服器,我們需要一些樣本資料,往後我們會將資料移到資料庫。先移除 index.js 中設定的空陣列 photos 變數。在專案的根目錄下建立一個子目錄 models 來放我們的樣本資料 data.js:
1 | exports.users = [ |
然後將它加入 index.js 中:
1 | const { users, photos } = require('./models/data'); |
因為連結是用物件型態的欄位建立的,所以它們可以對應解析函式。在這些函式中,我們可以使用父物件的資料來協助找到有關的資料並回傳。
我們將 postedPhotos 與 postedBy 解析函式加入服務:
1 | const resolvers = { |
我們必須在 Photo postedBy 欄位加入一個解析函式。我們可以自行決定如何在這個解析函式裡找到連結的資料。這裡我們使用陣列的 find( ) 方法取得 appLogin 符合每張照片的 appUser 值的使用者。
我們在 User 解析函式裡面使用陣列的 filter( ) 方法來取得該位使用者貼過的照片。這個 filter( ) 方法會回傳一個照片陣列。
接著我們試著傳送 allPhotos query:
1 | query photos { |
1 | { |
我們要自行連接資料與解析函式,但是一旦我們能夠回傳那個連接的資料,用戶端就可以開始編寫功能強大的 query。你可以在 allPhotos query 的 postedBy 下載加入 postedPhotos 看看:
1 | query photos { |
多對多
接下來要在服務中加入 “在照片中標記使用者” 的功能。這意味著一位 User 可被標記 (tag) 在許多不同的照片中,而一張照片裡面可以標記許多不同的使用者。使用者與照片透過標記建立的關係稱為多對多,多位使用者對多張照片。
為了建立多對多關係,我們在 Photo 加入 taggedUsers 欄位,在 User 加入 inPhotos 欄位。
1 | type User { |
taggedUsers 欄位會回傳一串使用者,而 inPhotos 欄位會回傳內含某位使用者的照片串列。
為了實作這個多對多連結,我們要在樣本資料中加入一個標記 (tags) 陣列,這是一個定義多對多關係的資料,它提供了兩組資料之間的交集。在關連式資料庫中稱為 intersection table,它定義兩個多對多資料表之間的關係。
1 | exports.tags = [ |
然後 import 到 index.js 中:
1 | const { users, photos, tags } = require('./models/data'); |
當我們有張照片時,必須搜尋 tags 資料集來找出在照片中被標記的使用者。當我們有一位使用者時,就可以找到內含該使用者的照片串列。因為目前的資料放在 JavaScript 陣列裡面,所以我們在解析函式裡面使用陣列方法來尋找資料
1 | Photo: { |
taggedUsers 欄位解析函式會濾除所有非當前照片的照片,並將過濾後的串列對應到實際的 User 物件組成的陣列。inPhotos 欄位解析函式會用使用者來過濾標記,並將使用者標記對應到實際的 Photo 物件組成的陣列。
接著我們可以傳送一個 GraphQL query 來查看每一張照片中有哪些使用者被標記:
1 | query listPhotos { |
你應該會發現,我們有個 tags 的陣列,但沒有稱為 Tag 的 GraphQL 型態。GraphQL 並不要求資料模型完全匹配 schema 內的型態。用戶端可以藉由查詢 User 型態或 Photo 型態在每張照片找到被標記的使用者,以及有某位使用者被標記的照片。他們不需要查詢 Tag 型態,這只會讓事情更複雜。我們已經完成在解析函式中尋找被標記的使用者或照片的工作了,這可以讓用戶端更容易查詢這些資料。
接續下一篇 建立 GraphQL API (3)