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を使ったアプリケーション作成の参考になれば幸いです。