Oracle RDF Graph ServerのGUIから毎回SPARQLを実行するのも手間がかかるため、実用的とは言えないですがCRUD操作をするAPEXアプリを作ってみました。CUDの操作はOracle RDF Graph Serverが提供しているREST APIを呼び出して実行します。
以下より、実施した作業について記載します。
Data sourceとしてapexdev、RDF networkとしてNET1が作成済みであることが前提です。
最初にモデルを作成します。Oracle RDF Graph Serverの画面のRegular modelsよりCreateを実行します。
Modelの名前はTESTとしました。Autonomous Databaseであれば、TablespaceはDATAになります。OKをクリックします。
モデルTESTが作成されました。作成したモデルTESTをOpenします。
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 (
この部分を以下に変更し、SUBJECT、PROPERTY、OBJECTの組とそれをBASE64でエンコードした列SUBJECT_ENC、PROPERTY_ENC、OBJECT_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コマンドに貼り付けて実行します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create or replace procedure rdf_update_model | |
( | |
p_sparql in varchar2 | |
, p_origin in varchar2 | |
, p_datasource in varchar2 | |
, p_datasetdef in varchar2 | |
, p_csrf_token in varchar2 | |
, p_username in varchar2 | |
, p_password in varchar2 | |
, p_response out clob | |
) | |
is | |
l_clob clob; | |
l_url varchar2(400); | |
begin | |
l_url := p_origin || '/orardf/api/v1/datasets/update?datasource=' || p_datasource || '&datasetDef=' || p_datasetdef; | |
-- HTTPリクエスト・ヘッダーを定義する。 | |
apex_web_service.g_request_headers.delete(); | |
apex_web_service.g_request_headers(1).name := 'Content-Type'; | |
apex_web_service.g_request_headers(1).value := 'application/json'; | |
-- CSRF(Cross Site Request Forgery)の対応を行う。 | |
apex_web_service.g_request_headers(2).name := 'X-CSRF-Token'; | |
apex_web_service.g_request_headers(2).value := p_csrf_token; | |
apex_web_service.g_request_headers(3).name := 'Origin'; | |
apex_web_service.g_request_headers(3).value := p_origin; | |
apex_web_service.g_request_headers(4).name := 'Cookie'; | |
apex_web_service.g_request_headers(4).value := 'ORACLE_RDFSERVER_CSRF=' || p_csrf_token; | |
-- REST APIの呼び出し。 | |
l_clob := apex_web_service.make_rest_request( | |
p_url => l_url | |
, p_http_method => 'POST' | |
, p_username => p_username | |
, p_password => p_password | |
, p_body => p_sparql | |
); | |
p_response := l_clob; | |
end; |
Oracle RDF Graph ServerのREST APIを呼び出す際に指定するパラメータを、共有コンポーネントのアプリケーション・アイテムとして定義します。
アプリケーション・アイテムの名前としてG_CSRF_TOKENを指定します。それ以外の項目は、セキュリティ上の制限が一番厳しい条件がデフォルトなので変更はしません。アプリケーション・アイテムの作成をクリックします。
同様の手順で、アプリケーション・アイテムとしてG_DATASETDEF、G_DATASOURCE、G_ORIGIN、G_USERNAME、G_PASSWORDを作成します。
作成したアプリケーション・アイテムに値を設定するため、共有コンポーネントのアプリケーションの計算を作成します。
作成済みのアプリケーションの計算が一覧されます。作成をクリックします。
計算アイテムとしてG_CSRF_TOKENを選択し、頻度の計算ポイントは認証後とします。計算の計算タイプはファンクション本体、計算には以下のコードを記述します。設定後、計算の作成をクリックします。CSRFの対応に使われるトークンの値を、Oracle RDF Graph Serverより取得しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_url varchar2(4000); | |
l_blob blob; | |
l_csrf_token varchar2(200); | |
begin | |
l_url := :G_ORIGIN || '/orardf/api/v1/utils/user'; | |
l_blob := apex_web_service.make_rest_request_b( | |
p_url => l_url | |
, p_http_method => 'GET' | |
, p_username => :G_USERNAME | |
, p_password => :G_PASSWORD | |
); | |
for i in 1..apex_web_service.g_response_cookies.count | |
loop | |
if apex_web_service.g_response_cookies(i).name = 'ORACLE_RDFSERVER_CSRF' then | |
l_csrf_token := apex_web_service.g_response_cookies(i).value; | |
end if; | |
end loop; | |
return l_csrf_token; | |
end; |
これ以外のアプリケーションの計算は、頻度の計算ポイントが新規インスタンス(新規セッション)開始時を選択し、計算の計算タイプは静的割当てとします。
環境の構成の仕方で異なりますが、それぞれの計算アイテムに対して、以下の静的値を割り当てます。
G_DATASOURCE: データソース名(今回の例ではapexdev)
G_DATASETDEF: 今回の例では{"metadata":[{"networkOwner":"APEXDEV","networkName":"NET1","models":["TEST"]}]}
G_ORIGIN: https://Oracle RDF Graph Serverのホスト名
G_USERNAME: Oracle RDF Graph Serverのユーザー名
G_PASSWORD: Oracle RDF Graph Serverのパスワード
RDFのトリプルの作成、更新、削除を行うページを作成します。ページ作成ウィザードを呼び出します。
データベースの表の操作ではなく実装も特殊なので、ページ・タイプはフォームではなく空白ページを選択します。
ナビゲーションのプリファレンスとして、このページとナビゲーション・メニュー・エントリを関連付けないを選択します。次に進みます。
確認画面が表示されるので、終了をクリックします。
静的コンテンツのリージョンをひとつ含んだページが作成されます。静的コンテンツのリージョンでページ・アイテムの作成を行います。
識別のタイプが非表示のページ・アイテムとして、P2_SUBJECT_OLD、P2_PROPERTY_OLD、P2_OBJECT_OLDを作成します。RDFグラフには(少なくてもSPARQLで操作する場合は)主キーの情報がなく、アップデートという操作もありません。値の更新を行う場合、変更前のトリプルを削除し、値を更新した新しいトリプルを作成するという操作を行います。変更前の値を維持するために、これらのページ・アイテムを使用します。
識別のタイプがテキスト・フィールドのページ・アイテムとして、P2_SUBJECT、P2_PROPERTY、P2_OBJECTを作成します。これらのページ・アイテムの値をトリプルとして挿入します。ラベルはそれぞれSubject、Property、Objectと設定します。
対話モード・レポートのページから渡されるサブジェクト、プロパティ、オブジェクトのデコードと、変更前データの保持を行うプロセスを作成します。
レンダリング前のヘッダーの前の位置でプロセスを作成します。識別の名前はデコードと保存とし、タイプとしてコードの実行を選択します。ソースのPL/SQLコードとして、以下のコードを記述します。新規作成の場合はページ・アイテムP2_SUBJECT、P2_PROPERTY、P2_OBJECTの値はこのページに渡されないため、このコードの実行は不要です。サーバー側の条件のタイプとしてリクエスト = 値を選択し、値がB_EDITのときにだけ実行させます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
begin | |
-- 受け取った値をデコードする。 | |
:P2_SUBJECT := utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(:P2_SUBJECT))); | |
:P2_PROPERTY := utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(:P2_PROPERTY))); | |
:P2_OBJECT := utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(:P2_OBJECT))); | |
-- 削除と更新のために、以前の値を保持する。 | |
:P2_SUBJECT_OLD := :P2_SUBJECT; | |
:P2_PROPERTY_OLD := :P2_PROPERTY; | |
:P2_OBJECT_OLD := :P2_OBJECT; | |
end; |
ページ・デザイナでホーム・ページを開き、対話モード・レポートより、このページを呼び出すリンクを作成します。
対話モード・レポートのリージョンにボタンを作成します。識別のボタン名はB_CREATE、ラベルは作成とします。ボタン位置は対話モード・レポートの検索バーの右を選択します。
動作のアクションとして、このアプリケーションのページにリダイレクトを選択します。ターゲットのページは2、キャッシュのクリアに2を指定します。OKをクリックします。
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コードとして以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_clob clob; | |
l_sparql varchar2(4000) := | |
q'~ | |
DELETE DATA { | |
%subject% %property% %object% | |
} | |
~'; | |
begin | |
-- SPARQL文の補完。 | |
l_sparql := replace(l_sparql, '%subject%', :P2_SUBJECT); | |
l_sparql := replace(l_sparql, '%property%', :P2_PROPERTY); | |
l_sparql := replace(l_sparql, '%object%', :P2_OBJECT); | |
apex_debug.info(l_sparql); | |
rdf_update_model( | |
p_sparql => l_sparql | |
, p_origin => :G_ORIGIN | |
, p_datasource => :G_DATASOURCE | |
, p_datasetdef => :G_DATASETDEF | |
, p_csrf_token => :G_CSRF_TOKEN | |
, p_username => :G_USERNAME | |
, p_password => :G_PASSWORD | |
, p_response => l_clob | |
); | |
apex_debug.info(l_clob); | |
end; |
識別の名前を更新、タイプにコードの実行を選択します。サーバー側の条件のボタン押下時としてB_UPDATEを選択します。ソースのPL/SQLコードとして以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_clob clob; | |
l_sparql varchar2(4000) := | |
q'~ | |
DELETE DATA { | |
%subject% %property% %object% | |
} | |
~'; | |
begin | |
-- SPARQL文の補完。 | |
l_sparql := replace(l_sparql, '%subject%', :P2_SUBJECT_OLD); | |
l_sparql := replace(l_sparql, '%property%', :P2_PROPERTY_OLD); | |
l_sparql := replace(l_sparql, '%object%', :P2_OBJECT_OLD); | |
apex_debug.info(l_sparql); | |
rdf_update_model( | |
p_sparql => l_sparql | |
, p_origin => :G_ORIGIN | |
, p_datasource => :G_DATASOURCE | |
, p_datasetdef => :G_DATASETDEF | |
, p_csrf_token => :G_CSRF_TOKEN | |
, p_username => :G_USERNAME | |
, p_password => :G_PASSWORD | |
, p_response => l_clob | |
); | |
apex_debug.info(l_clob); | |
end; |
作成のプロセスを作成します。識別の名前を作成、タイプにコードの実行を選択します。サーバー側の条件のタイプとしてリクエストは値に含まれる、値はB_CREATE B_UPDATEとして、ふたつのボタンを指定します。ソースのPL/SQLコードとして以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_clob clob; | |
l_sparql varchar2(4000) := | |
q'~ | |
INSERT DATA { | |
%subject% %property% %object% | |
} | |
~'; | |
begin | |
-- SPARQL文の補完。 | |
l_sparql := replace(l_sparql, '%subject%', :P2_SUBJECT); | |
l_sparql := replace(l_sparql, '%property%', :P2_PROPERTY); | |
l_sparql := replace(l_sparql, '%object%', :P2_OBJECT); | |
apex_debug.info(l_sparql); | |
rdf_update_model( | |
p_sparql => l_sparql | |
, p_origin => :G_ORIGIN | |
, p_datasource => :G_DATASOURCE | |
, p_datasetdef => :G_DATASETDEF | |
, p_csrf_token => :G_CSRF_TOKEN | |
, p_username => :G_USERNAME | |
, p_password => :G_PASSWORD | |
, p_response => l_clob | |
); | |
apex_debug.info(l_clob); | |
end; |
最後にダイアログをクローズするプロセスを作成します。識別の名前をダイアログのクローズ、タイプにダイアログを閉じるを選択します。
以上でアプリケーションは完成です。
少し調整をします。
ダイアログの静的コンテンツのリージョンTESTの見栄えを改善します。テンプレート・オプションを開き、HeaderをHidden、StyleをRemove Bordersとします。
ページ・デザイナにてホーム・ページを開き、ブレットクラムのリージョンRDF CRUD操作にボタンを作成します。識別のボタン名はB_DELETE_ALL、ラベルは全削除です。レイアウトのボタン位置をNextにします。動作のアクションはデフォルトのページの送信です。
全削除のボタンを押したときに実行されるプロセスを作成します。識別の名前は全削除、タイプはコードの実行とします。ソースのPL/SQLコードには以下を記載します。サーバー側の条件のボタン押下時はB_DELETE_ALLです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_clob clob; | |
-- 全消去 | |
l_sparql varchar2(4000) := | |
q'~ | |
delete { ?s ?p ?o } | |
where { ?s ?p ?o } | |
~'; | |
begin | |
apex_debug.info(l_sparql); | |
rdf_update_model( | |
p_sparql => l_sparql | |
, p_origin => :G_ORIGIN | |
, p_datasource => :G_DATASOURCE | |
, p_datasetdef => :G_DATASETDEF | |
, p_csrf_token => :G_CSRF_TOKEN | |
, p_username => :G_USERNAME | |
, p_password => :G_PASSWORD | |
, p_response => l_clob | |
); | |
apex_debug.info(l_clob); | |
end; |
作成したアプリケーションのエクスポートを以下に置きました。
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を使ったアプリケーション作成の参考になれば幸いです。
完