2021年12月15日水曜日

RDFモデルにトリプルをロードするPL/SQLコードを書く

 Oracle APEXのアプリケーションでRDFのモデルにデータをロードすることを想定して、PL/SQLのコードを書いてみます。以下のオラクルのマニュアルに記載されていることの確認になります。Oracle RDF Graph Serverを使用すると、GUIにてデータのインポートが可能です。そのため、インポートするデータがファイルとして存在する場合は、Oracle RDF Graph Serverを使って簡単にインポートできます。

RDFナレッジ・グラフ開発者ガイド

1.8 セマンティック・データのロードおよびエクスポート


バルク・ロードの実行


ステージング表を準備します。表名はSTAGE_TABLE_T1とします。ステージング表は必ずしも毎回作成する必要はなく、再利用もできます。

CREATE TABLE stage_table_t1
(
    RDF$STC_sub varchar2(4000) not null,
    RDF$STC_pred varchar2(4000) not null,
    RDF$STC_obj varchar2(4000) not null,
    RDF$STC_graph varchar2(4000)
);

RDF$STC_subに主語、RDF$STC_predに述語、RDF$STC_objに目的語を投入します。今回はRDF$STC_graphは使用しません。

Oracle APEXから利用できることを確認するため、SQLはSQLワークショップSQLコマンドより実行します。

ステージング表にデータを投入します。SQLコマンドで実行できるのは1行のSQL、またはbegin/endで囲んだ1ブロックだけです。そのため、7行のinsert文をbegin/endで囲んでいます。

begin
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article1>','<http://purl.org/dc/elements/1.1/title>','"All about XYZ"');
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article1>','<http://purl.org/dc/elements/1.1/creator>','"Jane Smith"');
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article1>','<http://purl.org/dc/terms/references>','<http://nature.example.com/article2>');
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article1>','<http://purl.org/dc/terms/references>','<http://nature.example.com/article3>');
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article2>','<http://purl.org/dc/elements/1.1/title>','"A Review of ABC"');
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article2>','<http://purl.org/dc/elements/1.1/creator>','"Joe Bloggs"');
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj) values('<http://nature.example.com/article2>','<http://purl.org/dc/terms/references>','<http://nature.example.com/article3>');
end;

投入された内容を確認します。元のデータはマニュアルに記載のある雑誌記事の情報です。

select * from stage_table_t1;

モデルを作成します。モデルの名前はT1とします。

begin
    sem_apis.create_sem_model(
        model_name => 'T1',
        table_name => null,
        column_name => null,
        network_owner => 'APEXDEV',
        network_name => 'NET1'
    );
end;
/

ステージング表STAGE_TABLE_T1の内容をモデルT1へロードします。flagsの指定についてはマニュアルの記載を参考にします。PARSEは必須です。

begin
    sem_apis.bulk_load_from_staging_table(
        model_name => 'T1'
        , table_owner => 'APEXDEV'
        , table_name => 'STAGE_TABLE_T1'
        , flags => ' PARSE '
        , network_owner => 'APEXDEV'
        , network_name => 'NET1'
    );
end;
/

ロードされた内容を確認します。ネットワーク名#RDFT_モデル名の表を検索します。今回の例ではNET1#RDFT_T1です。

select 
    a.triple.GET_SUBJECT('APEXDEV','NET1') as subject,
    a.triple.GET_PROPERTY('APEXDEV','NET1') as property,
    a.triple.GET_OBJ_VALUE('APEXDEV','NET1') as object
FROM APEXDEV.NET1#RDFT_T1 a;

以上でバルク・ロードの確認ができました。

ステージング表に投入されているデータにASCIIのBS(8)、HT(9)、LF(10)、FF(12)、CR(13)が含まれる場合、またはUnicode文字が含まれる場合は、あらかじめSEM_APIS.ESCAPE_RDF_TERM(もしくはSEM_APIS.ESCAPE_RDF_VALUE、SEM_APIS.ESCAPE_CLOB_TERM、SEM_APIS.ESCAPE_CLOB_VALUE)を使ってエンコードする必要があります。

