再前一次使用別名(Aliases)來查詢資料,達到同時查詢兩個使用者列表,如上圖,這時會發現查詢語法中會重複出現使用者欄位(Fields),在當前的情況應該還是能夠接受的,但是如果查詢的語法更加複雜時,就會讓查詢語法變得非常長,以至於難以管理,未來要修改查詢語法重複的部分會變得痛苦,在這種下 GraphQL 提供ㄧ種將查詢語法重複的部分提取出來的語法,就叫片段(Fragments),使用方法如下面兩張圖所示:
上面第一張圖可以看到,我們可以在 GraphiQL Explorer 上,在點選需要欄位後,在要再查詢的物件名稱旁有三個點,點擊後會自動產生片段,如同上面第二張圖的UserFragment
,也會自動將原本重複的欄位變成片段,像是...UserFragment
,除了在介面上設定片段之外,當然也能夠直接編輯語法,可以試試下面的查詢語法。
query TwoUserListQuery {
adminUsers: users(role: ADMIN) {
...UserFragment
}
normalUsers: users(role: NORMAL) {
...UserFragment
}
}
fragment UserFragment on User {
email
fullName
id
isActive
lastLogin
role
username
}
另外如果有需要的話,我們也能在片段(Fragments)內使用查詢操作定義的變數(Variables),可以參考 GraphQL 官方教學 [1]。
再做下一個語法操作之前,我們幫UserService
增加新的功能。
# app/services.py
# ... 省略
class UserService:
# ... 省略
def all_users(
self,
role: typing.Optional[types.UserRole] = None,
+ role_in: typing.Optional[typing.List[types.UserRole]] = None,
) -> typing.List[types.User]:
role_map = {
types.UserRole.NORMAL: self.normal_users,
types.UserRole.STAFF: self.staff_users,
types.UserRole.MANAGER: self.manager_users,
types.UserRole.ADMIN: self.admin_users,
}
if role:
return role_map[role]
+ elif role_in:
+ return list(itertools.chain.from_iterable(role_map[r] for r in role_in))
return list(itertools.chain.from_iterable(role_map.values()))
在UserService
我們幫all_users()
加上選擇多個角色的使用者列表的功能,結果如下:
query RolesUserListQuery($roleIn: [UserRole!]) {
users(roleIn: $roleIn) {
id
username
role
fullName
detail {
... on StaffUserDetail {
department
}
... on ManagerUserDetail {
department
}
}
}
}
{
"roleIn": ["STAFF", "MANAGER"]
}
在這邊,我們並無法在介面上設定陣列資料,所以需要透過 變數(Variables) 來傳入資料或是直接寫死資料來操作,此外可以看到這次的查詢有包含了 detail 欄位,但是會看到它裡面出現 ... on StaffUserDetail
與 ... on ManagerUserDetail
,是不是覺得這兩個跟上面的 片段(Fragments) 很像?
其實他跟片段很像,這樣的語法叫做 內嵌片段(Inline Fragments) ,用途是在如果查詢的物件可能有多種型態時,通常用在 介面(Interface) 與 聯合型別(Union Types) 上面。
上面是一個聯合型別的範例,在這範例中 detail 欄位如果是StaffUserDetail
型態就顯示 department 欄位,ManagerUserDetail
也是顯示 department 欄位,當然也可以針對不同型態顯示不同欄位。
在上面的查詢範例中可以發現,查詢結果的資料因為不同型態都顯示一樣的欄位,導致我們單看 detail 欄位無法知道它的類型,這樣是不是不利於客戶端(Client)辨識型別?
所幸 GraphQL 已經幫我們想好了,我只要在查詢語法內幫物件內加上額外資訊欄位(Meta fields),我們就可以在 JSON 格式資料中辨識物件的型態了,就像下面這樣:
我們在我們需要辨識型態的物件內加上 __typename
欄位(注意它是兩個底線),在結果的 JSON 格式資料中"__typename"
的值就是物件的型態名稱。
除此之外還有一些特殊的額外資訊欄位(Meta fields),是關於 Schema Introspection 的,可以參考 GraphQL 規格文件 [2]。