從[Day10]~[Day13],我們將針對不同人時地事四個面向,建立初始schema。
PoliceRank
共有十七個階級,其中十五個階級為依據維基百科所定義。剩餘兩個階級分別為Protected
(臥底)及Cadet
(警校學生)。
由於階級是有限個數,適合使用extending Enum來建立PoliceRank
。
其中extending
是EdgeDB中表現繼承(inheritance)的關鍵字。
scalar type PoliceRank extending enum<Protected, Cadet, PC, SPC, SGT, SSGT, PI, IP, SIP, CIP, SP, SSP, CSP, ACP, SACP, DCP, CP>;
GangsterRank
共有三個階級。
scalar type GangsterRank extending enum<Nobody, Leader, Boss>;
Abstract object types
不能直接被生成,但可以被其它object type
所繼承。
Person
作為一個抽象的人物abstract object type
,目的是希望能夠被真實世界的演員(Actor
)以及劇中角色(Character
)所extending
。不論是演員或角色,有三個property
是兩者皆具備的:
name property
為一必填的property
,為演員或劇中角色名字。nickname property
記錄綽號。eng_name property
記錄英文名字。abstract type Person {
required name: str;
nickname: str;
eng_name: str;
}
IsPolice
有三個property
:
police_rank property
代表警察官階,預設為警校學生(PoliceRank.Cadet
)。object type
的property
或link
可以使用{}
於其中添加一些額外的資訊或限制,例如給予預設值。dept property
代表警察部門。is_officer property
是一computed property
(留意is_officer
後使用的是:=
,不是:
),會根據police_rank
是否高於PoliceRank.PI
,來顯示true
或false
(enum
的值是可以比較的,所以這邊可以使用>=
)。abstract type IsPolice {
police_rank: PoliceRank {
default:= PoliceRank.Cadet;
};
dept: str;
is_officer:= .police_rank >= PoliceRank.PI;
}
computed property或link不必預先定義於schema,而可以根據某些條件來動態計算。例如這邊就用來動態計算警察的官階,是否高於PoliceRank.PI
這個階級。由於computed
是由已知資訊所計算而來,所以EdgeDB可以推斷出型別,無須特別指定。
IsGangster
有一個property
及一個link
:
gangster_rank property
代表黑社會階級,預設為小弟(GangsterRank.Nobody
)。gangster_boss link
代表此角色的老大。abstract type IsGangster {
gangster_rank: GangsterRank {
default:= GangsterRank.Nobody;
};
gangster_boss: GangsterBoss;
}
EdgeDB
具有多重繼承的特色,所以IsSpy
可以同時extending
IsPolice
及IsGangster
用來表示臥底。
abstract type IsSpy extending IsPolice, IsGangster;
Character
extending
Person
用來表示劇中角色,有一個property
與兩個link
:
classic_lines property
為一array<str>
,用來記錄角色於劇中的名言。lover link
為劇中角色的戀人。actors multi link
為飾演劇中角色的演員(使用multi
,因為一個角色可能由一個以上演員詮釋)。type Character extending Person {
classic_lines: array<str>;
lover: Character;
multi actors: Actor;
}
Actor
extending
Person
用來表示演員。
type Actor extending Person;
Police
同時extending
Character
及IsPolice
用來表示警察。
type Police extending Character, IsPolice;
Gangster
同時extending
Character
及IsGangster
用來表示黑社會。
type Gangster extending Character, IsGangster;
GangsterBoss
extending
Gangster
而來。
type GangsterBoss extending Gangster {
overloaded gangster_rank: GangsterRank {
default:= GangsterRank.Boss;
constraint expression on (__subject__ = GangsterRank.Boss);
};
# excluding self
constraint expression on (__subject__ != .gangster_boss) {
errmessage := "The boss can't be his/her own boss.";
}
}
當想要針對繼承而來的property
或link
進行更嚴格限制時,需要使用overloaded。請留意,overloaded
只能進行更嚴格的限制,無法放鬆。
由於GangsterBoss
也算是Gangster
,只不過其gangster_rank
位階較其它黑社會成員高,所以可以使用overloaded
針對gangster_rank
加上兩個限制:
預設其gangster_rank
為GangsterRank.Boss
:
default:= GangsterRank.Boss;
無論是insert
或update
GangsterBoss
,其gangster_rank
都只能指定為GangsterRank.Boss
:
constraint expression on (__subject__ = GangsterRank.Boss);
constraint expression on可接受一個expression
來返回true
或false
,如果返回的是true
的話,才能允許相關操作。而__subject__
代表此處將受限制的值(一個GangsterRank scalar
)。
此外,我們針對GangsterBoss
這個object type
也加上一個constraint
來限制自己不能是自己的gangster_boss
。其中:
.gangster_boss
的.
代表引用的是自身的gangster_boss property
(定義於Gangster
中)。__subject__
則代表自己(一個GangsterBoss object
)。errmessage
可以指定於insert
或update
發生錯誤時,所顯示的錯誤訊息。constraint expression on (__subject__ != .gangster_boss) {
errmessage := "The boss can't be his/her own boss.";
}
PoliceSpy
同時extending
Character
及IsSpy
,為警方派至黑社會之臥底。
type PoliceSpy extending Character, IsSpy;
GangsterSpy
同時extending
Character
及IsSpy
,為黑社會派至警方之臥底。
type GangsterSpy extending Character, IsSpy;