例えば、エンコードされたデータを保持するステージング表を作成します。

CREATE TABLE stage_table_t1_enc
(
    RDF$STC_sub varchar2(4000) not null,
    RDF$STC_pred varchar2(4000) not null,
    RDF$STC_obj varchar2(4000) not null,
    RDF$STC_graph varchar2(4000)
);

その後、元のステージング表よりデータをエンコードしてコピーします。

insert into stage_table_t1_enc(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj)
select
    sem_apis.escape_rdf_term(RDF$STC_sub,  options => ' UNI_ONLY=T '),
    sem_apis.escape_rdf_term(RDF$STC_pred, options => ' UNI_ONLY=T '),
    sem_apis.escape_rdf_term(RDF$STC_obj,  options => ' UNI_ONLY=T ')
from stage_table_t1;

エンコードされたデータを保持しているステージング表から、ロード処理を実行します。

begin
    sem_apis.bulk_load_from_staging_table(
        model_name => 'T1'
        , table_owner => 'APEXDEV'
        , table_name => 'STAGE_TABLE_T1_ENC'
        , flags => ' PARSE '
        , network_owner => 'APEXDEV'
        , network_name => 'NET1'
    );
end;
/

エスケープされたデータを元に戻すためにSEM_APIS.UNESCAPE_RDF_TERMが使えます。

動作テストのために作業を繰り返す際に実行したコマンドを、以下に記録しておきます。作成済みのモデルからセマンティック・データをエクスポートする手順です。

-- ステージング表の内容を削除する。
delete from stage_table_t1;
-- モデルからデータエクスポートし、ステージング表にインポートする。
insert into stage_table_t1(RDF$STC_sub,RDF$STC_pred,RDF$STC_obj)
select 
    a.triple.GET_SUBJECT('APEXDEV','NET1') as subject,
    a.triple.GET_PROPERTY('APEXDEV','NET1') as property,
    a.triple.GET_OBJ_VALUE('APEXDEV','NET1') as object
FROM APEXDEV.NET1#RDFT_T1 a;
-- モデルをドロップする。
begin
    sem_apis.drop_sem_model(
        model_name => 'T1'
        , options => null
        , network_owner => 'APEXDEV'
        , network_name => 'NET1'
    );
end;
/


INSERT文の実行によるデータのロード


モデルのトリプルを保持している表はネットワーク名#RDFT_モデル名です。今回の例ではNET1#RDFT_T1なので、この表に直接SDO_RDF_TRIPLE_Sのインスタンスであるトリプルを挿入します。

begin
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article1>','<http://purl.org/dc/elements/1.1/title>','"All about XYZ"','APEXDEV','NET1'));
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article1>','<http://purl.org/dc/elements/1.1/creator>','"Jane Smith"','APEXDEV','NET1'));
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article1>','<http://purl.org/dc/terms/references>','<http://nature.example.com/article2>','APEXDEV','NET1'));
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article1>','<http://purl.org/dc/terms/references>','<http://nature.example.com/article3>','APEXDEV','NET1'));
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article2>','<http://purl.org/dc/elements/1.1/title>','"A Review of ABC"','APEXDEV','NET1'));
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article2>','<http://purl.org/dc/elements/1.1/creator>','"Joe Bloggs"','APEXDEV','NET1'));
insert into net1#rdft_t1 values(sdo_rdf_triple_s('T1','<http://nature.example.com/article2>','<http://purl.org/dc/terms/references>','<http://nature.example.com/article3>','APEXDEV','NET1'));
end;



マニュアルの記載で不明だった点


ステージング表の非セマンティック・データ列の扱い

ステージング表に非セマンティック・データ用の列を追加する例が記載されています。以下のDDLです。

CREATE TABLE stage_table_with_extra_cols (
                     source VARCHAR2(4000),
                     id NUMBER,
                     RDF$STC_sub varchar2(4000) not null,
                     RDF$STC_pred varchar2(4000) not null,
                     RDF$STC_obj varchar2(4000) not null,
                     RDF$STC_graph varchar2(4000)
);

