9.1 JSONリレーショナル二面性ビュー用のOracle GraphQLディレクティブ
GraphQLディレクティブは、GraphQLスキーマの追加情報または特定の動作を指定する注釈です。二面性ビューを定義するためのすべてのOracle GraphQLディレクティブは、GraphQLフィールドに適用されます。
GraphQLディレクティブは、接頭辞@
が付いた名前であり、場合によっては引数が続きます。
二面性ビューを定義するためのOracle GraphQLには、次のディレクティブがあります:
-
ディレクティブ
@flex
は、JSON
型の列を二面性ビュー用のフレックス列として指定します。このディレクティブの使用については、「フレックス列(基本より詳しい説明)」で説明します。 -
ディレクティブ
@nest
および@unnest
は、二面性ビュー定義の中間オブジェクトのネストおよびネスト解除(フラット化)を指定します。これらは、それぞれSQLキーワードNEST
およびUNNEST
に対応します。制限(順守されない場合はエラーが発生します):
-
ルート表の識別列(主キー列、アイデンティティ列、一意制約または一意索引がある列)に対応付けられているフィールドはネストできません。
-
別名を持つフィールドのネストは解除できません。
例9-1に、
@nest
の使用例を示します。@unnest
を使用する例については、「GraphQLを使用したカーレース二面性ビューの作成」を参照してください。 -
-
ディレクティブ
@link
は、列間の複数の外部キー・リンクを区別します。「Oracle GraphQLディレクティブ@link」を参照してください。 -
ディレクティブ
@generated
により、生成されるJSONフィールドを指定します。生成フィールドにより、二面性ビューによってサポートされているドキュメントが拡張されます。これらは、基になる個々の列にマップされないため、読取り専用です。ディレクティブ
@generated
には、オプションの引数path
またはsql
を、そのJSONフィールド値の計算に使用される値とともに指定します。pathの値は、SQL/JSONパス式です。sql
の値は、SQL式または問合せです。「生成フィールド、非表示フィールド」を参照してください。 -
ディレクティブ
@hidden
により、非表示のJSONフィールドを指定します。それは、二面性ビューによってサポートされているどのドキュメントにも表示されません。ディレクティブ@hidden
には引数を指定できません。「生成フィールド、非表示フィールド」を参照してください。 -
ディレクティブ
@
[no
]update
、@
[no
]insert
および@
[no
]delete
は、注釈を更新する二面性ビューとして機能します。これらは、SQL注釈キーワード[NO
]UPDATE
、[NO
]INSERT
および[NO
]DELETE
に対応しており、「注釈(NO)UPDATE、(NO)INSERT、(NO)DELETEによる更新操作の許可/禁止」で説明されています。 -
ディレクティブ
@
[no
]check
は、オプティミスティックな同時実行性制御に対応する二面性ビューの部分を決定します。これらは、SQL注釈キーワード[NO
]CHECK
に対応しています。詳細は、「GraphQLを使用したカーレース二面性ビューの作成」を参照してください。
例9-1 ネストされたドライバ情報を使用した二面性ビューDRIVER_DV1の作成
この例では、二面性ビューdriver_dv1
を作成しています。これは、例3-7でGraphQLを使用して定義され、例3-3でSQLを使用して定義されたビューdriver_dv
と同じです。ただし、表driver
の列のフィールドname
およびpoints
は、フィールドdriverInfo
の値であるサブオブジェクトでネストされます。脚注1フィールドdriverInfo
の指定は、ビューdriver_dv1
の定義と元のビューdriver_dv
の定義の違いのみです。
driver_dv1
の対応するGraphQL定義およびSQL定義を示します。
CREATE JSON RELATIONAL DUALITY VIEW driver_dv1 AS
driver
{_id : driver_id,
driverInfo : driver @nest {team : name,
points : points},
team @unnest {teamId : team_id,
name : name},
race : driver_race_map
[ {driverRaceMapId : driver_race_map_id,
race @unnest {raceId : race_id,
name : name},
finalPosition : position} ]};
次に対応するSQL定義を示します:
CREATE JSON RELATIONAL DUALITY VIEW driver_dv1 AS
SELECT JSON {'_id' : d.driver_id,
'driverInfo' : {'name' : d.name,
'points' : d.points},
UNNEST
(SELECT JSON {'teamId' : t.team_id,
'team' : t.name}
FROM team t
WHERE t.team_id = d.team_id),
'race' :
[ SELECT JSON {'driverRaceMapId' : drm.driver_race_map_id,
UNNEST
(SELECT JSON {'raceId' : r.race_id,
'name' : r.name}
FROM race r
WHERE r.race_id = drm.race_id),
'finalPosition' : drm.position}
FROM driver_race_map drm
WHERE drm.driver_id = d.driver_id ]}
FROM driver d;
表driver
はビューのルート表であるため、デフォルトでフィールドはすべてビューにネストされず、GraphQLで@nest
を使用してネストする必要があります。
(ルート以外の表のフィールドはデフォルトでネストされており、ネストを解除するには@unnest
(SQLではキーワードUNNEST
)を明示的に使用する必要があります。これは、チーム・フィールドteamId
とname
、およびレース・フィールドraceId
とname
の場合です。)
_________________________________________________________
- Oracle GraphQLディレクティブ@link
GraphQLディレクティブ@link
は、二面性ビューの基礎となる表内の列間の複数の外部キー・リンクを区別します。
関連トピック
9.1.1 Oracle GraphQLディレクティブ@link
GraphQLディレクティブ@link
は、二面性ビューの基礎となる表内の列間の複数の外部キー・リンクを区別します。
ディレクティブ@link
により、二面性ビューの基になる表の列間のリンク、つまり結合を指定します。通常、列は別の表のためのものですが、同じ表の列もリンクできます。この場合、外部キーは自己参照と呼ばれます。
一般に、外部キー・リンクを明示的に指定する必要がないという点は、二面性ビュー定義に対してGraphQLが示す、SQLより優れている利点です。このようなリンクは通常、基礎となる表依存関係グラフによって推測されるため、冗長性が低くなります。
GraphQLで外部キー・リンクを明示的に使用する必要があるのは、(1) 2つの表の間に複数の外部キー関係がある場合、または(2)表に同じ表を参照する外部キーがある場合、あるいはその両方の場合のみです。このような場合は、@link
ディレクティブを使用して、特定のリンク(外部キーとリンクの方向)を指定します。
@link
ディレクティブには、to
またはfrom
という名前の単一の引数が必要です。この引数では、値がネストされたオブジェクトである二面性ビュー・フィールドに対して、(1)ネストされたオブジェクトのフィールドを定義する列を持つ表の外部キー(to方向)を使用するか、(2)列がオブジェクトのフィールドのネスト/囲みを使用するかを定義する列を持つ表の外部キーを使用するか(from方向)を指定します。
to
またはfrom
引数の値は文字列のGraphQLリストで、各文字列は単一の外部キー列(たとえば、to : ["FKCOL"]
)を指定します。複数の文字列のGraphQLリストは、複合外部キー(to : ["FKCOL1", "FKCOL2"]
など)を表します。(GraphQLリストは、JSON配列に対応しています。カンマは、GraphQLではオプションです。)
表間の異なる外部キー関係を識別するための@linkディレクティブ
異なる表間の複数の外部キー関係を区別する@link
ディレクティブの最初のユースケースは、二面性ビューteam_dv2
およびdriver_dv2
で示されます。
例9-2のteam_w_lead
表定義には、列lead_driver
からdriver
表の列driver_id
への外部キー・リンクがあります。また、driver
表定義には、列team_id
からteam_w_lead
表の主キー列team_id
への外部キー・リンクがあります。
図9-1の表依存関係グラフは、これら2つの依存関係を示しています。これは図3-1のグラフと同じですが、表team_w_lead
の外部キー列lead_driver
から表driver
の主キー列driver_id
への追加リンクが含まれている点が異なります。
例9-2 LEAD_DRIVER列を含む表TEAM_W_LEADの作成
この例では、表team_w_lead
を作成します。これは、例2-4の表team
と同じです。ただし、表driver
の列driver_id
への外部キーである追加の列lead_driver
がある点が異なっています。
CREATE TABLE team_w_lead
(team_id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
name VARCHAR2(255) NOT NULL UNIQUE,
lead_driver INTEGER,
points INTEGER NOT NULL,
CONSTRAINT team_pk PRIMARY KEY(team_id),
CONSTRAINT lead_fk FOREIGN KEY(lead_driver) REFERENCES driver(driver_id));
表driver
には、team表の列team_id
を参照する外部キー列team_id
があります。ここに示す例では、表driver
の定義が例2-4と同じであるとします。ただし、外部キーは例2-4の表team
ではなく、表team_w_lead
を参照します。つまり、ここでは次のdriver
表定義を使用します:
CREATE TABLE driver
(driver_id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
name VARCHAR2(255) NOT NULL UNIQUE,
points INTEGER NOT NULL,
team_id INTEGER,
CONSTRAINT driver_pk PRIMARY KEY(driver_id),
CONSTRAINT driver_fk FOREIGN KEY(team_id) REFERENCES team_w_lead(team_id));
表team_w_lead
とdriver
の間に2つの外部キー・リンクがあるため、これらの表を使用するチームおよびドライバの二面性ビューでは、例9-3および例9-4に示すように、ディレクティブ@link
を使用する必要があります。
例9-3 GraphQLディレクティブ@linkを示す、LEAD_DRIVERを使用した二面性ビューTEAM_DV2の作成
この例は例3-6と似ていますが、例9-2で定義されている表team_w_lead
を使用しており、これには外部キー列lead_driver
があります。表team_w_lead
とdriver
の間に2つの外部キー関係があるため、ディレクティブ@link
を使用して、使用する外部キーとその場所を指定する必要があります。
最上位レベルのJSONフィールドleadDriver
の値は、表team_w_lead
の外部キー列lead_driver
によって提供されるドライバ・オブジェクトです。最上位フィールドdriver
の値は、表driver
の外部キー列team_id
によって提供されるドライバ・オブジェクトのJSON配列です。
フィールドleadDriver
の@link
引数は、from
を使用します。その値lead_driver
が、外部にある/ネストするオブジェクトの基礎となる表team_w_lead
の外部キー列であるためです。これは1対1の結合です。
フィールドdriver
の@link
引数はto
を使用します。その値team_id
が、内部にある/ネストされるオブジェクトの基礎となる表driver
脚注2の外部キー列であるためです。これは1対多の結合です。
CREATE JSON RELATIONAL DUALITY VIEW team_dv2 AS
team_w_lead
{_id : team_id,
name : name,
points : points,
leadDriver : driver @link (from : ["LEAD_DRIVER"])
{driverId : driver_id,
name : name,
points : points},
driver : driver @link (to : ["TEAM_ID"])
[ {driverId : driver_id,
name : name,
points : points} ]};
例9-4 GraphQLディレクティブ@linkを示す、二面性ビューDRIVER_DV2の作成
この例は例3-7と似ていますが、例9-2で定義されている表team_w_lead
を使用しており、これには外部キー列lead_driver
があります。表team_w_lead
とdriver
脚注2の間に2つの外部キー関係があるため、ディレクティブ@link
を使用して、使用する外部キーとその場所を指定する必要があります。
フィールドteam
の@link
引数はfrom
を使用します。その値team_id
が、外部にある/ネストするオブジェクトの基礎となる表driver
の外部キー列であるためです。
CREATE JSON RELATIONAL DUALITY VIEW driver_dv2 AS
driver
{_id : driver_id
name : name
points : points
team_w_lead
@link (from: ["TEAM_ID"])
@unnest
{teamId : team_id,
team : name}
race : driver_race_map
[ {driverRaceMapId : driver_race_map_id,
race @unnest
{raceId : race_id,
name : name}
finalPosition : position} ]};
同じ表を参照する外部キー関係を識別するための@linkディレクティブ
特定の表からそれ自体への自己参照外部キーを識別する@link
ディレクティブの2番目のユースケースは、二面性ビューteam_dv3
、driver_dv3
およびdriver_manager_dv
で示されます。脚注3
例9-5のdriver_w_mgr
表定義には、同じ表driver_w_mgr
の列manager_id
から列driver_id
への外部キー・リンクがあります。脚注4
図9-2の表依存関係グラフは、この自己参照表依存関係を示しています。これは、図3-1のグラフの簡易バージョン(race
表またはdriver_race map
マッピング表なし)ですが、表driver_w_mgr
の外部キー列manager_id
から同じ表の主キー列driver_id
への追加リンクが含まれています。
team_dv3
およびdriver_dv3
二面性ビューの定義は、それぞれ例9-6および例9-7にあります。@link
の使用に関して、元のカーレース・ビュー(team_dv
およびdriver_dv
)との顕著な違いは次のとおりです:
-
ビュー
team_dv3
の配列driver
の情報は、各ドライバのマネージャをフィールドmanagerId
で識別します。 -
ビュー
driver_dv3
では、ドライバのマネージャの識別子がフィールドboss
に含まれています。
ここでは、3番目の二面性ビューdriver_manager_dv
にドライバとしてのマネージャの情報(フィールドname
およびpoints
)が含まれ、マネージャにレポートするドライバの情報(配列reports
)が含まれています。この定義は、例9-8にあります。
例9-5 列MANAGER_IDを含む表DRIVER_W_MGRの作成
この例では、表driver_w_mgr
を作成します。これは、例2-4の表driver
と同じです。ただし、同じ表(driver_w_mgr
)の列driver_id
への外部キーである追加の列manager_id
がある点が異なっています。
CREATE TABLE driver_w_mgr
(driver_id INTEGER GENERATED BY DEFAULT ON NULL AS IDENTITY,
name VARCHAR2(255) NOT NULL UNIQUE,
points INTEGER NOT NULL,
team_id INTEGER,
manager_id INTEGER,
CONSTRAINT driver_pk PRIMARY KEY(driver_id),
CONSTRAINT driver_fk1 FOREIGN KEY(manager_id) REFERENCES driver_w_mgr(driver_id),
CONSTRAINT driver_fk2 FOREIGN KEY(team_id) REFERENCES team(team_id));
外部キー列manager_id
は同じ表driver_w_mgr
を参照するため、この表を使用するドライバの二面性ビュー(driver_dv3
)およびマネージャの二面性ビュー(driver_manager_dv
)は、それぞれ例9-7および例9-8に示すように、ディレクティブ@link
を使用する必要があります。
例9-6 二面性ビューTEAM_DV3の作成(マネージャ付きドライバ)
二面性ビューteam_dv3
の定義は、例3-6の二面性ビューteam_dv
の定義と同じです。ただし、表driver
ではなく、表driver_w_mgr
を使用し、配列driver
のドライバ情報には、フィールドmanagerId
が含まれ、その値は、(表driver_w_mgr
の列manager_id
からの)ドライバのマネージャの識別子である点が異なっています。
CREATE JSON RELATIONAL DUALITY VIEW team_dv3 AS
team @insert @update @delete
{_id : team_id,
name : name,
points : points,
driver : driver_w_mgr @insert @update
[ {driverId : driver_id,
name : name,
managerId : manager_id,
points : points @nocheck} ]};
これは、ビューの同等のSQL定義です:
CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW team_dv3 AS
SELECT JSON {'_id' : t.team_id,
'name' : t.name,
'points' : t.points,
'driver' :
[ SELECT JSON {'driverId' : d.driver_id,
'name' : d.name,
'managerId' : d.manager_id,
'points' : d.points WITH NOCHECK}
FROM driver_w_mgr d WITH INSERT UPDATE
WHERE d.team_id = t.team_id ]}
FROM team t WITH INSERT UPDATE DELETE;
3つのチーム・ドキュメントがビューteam_dv3
に挿入されます。配列driver
の各ドライバ・オブジェクトには、managerId
フィールドがあり、その値はドライバのマネージャの識別子であるか、ドライバにマネージャがいない(ドライバがマネージャである)ことを示すnull
のいずれかです。このユースケースでは、チームのすべてのドライバに同じマネージャ(同様にチームに所属)がいます。
INSERT INTO team_dv3 VALUES ('{"_id" : 301,
"name" : "Red Bull",
"points" : 0,
"driver" : [ {"driverId" : 101,
"name" : "Max Verstappen",
"managerId" : null,
"points" : 0},
{"driverId" : 102,
"name" : "Sergio Perez",
"managerId" : 101,
"points" : 0} ]}');
INSERT INTO team_dv3 VALUES ('{"_id" : 302,
"name" : "Ferrari",
"points" : 0,
"driver" : [ {"driverId" : 103,
"name" : "Charles Leclerc",
"managerId" : null,
"points" : 0},
{"driverId" : 104,
"name" : "Carlos Sainz Jr",
"managerId" : 103,
"points" : 0} ]}');
INSERT INTO team_dv3 VALUES ('{"_id" : 303,
"name" : "Mercedes",
"points" : 0,
"driver" : [ {"driverId" : 105,
"name" : "George Russell",
"managerId" : null,
"points" : 0},
{"driverId" : 106,
"name" : "Lewis Hamilton",
"managerId" : 105,
"points" : 0},
{"driverId" : 107,
"name" : "Liam Lawson",
"managerId" : 105,
"points" : 0} ]}');
例9-7 二面性ビューDRIVER_DV3の作成(マネージャ付きドライバ)
この例は、例3-7で定義されているビューの簡易バージョンです。チームもドライバのためのレース情報も含まれません。かわりに、ドライバのマネージャの識別子がフィールドboss
に含まれます。
例9-5に定義されている表driver_w_mgr
を使用して、外部キー列manager_id
を使用してそのマネージャ情報を取得します。その外部キー関係は同じ表driver_w_mgr
を参照するため、ディレクティブ@link
を使用して外部キーを指定する必要があります。
フィールドboss
の@link
引数はfrom
を使用します。その値["MANAGER_ID"]
が、外部にある/ネストするオブジェクトの基礎となる表driver_w_mgr
の外部キー列であるためです。これは1対1の結合です。
CREATE JSON RELATIONAL DUALITY VIEW driver_dv3 AS
driver_w_mgr @insert @update @delete
{_id : driver_id,
name : name,
points : points @nocheck,
boss : driver_w_mgr @link (from : ["MANAGER_ID"])
{driverId : driver_id,
name : name}};
これはビューの同等のSQL定義で、結合を明示的にします:
CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW driver_dv3 AS
SELECT JSON {'_id' : d1.driver_id,
'name' : d1.name,
'points' : d1.points WITH NOCHECK,
'boss' : (SELECT JSON {'driverId' : d2.driver_id,
'name' : d2.name,
'points' : d2.points WITH NOCHECK}
FROM driver_w_mgr d2
WHERE d1.manager_id = d2.driver_id)}
FROM driver_w_mgr d1 WITH INSERT UPDATE DELETE;
この問合せでは、ドライバ106 (Lewis Hamilton)のドキュメントが選択されます。
SELECT json_serialize(DATA PRETTY)
FROM driver_dv3 v WHERE v.data."_id" = 106;
ドライバLewis HamiltonにはマネージャGeorge Russellがいることを示しています。ドライバとボスの関係は1対1です。
JSON_SERIALIZE(DATAPRETTY)
--------------------------
{
"_id" : 106,
"_metadata" :
{
"etag" : "998443C3E7762F0EB88CB90899E3ECD1",
"asof" : "0000000000000000"
},
"name" : "Lewis Hamilton",
"points" : 0,
"boss" :
{
"driverId" : 105,
"name" : "George Russell",
"points" : 0
}
}
1 row selected.
例9-8 二面性ビューDRIVER_MANAGER_DVの作成
この二面性ビューは、他のドライバを管理するドライバに関する情報を提供します。フィールド_id
、name
およびpoints
には、マネージャに関する情報が含まれます。フィールドreports
は、マネージャにレポートするドライバの配列(ID、名前およびポイント)です。
フィールドreports
の@link
引数はto
を使用します。その値["MANAGER_ID"]
が、外部にある/ネストするオブジェクトの基礎となる表driver_manager_dv
の外部キー列であるためです。これは1対多の結合です。
CREATE JSON RELATIONAL DUALITY VIEW driver_manager_dv AS
driver_w_mgr @insert @update @delete
{_id : driver_id,
name : name,
points : points @nocheck,
reports : driver_w_mgr @link (to : ["MANAGER_ID"])
[ {driverId : driver_id,
name : name,
points : points @nocheck} ]};
これはビューの同等のSQL定義で、結合を明示的にします:
CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW driver_manager_dv AS
SELECT JSON {'_id' : d1.driver_id,
'name' : d1.name,
'points' : d1.points WITH NOCHECK,
'reports' : [ SELECT JSON {'driverId' : d2.driver_id,
'name' : d2.name,
'points' : d2.points WITH NOCHECK}
FROM driver_w_mgr d2
WHERE d1.driver_id = d2.manager_id ]}
FROM driver_w_mgr d1 WITH INSERT UPDATE DELETE;
この問合せでは、ドライバ(マネージャ) 105 (George Russell)のドキュメントが選択されます。
SELECT json_serialize(DATA PRETTY)
FROM driver_manager_dv v WHERE v.data."_id" = 105;
マネージャGeorge Russellには、彼にレポートする2人のドライバLewis HamiltonとLiam Lawsonがいることを示しています。マネージャとレポートの関係は1対多です。
JSON_SERIALIZE(DATAPRETTY)
--------------------------
{
"_id" : 105,
"_metadata" :
{
"etag" : "7D91177F7213E086ADD149C2193182FD",
"asof" : "0000000000000000"
},
"name" : "George Russell",
"points" : 0,
"reports" :
[
{
"driverId" : 106,
"name" : "Lewis Hamilton",
"points" : 0
},
{
"driverId" : 107,
"name" : "Liam Lawson",
"points" : 0
}
]
}
1 row selected.
脚注一覧
脚注1: 更新およびETAGチェックの注釈はここでは示していません。脚注2: 例9-2で示した表driverの定義を想定しています。
脚注3: このユースケースを示すためにここで使用されているデータは架空のものです。
脚注4: レースカーのドライバのマネージャがドライバでもあるというユースケースは、現実にはありえないことである可能性があります。ただし、表からそれ自体への外部キー・リンクを識別する機能は、間違いなく役に立ちます。