バルク・ロードを実行するAPIのSEM_APIS.BULK_LOAD_FROM_STAGING_TABLEに、追加列を扱う方法が記載されていません。ユーザー作成のアプリケーションの都合に応じてステージング表に列を追加しても、バルク・ロードには影響がない、ということを言っていると思われます。実際、以下の定義で作成したステージング表から問題なく、データのロードができました。

CREATE TABLE stage_table_t1
(
    source varchar2(4000),
    id number,
    RDF$STC_sub varchar2(4000) not null,
    RDF$STC_pred varchar2(4000) not null,
    RDF$STC_obj varchar2(4000) not null,
    RDF$STC_graph varchar2(4000)
);


データのエスケープについて


データのエスケープを行うために、以下のAPIが用意されています。

SEM_APIS.ESCAPE_CLOB_TERM

SEM_APIS.ESCAPE_CLOB_VALUE

SEM_APIS.ESCAPE_RDF_TERM

SEM_APIS.ESCAPE_RDF_VALUE

TERMはURI、VALUEはリテラルを対象にしていると思われますが、マニュアルにそういった記載はありません。また、SEM_APIS.ESCAPE_RDF_VALUEについては、CLOB値が返される、といった誤った記載がされています。ファンクション定義については、マニュアルと実際とでは違いがあります。APIの問題というよりはマニュアルの誤記のようです。

FUNCTION ESCAPE_RDF_VALUE RETURNS VARCHAR2

 Argument Name Type In/Out Default?

 ------------------------------ ----------------------- ------ --------

 VAL VARCHAR2 IN

 UTF_ENCODE NUMBER IN     DEFAULT

 ALLOW_LONG NUMBER IN     DEFAULT

 OPTIONS VARCHAR2 IN     DEFAULT

データをエスケープする際には注意が必要でしょう。


SEM_APIS.CREATE_SEM_MODELの表指定

表を作成し、その表をSEM_APIS.CREATE_SEM_MODELの引数table_nameに与えると、表がビューに置き換えられます。

例えば表T1_DATAを作成します。

create table t1_data(triple sdo_rdf_triple_s) compress;

続いてモデルT1を作成します。

begin
    sem_apis.create_sem_model(
        model_name => 'T1',
        table_name => 'T1_DATA',
        column_name => 'TRIPLE',
        network_owner => 'APEXDEV',
        network_name => 'NET1'
    );
end;
/

以下のSQLを実行し、オブジェクト・タイプを確認すると、VIEWになっています。

select object_name, object_type from user_objects where object_name = 'T1_DATA';


ビューとしての定義は以下です。表NET#RDFT_T1を参照しています。

CREATE OR REPLACE FORCE VIEW "T1_DATA" ("TRIPLE") AS
SELECT triple AS "TRIPLE" FROM "APEXDEV".NET1#RDFT_T1
/

モデルT1をドロップすると、ビューから表に戻ります。
begin
    sem_apis.drop_sem_model(
        model_name => 'T1'
        , options => null
        , network_owner => 'APEXDEV'
        , network_name => 'NET1'
    );
end;
ドロップ後の確認です。モデルT1をドロップしているため、表NET1#RDFT_T1もドロップされています。ビューT1_DATAはT1_DATAに戻っていますが、モデルは削除されているため表T1_DATAの内容も空になっています。


SEM_APIS.CREATE_SEM_MODELの引数としてtable_nameを与えると、ネットワーク名#RDFT_モデル名より判別しやすい名前で(データの挿入などに)扱えますが、データの保持のされ方が変わるといったことは無いようです。

現行のRDFグラフではスキーマプライベート・ネットワークが推奨ですし、それでネットワークを作成していれば、モデル作成時に表を指定する利点はあまりないでしょう。

2021年12月14日火曜日

RDFグラフのCRUD操作を行うAPEXアプリを作る

 Oracle RDF Graph ServerのGUIから毎回SPARQLを実行するのも手間がかかるため、実用的とは言えないですがCRUD操作をするAPEXアプリを作ってみました。CUDの操作はOracle RDF Graph Serverが提供しているREST APIを呼び出して実行します。

以下より、実施した作業について記載します。

Data sourceとしてapexdevRDF networkとしてNET1が作成済みであることが前提です。

最初にモデルを作成します。Oracle RDF Graph Serverの画面のRegular modelsよりCreateを実行します。

Modelの名前はTESTとしました。Autonomous Databaseであれば、TablespaceDATAになります。OKをクリックします。

モデルTESTが作成されました。作成したモデルTESTをOpenします。


RDFのデータは一行も保存されていませんが、全件検索を行うSQLのSELECT文を取り出すため、以下のSPARQL文を実行します。Queryを選択して、SPARQL文を記述した後、Executeをクリックします。
SELECT ?s ?p ?o
WHERE { ?s ?p ?o } 


Query Resultsには、表示するデータがありません。とメッセージが出力されます。

Query cacheを開きます。


モデルTESTを選択し、直近で実行したSPARQL文を選択します。そのSPARQL文のCache Detailsを開きます。


SQLのセクションを開き、変換されたSELECT文をコピーします。このSELECT文はOracle APEXのアプリケーションの対話モード・レポートのソースとして使います。


Oracle RDF Graph Serverでの作業は、ここで終了です。

続いてOracle APEXのアプリケーションを作成します。

アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前RDF CRUD操作とします。アプリケーションの作成を実行します。


アプリケーションが作成されたら、ページ・デザイナホーム・ページ(ページ番号1)を開きます。

新規にリージョンの作成を行い、識別タイトルとしてTESTタイプ対話モード・レポートを選択します。ソースタイプSQL問合せとし、先ほどCache DetailsからコピーしたSELECT文を貼り付けます。

ここで一点、SELECT文を変更します。元のSELECT文はすべての列を表示するようになっています。
SELECT * FROM (
この部分を以下に変更し、SUBJECTPROPERTYOBJECTの組とそれをBASE64でエンコードした列SUBJECT_ENCPROPERTY_ENCOBJECT_ENCを表示するようにします。
SELECT
    s subject
    , utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw('<' || s || '>'))) subject_enc
    , p property ,
    case p$rdfvtyp
    when 'URI' then
        utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw('<' || p || '>')))
    when 'LIT' then
        utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw('"' || p || '"')))
    end property_enc
    , o object ,
    case o$rdfvtyp
    when 'URI' then
        utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw('<' || o || '>')))
    when 'LIT' then
        utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw('"' || o || '"')))
    end object_enc
FROM (
RDFのデータにURIがあり、URIには特殊文字が含まれています。Oracle APEXのアプリケーションでページ・アイテムの値をページ間で受け渡す際に、一部の特殊文字(コロンやカンマなど)で値が分割されてしまうケースがあります。そのため、代わりにBASE64でエンコードした値を受け渡しに使います。


REST APIを呼び出してRDFのモデルをアップデートするプロシージャrdf_update_modelを作成します。コードは以下になります。SQLワークショップSQLコマンドに貼り付けて実行します。



Oracle RDF Graph ServerのREST APIを呼び出す際に指定するパラメータを、共有コンポーネントアプリケーション・アイテムとして定義します。


作成済みのアプリケーション・アイテムの一覧が表示されます。作成をクリックします。


アプリケーション・アイテムの名前としてG_CSRF_TOKENを指定します。それ以外の項目は、セキュリティ上の制限が一番厳しい条件がデフォルトなので変更はしません。アプリケーション・アイテムの作成をクリックします。


同様の手順で、アプリケーション・アイテムとしてG_DATASETDEFG_DATASOURCEG_ORIGING_USERNAMEG_PASSWORDを作成します。


作成したアプリケーション・アイテムに値を設定するため、共有コンポーネントアプリケーションの計算を作成します。


作成済みのアプリケーションの計算が一覧されます。作成をクリックします。


計算アイテムとしてG_CSRF_TOKENを選択し、頻度計算ポイント認証後とします。計算計算タイプファンクション本体計算には以下のコードを記述します。設定後、計算の作成をクリックします。CSRFの対応に使われるトークンの値を、Oracle RDF Graph Serverより取得しています。



これ以外のアプリケーションの計算は、頻度計算ポイント新規インスタンス(新規セッション)開始時を選択し、計算計算タイプ静的割当てとします。


環境の構成の仕方で異なりますが、それぞれの計算アイテムに対して、以下の静的値を割り当てます。

G_DATASOURCE: データソース名(今回の例ではapexdev)
G_DATASETDEF: 今回の例では{"metadata":[{"networkOwner":"APEXDEV","networkName":"NET1","models":["TEST"]}]}
G_ORIGINhttps://Oracle RDF Graph Serverのホスト名
G_USERNAME: Oracle RDF Graph Serverのユーザー名
G_PASSWORD: Oracle RDF Graph Serverのパスワード


RDFのトリプルの作成、更新、削除を行うページを作成します。ページ作成ウィザードを呼び出します。


データベースの表の操作ではなく実装も特殊なので、ページ・タイプはフォームではなく空白ページを選択します。


ページ番号(異なるとページ・アイテムの名前に影響があります)、ページの名前トリプルページ・モードモーダル・ダイアログを選択します。オプションの静的コンテンツ・リージョンを開き、リージョン1の名称としてトリプルを指定します。へ進みます。


ナビゲーションのプリファレンスとして、このページとナビゲーション・メニュー・エントリを関連付けないを選択します。に進みます。


確認画面が表示されるので、終了をクリックします。


静的コンテンツのリージョンをひとつ含んだページが作成されます。静的コンテンツのリージョンでページ・アイテムの作成を行います。

識別タイプ非表示のページ・アイテムとして、P2_SUBJECT_OLDP2_PROPERTY_OLDP2_OBJECT_OLDを作成します。RDFグラフには(少なくてもSPARQLで操作する場合は)主キーの情報がなく、アップデートという操作もありません。値の更新を行う場合、変更前のトリプルを削除し、値を更新した新しいトリプルを作成するという操作を行います。変更前の値を維持するために、これらのページ・アイテムを使用します。


識別タイプテキスト・フィールドのページ・アイテムとして、P2_SUBJECTP2_PROPERTYP2_OBJECTを作成します。これらのページ・アイテムの値をトリプルとして挿入します。ラベルはそれぞれSubjectPropertyObjectと設定します。


対話モード・レポートのページから渡されるサブジェクト、プロパティ、オブジェクトのデコードと、変更前データの保持を行うプロセスを作成します。

レンダリング前ヘッダーの前の位置でプロセスを作成します。識別名前デコードと保存とし、タイプとしてコードの実行を選択します。ソースPL/SQLコードとして、以下のコードを記述します。新規作成の場合はページ・アイテムP2_SUBJECT、P2_PROPERTY、P2_OBJECTの値はこのページに渡されないため、このコードの実行は不要です。サーバー側の条件タイプとしてリクエスト = 値を選択し、B_EDITのときにだけ実行させます。



ページ・デザイナでホーム・ページを開き、対話モード・レポートより、このページを呼び出すリンクを作成します。

対話モード・レポートのリージョンにボタンを作成します。識別ボタン名B_CREATEラベル作成とします。ボタン位置対話モード・レポートの検索バーの右を選択します。


動作アクションとして、このアプリケーションのページにリダイレクトを選択します。ターゲットページキャッシュのクリアを指定します。OKをクリックします。


データの更新や削除を行うため、レポートの行それぞれに編集リンクを付けます。対話モード・レポートを選択し、Attributesリンクを設定します。リンク列カスタム・ターゲットへのリンクを選択します。


ターゲットタイプこのアプリケーションのページページです。アイテムの設定名前の組み合わせは以下になります。

P2_SUBJECT: #SUBJECT_ENC#
P2_PROPERTY: #PROPERTY_ENC#
P2_OBJECT: #OBJECT_ENC#


ダイアログの画面を少し下にスクロールさせ、詳細リクエストB_EDITを指定します。以上でOKをクリックします。


ページ番号2の画面が閉じたときに対話モード・レポートがリフレッシュされるよう、動的アクションを作成します。

左ペインにて動的アクション・ビューを開き、ダイアログのクローズの位置で動的アクションの作成を行います。

識別名前ダイアログのクローズタイミングイベントダイアログのクローズ選択タイプリージョンリージョンとして対話モード・レポートのリージョンであるTESTを選択します。


TRUEアクション識別アクションリフレッシュ影響を受ける要素選択タイプリージョンとし、リージョンTESTを選択します。


ホーム・ページはこれで完成です。

フォームのページにトリプルの作成、更新、削除を行うボタンおよび、それに紐づくプロセスを作成します。

ボタンB_CREATEを作成します。ラベル作成レイアウトボタン位置Createを選択します。動作アクションは(デフォルトの)ページの送信です。サーバー側の条件タイプとしてアイテムはNULLを選択し、アイテムP2_SUBJECTとします。呼び出し元からP2_SUBJECTの情報が渡されない場合は、新規作成と見做します。


同様にボタンB_UPDATEを作成します。ラベル更新レイアウトボタン位置Changeを選択します。動作アクションは(デフォルトの)ページの送信です。サーバー側の条件タイプとしてアイテムはNULLではないを選択し、アイテムP2_SUBJECTとします。呼び出し元からP2_SUBJECTの情報が渡されている場合は、更新か削除を行うと見做します。


ボタンB_DELETEを作成します。ラベル削除レイアウトボタン位置Deleteを選択します。動作アクションは(デフォルトの)ページの送信です。サーバー側の条件タイプとしてアイテムはNULLではないを選択し、アイテムP2_SUBJECTとします。


左ペインでプロセス・ビューを開き、ボタンを押したときに実行されるプロセスを作成します。

最初に削除のプロセスを作成します。識別名前削除タイプコードの実行を選択します。サーバー側の条件ボタン押下時としてB_DELETEを選択します。ソースPL/SQLコードとして以下を記述します。



更新のプロセスを作成します。実際の処理は変更前のトリプルの削除です。更新された値は作成のプロセスによって新たに挿入されます。

識別名前更新タイプコードの実行を選択します。サーバー側の条件ボタン押下時としてB_UPDATEを選択します。ソースPL/SQLコードとして以下を記述します。



作成のプロセスを作成します。識別名前作成タイプコードの実行を選択します。サーバー側の条件タイプとしてリクエストは値に含まれる、値はB_CREATE B_UPDATEとして、ふたつのボタンを指定します。ソースPL/SQLコードとして以下を記述します。



最後にダイアログをクローズするプロセスを作成します。識別名前ダイアログのクローズタイプダイアログを閉じるを選択します。


以上でアプリケーションは完成です。

少し調整をします。

ダイアログの静的コンテンツのリージョンTESTの見栄えを改善します。テンプレート・オプションを開き、HeaderHiddenStyleRemove Bordersとします。


ページ・デザイナにてホーム・ページを開き、ブレットクラムのリージョンRDF CRUD操作ボタンを作成します。識別ボタン名B_DELETE_ALLラベル全削除です。レイアウトボタン位置Nextにします。動作アクションはデフォルトのページの送信です。


全削除のボタンを押したときに実行されるプロセスを作成します。識別名前全削除タイプコードの実行とします。ソースPL/SQLコードには以下を記載します。サーバー側の条件ボタン押下時B_DELETE_ALLです。



以上でアプリケーションの調整は完了です。記事の先頭の動画では、最初にOracle RDF Graph Serverにて、マニュアルに記載されている雑誌記事の情報をロードしています。


作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/rdf-crud-ops.sql

色々な原因によりREST APIの呼び出しでエラーが発生することがあります。その場合はデバッグを有効にすると、実行したSPARQL文とREST APIのレスポンスを確認できます。


このアプリケーションを作成した一番の目的は、Oracle RDF Graph ServerのREST APIの動作確認です。何しろREST APIの/datasets/updateはドキュメントに記載がありません。

それでは、この記事がOracle APEXのアプリケーション作成と、Oracle RDF Graph Serverを使ったアプリケーション作成の参考になれば幸いです。