2021年12月7日火曜日

Oracle RDF Graph ServerをAutonomous Databaseで使用する(1) - 環境構築

 Autonomous Database単体ではSPARQLの実行ができないため、Oracle RDF Graph Serverを導入してみました。マーケットプレースからOracle RDF Graph Serverを導入する方法は、こちらに紹介されています。本記事ではWebサーバーとしてJettyを使いたかったので、マーケットプレースからは導入しませんでした。

Oracle DatabaseのRDFグラフの機能に興味がある方がOracle Cloud Infrastructureに詳しいとは限らないため、基本的な環境の準備方法から紹介します。


Autonomous Databaseの作成


RDFグラフのデータを保持するデータベースを作成します。無料で利用可能なAlways Free枠を使います。

OCIのコンソールより、Autonomous Databaseを開きます。作成済みのAutonomous Databaseの一覧画面より、Autonomous Databaseの作成をクリックします。コンパートメントルートを選択しています。無料利用枠のアカウントであれば利用できるサービスは限定されているため、コンパートメントを新設してサービスを割り振る必要性はあまりないでしょう。

Autonomous Databaseの作成画面が開きます。

コンパートメントルート表示名APEXDEVデータベース名APEXDEVとしています。表示名やデータベース名は作業に合わせて変更して構いませんが、この後に出てくるAPEXDEVという文字列は、変更した名前に書き直す必要があります。ワークロード・タイプの選択としてデータ・ウェアハウスもしくはトランザクション処理のどちらかを選択できます。JSONおよびAPEXではOracle RDF Graph Serverを使うことはできません。今回はトランザクション処理を選んでいます。Always Free枠を使用するため、デプロイメント・タイプの選択共有インフラストラクチャになります。


Always FreeONにします。データベース・バージョンの選択19cとします。Always Freeの場合、OCPU数1ストレージ0.02TBに限定されます。管理者資格証明の生成パスワードパスワードの確認に、同じパスワードを入力します。


ネットワーク・アクセスの選択アクセス・タイプとしてすべての場所からのセキュア・アクセスを選択します。ライセンス・タイプの選択ライセンス込み(Always Freeはライセンスを含んでいます)を選び、Autonomous Databaseの作成をクリックします。


数分すると、Autonomous Databaseのプロビジョニングが終了し使用可能になります。



作業用スキーマの作成


Oracle APEXのワークスペースとして作成されるスキーマを、RDFグラフのデータを保持するスキーマとして使用します。

データベース・アクションデータベース・ユーザーよりデータベース・ユーザー(スキーマ)を作成する場合は、SEM_APISパッケージのプロシージャを実行するために必要なシステム権限を個別に付与する必要があるようです。APEXからスキーマを作成した場合は、APEXが必要とする権限がスキーマの作成時に与えられ、その権限でRDFグラフも扱えます。APEXのワークスペースとして作成したスキーマで問題なく作業ができるため、与えられている権限のどれがSEM_APIS.CREATE_SEM_NETWORKやその他のAPIの実行に必要とされているのかは調べていません。

ツール・タブを選択し、APEXを開くをクリックします。


APEXの管理サービスへのサインイン画面が表示されます。下にある日本語をクリックすると、表示が日本語に切り替わります。Autonomous Databaseの作成時に指定したパスワードを入力し、管理にサインインをクリックします。


ワークスペースがひとつも作成されていない場合(データベースの作成直後など)は、Oracle Application Expressへようこそと案内文が表示されます。ワークスペースの作成をクリックします。


データベース・ユーザーパスワードおよびワークスペース名を指定し、ワークスペースの作成をクリックします。ここで指定したデータベース・ユーザー、パスワードを使って、Oracle RDF Graph Serverはデータベースに接続します。パスワードは2回入力ではないので、入力したつもりの文字列と実際に入力した文字列に違いが無い(大文字や小文字など)ように注意します。


APEXのワークスペースとともにデータベース・ユーザー(スキーマ)APEXDEVが作成されます。作成したデータベース・ユーザーのRESTサービスを有効にするため、ワークスペースAPEXDEVにサインインします。APEXDEVのリンクをクリックします。


先ほど指定したデータベース・ユーザーのパスワードを入力し、サインインをクリックします。ワークスペースAPEXDEVにサインインします。


ワークスペースにサインインした後、SQLワークショップRESTfulサービスを呼び出します。


ORDSに登録されていないスキーマと表示されます。ORDSにスキーマを登録をクリックします。


ORDSスキーマ属性RESTfulアクセスの有効化ONにします。スキーマ別名はデフォルトでスキーマ名と同じになります。変更すると構成が複雑になるため、今回は変更しません。サンプル・サービスのインストールは必ずしも必要では無いためOFFとします。スキーマ属性の保存をクリックします。


データベース・ユーザーAPEXDEVのRESTサービスが有効になりました。これでOracle RDF Graph ServerからAutonomous Databaseに接続することができます。

APEXを使った作業は以上で完了です。右上のユーザー名をクリックし、サインアウトします。


なお、この操作はデータベース・アクションデータベース・ユーザーより、WebアクセスONに変更するのと同等の操作になります。(メニューから実施する場合は、RESTの有効化を呼び出します)



VCNの作成


OCIのコンソールよりネットワーキング仮想クラウド・ネットワークを開き、VCNウィザードの起動をクリックします。


インターネット接続性を持つVCNの作成(デフォルト)を選択し、VCNウィザードの起動をクリックします。


VCN名を指定し(ここではMyRDFVCNとしています)、それ以外はデフォルトのままに進みます。


確認画面が表示されます。作成をクリックします。


仮想クラウド・ネットワークの作成を確認して、この作業は完了です。



コンピュート・インスタンスの作成


Oracle RDF Graph Serverを実行するコンピュート・インスタンスを作成します。これもAlways Free枠を使用します。OCIコンソールよりコンピュートインスタンスを開きます。


名前は任意ですが、ここではRDFGSとしています。配置はデフォルトのまま、イメージとシェイプイメージOracle Linux 8に変更します。ネットワーキングはデフォルトのままとします。仮想クラウド・ネットワークとして先ほど作成したVCN- MyRDFVCNサブネットにはパブリック・サブネット-MyRDFVCNがデフォルトで選択されます。また、パブリックIPv4アドレスの割当てはいです。


SSHキーの追加では、キー・ペアを自動で生成を選択し、秘密キーの保存と(今回の用途では使いませんが)公開キーの保存をクリックし、それぞれファイルとしてダウンロードしておきます。ファイル名はssh-key-YYYY-MM-DD.keyssh-key-YYY-MM-DD.key.pubとなります。ブート・ボリュームの設定はデフォルトのままです。

作成をクリックします。


一分程度でコンピュート・インスタンスが作成されます。インスタンスに割り当てられたパブリックIPアドレスコピーしておきます。


以上でOracle RDF Graph Serverを実行するコンピュート・インスタンスの作成が完了しました。

JettyとOracle RDF Graph Serverの準備


Oracle RDF Graph ServerをデプロイするWebサーバーとしてJettyを使用します。Jettyのダウンロード・ページを開いて、Jetty 9.xシリーズのzipファイルをダウンロードします。10.x、11.xではOracle RDF Graph Serverの起動時にエラーが発生し、利用することができませんでした。回避方法はあるのかもしれませんが、9.xで動作したので原因については調査していません。



Oracle RDF Graph Serverをダウンロードします。Oracle Graph Server and Clientのページを開き、Download Oracle Graph Server and Clientをクリックします。


ダウンロード・ページよりCurrent releaseOracle Property Graph and Oracle RDF Graph Webappsをダウンロードします。


Oracle Property Graph and Oracle RDF Graph Webappsをダウンロードするには、Oracleのプロファイルの登録し、そのIDでサインインする必要があります。

jetty-distribution-9.4.44.v20210927.zip、oracle-graph-webapps-21.4.0.zip(ダウンロードする時期によってバージョンの部分は変わります)といったファイルがダウンロードされます。oracle-graph-webapps-21.4.0.zipについてはZIPファイルを展開し、Oracle RDF Graph Serverの実装であるorardf-21.4.0.warを取り出します。

sftpコマンド(または他の、SSHによるファイル転送コマンド)を使用し、先ほど作成したコンピュート・インスタンスにjetty-distribution-9.4.44.v20210927.zipとorardf-21.4.0.warをアップロードします。接続ユーザーにはopcを使います。

% sftp -i ssh-key-2021-12-07.key opc@150.***.***.168

The authenticity of host '150.***.***.168 (150.***.***.168)' can't be established.

ED25519 key fingerprint is SHA256:P5quBXUaQ+dtv6JtisHrE78wirGmvyu78zl99NHiGzI.

This key is not known by any other names

Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

Warning: Permanently added '150.***.***.168' (ED25519) to the list of known hosts.

Connected to 150.***.***.168.

sftp> put jetty-distribution-9.4.44.v20210927.zip

Uploading jetty-distribution-9.4.44.v20210927.zip to /home/opc/jetty-distribution-9.4.44.v20210927.zip

jetty-distribution-9.4.44.v20210927.zip                                                               100%   18MB 814.7KB/s   00:23    

sftp> put orardf-21.4.0.war

Uploading orardf-21.4.0.war to /home/opc/orardf-21.4.0.war

orardf-21.4.0.war                                                                                     100%   35MB   1.1MB/s   00:32    

sftp> exit

% 


以上でOracle RDF Graph Serverをコンピュート・インスタンスに構成する準備が完了しました。

Oracle RDF Graph Serverの構成


コンピュート・インスタンスにユーザーopcでSSH接続します。

最初にJDK1.8をインストールします。新しいJDK-17も試しましたがエラーが発生したため、1.8を使用しています。JDK1.8で動作したため、原因については調査していません。

sudo dnf install jdk1.8

[opc@rdfgs ~]$ sudo dnf install jdk1.8

Failed to set locale, defaulting to C.UTF-8

Last metadata expiration check: 0:08:32 ago on Tue Dec  7 04:22:20 2021.

Dependencies resolved.

========================================================================================================================================

 Package                   Architecture              Version                                  Repository                           Size

========================================================================================================================================

Installing:

 jdk1.8                    x86_64                    2000:1.8.0_301-fcs                       ol8_oci_included                    109 M


Transaction Summary

========================================================================================================================================

Install  1 Package


Total download size: 109 M

Installed size: 253 M

Is this ok [y/N]: y

Downloading Packages:

jdk-8u301-linux-x64.rpm                                                                                  32 MB/s | 109 MB     00:03    

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

Total                                                                                                    32 MB/s | 109 MB     00:03     

Running transaction check

Transaction check succeeded.

Running transaction test

Transaction test succeeded.

Running transaction

  Preparing        :                                                                                                                1/1 

  Installing       : jdk1.8-2000:1.8.0_301-fcs.x86_64                                                                               1/1 

  Running scriptlet: jdk1.8-2000:1.8.0_301-fcs.x86_64                                                                               1/1 

Unpacking JAR files...

tools.jar...

plugin.jar...

javaws.jar...

deploy.jar...

rt.jar...

jsse.jar...

charsets.jar...

localedata.jar...


  Verifying        : jdk1.8-2000:1.8.0_301-fcs.x86_64                                                                               1/1 


Installed:

  jdk1.8-2000:1.8.0_301-fcs.x86_64                                                                                                      


Complete!

[opc@rdfgs ~]$ 


jettyのZIPファイルを展開します。ユーザーopcのホーム直下にjetty-distribution-9.4.44.v2021092という名前のディレクトリが作成されます。このディレクトリの下にwebappsというディレクトリがあるので、アップロードしたorardf-21.4.0.warをorardf.warとしてコピーします。

[opc@rdfgs ~]$ cp orardf-21.4.0.war jetty-distribution-9.4.44.v20210927/webapps/orardf.war


同じくwebappsorardf.xmlを、以下の内容を記載した構成ファイルとして配置します。


/home/opc/jetty-distribution-9.4.44.v20210927/etc以下にrealm.propertiesとして、ユーザーの認証情報を含んだファイルを作成します。ユーザー認証にはJettyが提供しているorg.eclipse.jetty.security.HashLoginServiceを使用します。

ユーザーadmin、パスワードをadminとしたエントリを生成します。パスワードを生成するために以下のコマンドを実行します。jetty-util-xxxxx.jarに含まれているorg.eclipse.jetty.util.security.Passwordに、ユーザー名とパスワードを引数として与えて実行します。

[opc@rdfgs ~]$ java -cp jetty-distribution-9.4.44.v20210927/lib/jetty-util-9.4.44.v20210927.jar org.eclipse.jetty.util.security.Password admin admin

2021-12-07 05:23:48.755:INFO::main: Logging initialized @608ms to org.eclipse.jetty.util.log.StdErrLog

admin

OBF:1u2a1toa1w8v1tok1u30

MD5:21232f297a57a5a743894a0e4a801fc3

CRYPT:adpexzg3FUZAk

[opc@rdfgs ~]$ 


暗号化されたパスワードが出力されます。CRYPTを選んで、以下の一行をrealm.propertiesに書き込みます。ロールであるrdf-admin-userは、オラクルのマニュアルのTomcatの設定を参考に、rdf-admin-user、rdf-read-user、rdf-readwrite-userより選んでいます。

admin= CRYPT:adpexzg3FUZAk,rdf-admin-user

RDF Graph Serverの構成ファイルを/home/opc/workspace以下に保存するように設定します。ディレクトリはJava VMへのプロパティとして設定します。

export JAVA_OPTIONS=-Doracle.rdf.workspace.dir=/home/opc/workspace

[opc@rdfgs ~]$ mkdir /home/opc/workspace

[opc@rdfgs ~]$ export JAVA_OPTIONS="-Doracle.rdf.workspace.dir=/home/opc/workspace -Dfile.encoding=UTF-8"


.bashrcにも記載を追加しておくと、指定を忘れることがありません。

以上の設定を行い、Oracle RDF Graph Serverを実装したJettyを起動します。/home/opc/jetty-distribution-9.4.44.v20210927/bin以下へ移動し、jetty.sh startを実行します。

[opc@rdfgs ~]$ cd /home/opc/jetty-distribution-9.4.44.v20210927/bin

[opc@rdfgs bin]$ ./jetty.sh start

Starting Jetty: 2021-12-07 04:58:56.615:INFO::main: Logging initialized @2003ms to org.eclipse.jetty.util.log.StdErrLog

2021-12-07 04:58:57.922:WARN:oejs.HomeBaseWarning:main: This instance of Jetty is not running from a separate {jetty.base} directory, this is not recommended.  See documentation at https://www.eclipse.org/jetty/documentation/current/startup.html

2021-12-07 04:58:58.217:INFO:oejs.Server:main: jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 1.8.0_301-b09

2021-12-07 04:58:58.325:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///home/opc/jetty-distribution-9.4.44.v20210927/webapps/] at interval 1

. . . 2021-12-07 04:59:07.421:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=3999ms

2021-12-07 04:59:08.614:INFO:oejs.session:main: DefaultSessionIdManager workerName=node0

2021-12-07 04:59:08.614:INFO:oejs.session:main: No SessionScavenger set, using defaults

2021-12-07 04:59:08.615:INFO:oejs.session:main: node0 Scavenging every 600000ms

Dec 07, 2021 4:59:08 AM oracle.graph.rdf.rest.csrf.CSRFTokenUtils setCSRFCookiePath

INFO: Cookie path for CSRF Token set to: /orardf

Dec 07, 2021 4:59:08 AM oracle.graph.rdf.server.ServerInitListener recordWorkspaceInfo

INFO: Configuration folder: /tmp/jetty-0_0_0_0-8080-orardf_war-_orardf-any-2442688635705643790/webapp/WEB-INF/workspace/config

Dec 07, 2021 4:59:08 AM oracle.graph.rdf.server.ServerInitListener recordWorkspaceInfo

INFO: Private folder: /tmp/jetty-0_0_0_0-8080-orardf_war-_orardf-any-2442688635705643790/webapp/WEB-INF/workspace/

Dec 07, 2021 4:59:08 AM oracle.graph.rdf.dataloader.TempFileManager <init>

INFO: Setting temp file manager folder to: /tmp/jetty-0_0_0_0-8080-orardf_war-_orardf-any-2442688635705643790/webapp/WEB-INF/workspace/temp/_uploads

Dec 07, 2021 4:59:08 AM oracle.graph.rdf.server.ServerInitListener getLogManagerConfigurationAsStream

INFO: =======> RDF server log files will be written to: /tmp/jetty-0_0_0_0-8080-orardf_war-_orardf-any-2442688635705643790/webapp/WEB-INF/workspace/logs/RDFserver__C_69e1dd28_rdfgs_sub12070254260_myrdfvcn_oraclevcn_com/

Dec 07, 2021 4:59:09 AM oracle.graph.rdf.server.ServerInitListener initializeLoggingFramework

INFO: ====> RDF server global logging has been initiliazed.

Dec 07, 2021 4:59:09 AM oracle.graph.rdf.server.ServerInitListener contextInitialized

INFO: Application Server: jetty/9.4.44.v20210927

Dec 07, 2021 4:59:09 AM oracle.graph.rdf.server.ServerInitListener contextInitialized

INFO: RDF server workspace directory: /tmp/jetty-0_0_0_0-8080-orardf_war-_orardf-any-2442688635705643790/webapp/WEB-INF/workspace

Dec 07, 2021 4:59:09 AM oracle.graph.rdf.server.ServerInitListener contextInitialized

INFO: RDF server has been initialized

Dec 07, 2021 4:59:10 AM oracle.graph.rdf.rest.RdfApplication initialize

INFO: Initializing the RDF web application ...

. Dec 07, 2021 4:59:11 AM oracle.graph.rdf.rest.RdfApplication initialize

INFO: RDF web application initialization done.

2021-12-07 04:59:14.227:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@5bcea91b{/orardf,file:///tmp/jetty-0_0_0_0-8080-orardf_war-_orardf-any-2442688635705643790/webapp/,AVAILABLE}{/home/opc/jetty-distribution-9.4.44.v20210927/webapps/orardf.war}

2021-12-07 04:59:14.315:INFO:oejs.AbstractConnector:main: Started ServerConnector@1ec7d8b3{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}

2021-12-07 04:59:14.319:INFO:oejs.Server:main: Started @19708ms

OK Tue Dec  7 04:59:15 GMT 2021

[opc@rdfgs bin]$ 


Oracle RDF Graph Serverが起動されました。

ファイアウォールとイングレス・ルールの設定


Jettyはデフォルトで8080番ポートで接続を待ち受けています。このポートに外部から接続できるように許可を与えます。

sudo firewall-cmd --add-port=8080/tcp

[opc@rdfgs ~]$ sudo firewall-cmd --add-port=8080/tcp

success

[opc@rdfgs ~]$ 


設定を出力し、portsに8080/tcpが追加されていることを確認します。

sudo firewall-cmd --list-all

[opc@rdfgs ~]$ sudo firewall-cmd --list-all

public (active)

  target: default

  icmp-block-inversion: no

  interfaces: ens3

  sources: 

  services: ssh

  ports: 8080/tcp

  protocols: 

  masquerade: no

  forward-ports: 

  source-ports: 

  icmp-blocks: 

  rich rules: 

[opc@rdfgs ~]$ 


内容が確認できたら、設定を永続化します。

sudo firewall-cmd --runtime-to-permanent

[opc@rdfgs ~]$ sudo firewall-cmd --runtime-to-permanent

success

[opc@rdfgs ~]$ 


次にネットワークの設定として、8080番を宛先とした通信を許可します。

仮想クラウド・ネットワーク(ここではMyRDFVCN)のリソースセキュリティ・リストを選択し、パブリック・ネットワークのセキュリティ・リスト(ここではDefault Security List for MyRDFVCN)を開きます。


イングレス・ルールの追加をクリックします。


追加するイングレス・ルールとして、ソースCIDR0.0.0.0/0宛先ポート範囲として8080を指定します。イングレス・ルールの追加をクリックします。


ポート8080番を宛先とするイングレス・ルールが追加されました。


Oracle RDF Graph Serverに接続します。

http://コンピュート・インスタンスのIPアドレス:8080/orardf/

ユーザー名としてadminパスワードとしてadminを指定してSign Inをクリックします。


Oracle RDF Graph ServerのHome画面が開きます。


Data Sourceの作成


Autonomous DatabaseをOracle RDF Graph ServerのData Sourceとして登録します。登録にあたって、Autonomous DatabaseのWalletを使用します。作成済みのAutonomous Database - APEXDEVのコンソールを開き、DB接続をクリックします。


ドロワーが開くので、ウォレット・タイプインスタンス・ウォレットのままで、ウォレットのダウンロードをクリックします。


ウォレットのダウンロードのダイアログでパスワードを入力し、ダウンロードをクリックします。この後で、接続に使用するデータベース・ユーザーとパスワードをウォレットに書き込みます。その際に、ここで指定したパスワードを使用するので、入力したパスワードを覚えておいてください


ダウンロードしたファイルはWallet_インスタンス名.zipとなります。今回の例ではWallet_APEXDEV.zipです。

ウォレットにデータベース・ユーザーとパスワードを書き込むためにmkstoreコマンドを使用します。mkstoreコマンドはOracle DatabaseまたはOracle WebLogicサーバーに同梱されています。

Oracle Databaseがインストールされている場合は、$ORACLE_HOME/bin/mkstoreを使用します。とはいえ、Oracle Databaseがインストールされている環境でないと実行できないのも融通が効かないので、以下ではmkstoreが実装されているJarファイルを取り出してウォレットの処理を行なっています。$ORACLE_HOME/jlibにある以下のファイルを使用します。

osdt_cert.jar
osdt_core.jar
oraclepki.jar
cryptoj.jar

次の順序で作業を行なっています。

1. ウォレットを展開するディレクトリを作成します。
  mkdir wallet
2. ウォレットを作成したディレクトリ以下に展開します。
  unzip -d wallet Wallet_APEXDEV.zip
3. ウォレットにデータベース・ユーザー名とパスワードを書き込みます。
  mkstore -wrl ウォレットを展開してディレクトリ -createCredential 接続先 データベース・ユーザー名 パスワード
  今回の例では、以下になります。
  mkstore -wrl /home/opc/wallet -createCredential apexdev_low APEXDEV **************
  Jarファイルをそのまま使用する場合はmkstoreの部分が以下に変わります。
  java -cp oraclepki.jar:osdt_cert.jar:osdt_core.jar:cryptoj.jar oracle.security.pki.OracleSecretStoreTextUI -wrl /home/opc/wallet -createCredential apexdev_low APEXDEV **************

  $ORACLE_HOME/jdkとしてバンドルされているJavaを使用することが前提なので、Javaのバージョンは1.8を使用します。

4. 再度ZIPファイルにまとめます。
  cd wallet
  zip ../wallet.zip *

以上でOracle RDF Graph ServerのData Sourceとして利用可能なZIPファイルが、wallet.zipとして作成できました。

[opc@rdfgs ~]$ mkdir wallet

[opc@rdfgs ~]$ unzip -d wallet Wallet_APEXDEV.zip 

Archive:  Wallet_APEXDEV.zip

  inflating: wallet/README           

  inflating: wallet/cwallet.sso      

  inflating: wallet/tnsnames.ora     

  inflating: wallet/truststore.jks   

  inflating: wallet/ojdbc.properties  

  inflating: wallet/sqlnet.ora       

  inflating: wallet/ewallet.p12      

  inflating: wallet/keystore.jks     

[opc@rdfgs ~]$ java -cp oraclepki.jar:osdt_cert.jar:osdt_core.jar:cryptoj.jar oracle.security.pki.OracleSecretStoreTextUI -wrl /home/opc/wallet -createCredential apexdev_low APEXDEV **************

Oracle Secret Store Tool Release 21.0.0.0.0 - Production

Version 21.3.0.0.0

Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.


Enter wallet password:   ********** (ウォレットをダウンロードしたときに指定したパスワード)

[opc@rdfgs ~]$ cd wallet

[opc@rdfgs wallet]$ zip ../wallet.zip *

  adding: cwallet.sso (stored 0%)

  adding: cwallet.sso.lck (stored 0%)

  adding: ewallet.p12 (stored 0%)

  adding: ewallet.p12.lck (stored 0%)

  adding: keystore.jks (deflated 13%)

  adding: ojdbc.properties (deflated 49%)

  adding: README (deflated 57%)

  adding: sqlnet.ora (deflated 9%)

  adding: tnsnames.ora (deflated 84%)

  adding: truststore.jks (deflated 27%)

[opc@rdfgs wallet]$ 


このwallet.zipを使用して、Data Sourceを作成します。

Oracle RDF Graph Serverに接続し、Data Sourcesタブを開いてCreateボタンをクリックします。Create data sourceの選択肢の中からWalletをクリックします。


zipをクリックし、先ほど更新したウォレットwallet.zipを選択します。Nameは任意ですが、ここではapexdevとしています。Wallet Serviceとしてapexdev_lowを選択しています。以上の指定を行い、OKをクリックします。


ウォレットや接続先となるAutonomous Databaseに問題がなければ、Data Sourceとして追加されます。


これ以降、Dataタブを開き、作成したData Sourceを選択して、RDFグラフのネットワークやモデルの作成、SPARQLクエリの実行といった作業ができます。


以上で、Oracle RDF Graph Serverの環境構築の説明は終了です。

2021年12月2日木曜日

RDFグラフの問合せにバインド変数を使用する

 先日、RDFグラフをAPEXで扱う記事を書いたのですが、基本的すぎて面白みがないと感じたので、RDFグラフの検索にバインド変数を使ってみました。アプリケーションの作成に当たっては、マニュアルRDFナレッジ・グラフ開発者ガイドに記載されている家系の情報を使っています。

以下のような動作をするアプリケーションを作成します。人の名前を入力すると、上の対話レポートにその人の子供を一覧し、下の対話モード・レポートには孫を一覧します。

アプリケーション開発に使用する環境は、以前の記事と同じです。データの準備やSQL の取り出しは手元で動かしているOracle Database 21c XEを使います。APEXアプリケーションはAutonomous Databaseに作成します。

両方の環境ともに、APEXDEVというスキーマで作業を行います。

Oracle Database 21c XEにロードしたグラフのデータは、DataPumpを使ってAutonomous Databaseへ移行します。


データの準備とSQLの取り出し


Oracle Database 21c XEに家系の情報をロードし、APEXアプリケーションに埋め込むSQLの取り出しを行います。

もしネットワークNET1が作成されていたら、削除しておきます。まっさらな環境で作業を行なうことで、エラーが発生する可能性を減らしておきます。

家系の情報をロードします。以下の作業を行なっています。

  1. SEM_APIS.CREATE_SEM_NETWORKを呼び出し、ネットワークNET1を作成。
  2. SEM_APIS.CRAETE_SEM_MODELを使用してモデルfamilyを作成。
  3. SEM_APIS.UPDATE_MODELを呼び出し、家系のスキーマを定義しデータを投入。
  4. SEM_APIS.CREATE_RULEBASEを呼び出し、grandParentOfのルール(parentOfのparentOf)を定義する。
  5. SEM_APIS.CREATE_ENTAILMENTを呼び出し、伴意(ルール索引)を作成する。
  6. SEM_MATCHを使った検索を実行し、動作の確認をする。

スクリプトを実行すると最後の検索結果が、以下のように表示されます。最初のSEM_MATCH関数を含むSELECT文は結果を返さないので、2つめのSELECT文の結果のみが表示されます。

GRANDFATHER

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

GRANDCHILD

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

<http://www.example.org/family/John>

<http://www.example.org/family/Cathy>


<http://www.example.org/family/John>

<http://www.example.org/family/Jack>


<http://www.example.org/family/John>

<http://www.example.org/family/Tom>


<http://www.example.org/family/John>

<http://www.example.org/family/Cindy>


RDFグラフの準備は完了したのでSQLを取り出します。

子供を検索するSQLを取り出します。
set serveroutput on
および
spool children.sql
などを最初に実行し、出力されるSQLを保存します。オプションにUSE_BIND_VAR=PLSQLを追加します。

最後に以下が出力されます。MarthaとSammyの子供が一覧されています。この部分は確認のための出力なので、SELECT文を取り出す際にファイルから削除します。

?s=<http://www.example.org/family/Martha>

|-->?c=<http://www.example.org/family/Cindy>

|-->?c=<http://www.example.org/family/Tom>


?s=<http://www.example.org/family/Sammy>

|-->?c=<http://www.example.org/family/Jack>

|-->?c=<http://www.example.org/family/Cathy>


出力されたSELECT文にはバインド変数のプレースホルダーとして:0:1が含まれます。これをAPEXで使用するページ・アイテム名に変更します。:0:P1_VID:1は:P1_TERMに置き換えます。


同様に孫を検索するSQLを取り出します。


最後に以下が出力されます。Johnの孫が一覧されています。SELECT文を取り出すにあたって削除します。

?s=<http://www.example.org/family/John>

|-->?c=<http://www.example.org/family/Cindy>

|-->?c=<http://www.example.org/family/Jack>

|-->?c=<http://www.example.org/family/Cathy>

|-->?c=<http://www.example.org/family/Tom>


出力されたSELECT文のバインド変数の:0:P1_VID:1:P1_TERMに置き換えます。


これで対話モード・レポートのソースとなるSELECT文の準備ができました。


データの移行


Oracle Database 21c XEからAutonomous Databaseでデータを移行します。

expdpの実行をユーザーAPEXDEVで行なうため、APEXDEVに必要な権限を与えます。SYS AS SYSDBAで接続し、以下を実行します。ディレクトリの作成権限をAPEXDEVに与えます。

SQL> grant create any directory to apexdev;


Grant succeeded.


SQL> 


ユーザーAPEXDEVで接続し、ディレクトリdump_dirを作成します。

SQL> create directory dump_dir as '/home/oracle';


Directory created.


SQL> 


expdpを実行します。ダンプ・ファイルは/home/oracle/rdf.dmpとして出力します。

expdp apexdev/********@localhost/xepdb1 directory=dump_dir dumpfile=rdf.dmp version=19

$ expdp apexdev/********@localhost/xepdb1 directory=dump_dir dumpfile=rdf.dmp version=19


Export: Release 21.0.0.0.0 - Production on Thu Dec 2 14:50:38 2021

Version 21.3.0.0.0


Copyright (c) 1982, 2021, Oracle and/or its affiliates.  All rights reserved.


Connected to: Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production

Starting "APEXDEV"."SYS_EXPORT_SCHEMA_01":  apexdev/********@localhost/xepdb1 directory=dump_dir dumpfile=rdf.dmp version=19 

Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA

Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS

Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICS

Processing object type SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS

Processing object type SCHEMA_EXPORT/STATISTICS/MARKER

Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA

Processing object type SCHEMA_EXPORT/SEQUENCE/SEQUENCE

Processing object type SCHEMA_EXPORT/TABLE/TABLE

Processing object type SCHEMA_EXPORT/TABLE/GRANT/OWNER_GRANT/OBJECT_GRANT

Processing object type SCHEMA_EXPORT/TABLE/COMMENT

Processing object type SCHEMA_EXPORT/PROCEDURE/PROCEDURE

Processing object type SCHEMA_EXPORT/PROCEDURE/GRANT/OWNER_GRANT/OBJECT_GRANT

Processing object type SCHEMA_EXPORT/PROCEDURE/ALTER_PROCEDURE

Processing object type SCHEMA_EXPORT/VIEW/VIEW

Processing object type SCHEMA_EXPORT/VIEW/GRANT/OWNER_GRANT/OBJECT_GRANT

Processing object type SCHEMA_EXPORT/TABLE/INDEX/INDEX

Processing object type SCHEMA_EXPORT/TABLE/INDEX/FUNCTIONAL_INDEX/INDEX

Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT

Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINT

Processing object type SCHEMA_EXPORT/VIEW/TRIGGER

. . exported "APEXDEV"."NET1#RDF_LINK$":"MODEL_2"        23.95 KB     219 rows

. . exported "APEXDEV"."NET1#RDF_VALUE$"                 23.67 KB      94 rows

. . exported "APEXDEV"."NET1#RDF_RULEBASE$"              6.585 KB       9 rows

. . exported "APEXDEV"."NET1#RDF_CLIQUE$":"MODEL_0"          0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_COLLISION$"             5.070 KB       1 rows

. . exported "APEXDEV"."NET1#RDF_CRS_URI$"               293.2 KB    5688 rows

. . exported "APEXDEV"."NET1#RDF_DELTA$":"MODEL_0"           0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_GRANT_INFO$"                0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_HIST$"                  7.328 KB       1 rows

. . exported "APEXDEV"."NET1#RDF_LINK$":"MODEL_0"            0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_LINK$":"MODEL_1"        12.67 KB      29 rows

. . exported "APEXDEV"."NET1#RDF_MODEL$_TBL"             8.109 KB       1 rows

. . exported "APEXDEV"."NET1#RDF_MODEL_INTERNAL$"        8.093 KB       1 rows

. . exported "APEXDEV"."NET1#RDF_NAMESPACE$"                 0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_NETWORK_INDEX_INTERNAL$"  14.77 KB      12 rows

. . exported "APEXDEV"."NET1#RDF_PARAMETER"              6.492 KB       2 rows

. . exported "APEXDEV"."NET1#RDF_PRECOMP$"               6.828 KB       1 rows

. . exported "APEXDEV"."NET1#RDF_PRECOMP_DEP$"           5.968 KB       3 rows

. . exported "APEXDEV"."NET1#RDF_PRED_STATS$":"MODEL_0"      0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_PRED_STATS$":"MODEL_1"      0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_RI_SHAD_2$"                 0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_RULE$"                  19.73 KB      20 rows

. . exported "APEXDEV"."NET1#RDF_SYSTEM_EVENT$"              0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_TERM_STATS$":"MODEL_0"      0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_TERM_STATS$":"MODEL_1"      0 KB       0 rows

. . exported "APEXDEV"."NET1#RDF_TS$"                        0 KB       0 rows

. . exported "APEXDEV"."NET1#RENAMED_APPTAB_RDF_MODEL_ID_1"      0 KB       0 rows

. . exported "APEXDEV"."NET1#SEM_INDEXTYPE_METADATA$"        0 KB       0 rows

Master table "APEXDEV"."SYS_EXPORT_SCHEMA_01" successfully loaded/unloaded

******************************************************************************

Dump file set for APEXDEV.SYS_EXPORT_SCHEMA_01 is:

  /home/oracle/rdf.dmp

Job "APEXDEV"."SYS_EXPORT_SCHEMA_01" successfully completed at Thu Dec 2 14:51:29 2021 elapsed 0 00:00:49


$ 


出力されたダンプ・ファイルをオブジェクト・ストレージにアップロードします。

適当なバケットを開き、アップロードをクリックします。


ドロワーが開くので、ファイルとしてrdf.dmpを選択しアップロードをクリックします。ファイルのアップロードが完了したら、ドロワーを閉じます


オブジェクトとしてアップロードしたrdf.dmpが一覧されます。右端のハンバーガー・メニューを開き、事前承認済リクエストの作成を実行します。


有効期限を短期間(1日など)に設定し直し、事前承認済リクエストの作成を実行します。


事前承認済リクエストのURLが表示されるので、コピーしてどこかに保存しておきます。Autonomous Database側からは、このURLよりダンプ・ファイルを取得します。


URLのコピーができたら、ダイアログを閉じます。

出力されたダンプ・ファイルをインポートします。

最初にAutonomous Database側でもグラフのデータを削除しておきます。

SQLワークショプSQLコマンドより、SEM_APIS.DROP_SEM_NETWORKを実行します。これはOracle Database 21c XEで実行したコマンドと全く同じです。


Autonomous Databaseのデータベース・アクションに、管理者であるADMINで接続します。開発SQLを開きます。

オブジェクト・ストレージにあるダンプ・ファイルをディレクトリDATA_PUMP_DIRへダウンロードします。DBMS_CLOUD.GET_OBJECTを呼び出します。object_uriとして、先ほどコピーした事前承認済のURLを指定します。
-- 入れ替える場合は、すでにあるファイルを削除する。
begin
    dbms_cloud.delete_file(
        directory_name => 'DATA_PUMP_DIR'
        , file_name => 'rdf.dmp');
end;
/

begin
    dbms_cloud.get_object(
        object_uri => 'https://objectstorage.ap-tokyo-1.oraclecloud.com/p/事前承認済のURL/o/rdf.dmp',
        directory_name => 'DATA_PUMP_DIR'
    );
end;

DataPump APIを使って、インポート処理を行います。以前に記載したDataPump APIの紹介記事をもとに、以下のスクリプトを実行します。




実行結果は以下になりました。いくつかエラーや警告が出ていますが、これから行なう作業には影響はありませんでした。
Master table "ADMIN"."IMP_APEXDEV" successfully loaded/unloaded
Starting "ADMIN"."IMP_APEXDEV":  
Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processing object type SCHEMA_EXPORT/SEQUENCE/SEQUENCE
Processing object type SCHEMA_EXPORT/TABLE/TABLE
Table "APEXDEV"."NET1#RENAMED_APPTAB_RDF_MODEL_ID_1" exists. All dependent
metadata and data will be skipped due to table_exists_action of skip
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
. . imported "APEXDEV"."NET1#RDF_LINK$":"MODEL_2"        23.95 KB     219 rows
. . imported "APEXDEV"."NET1#RDF_VALUE$"                 23.67 KB      94 rows
*** Job percent done = 32
. . imported "APEXDEV"."NET1#RDF_RULEBASE$"              6.585 KB       9 rows
. . imported "APEXDEV"."NET1#RDF_CLIQUE$":"MODEL_0"          0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_COLLISION$"             5.070 KB       1 rows
*** Job percent done = 99
. . imported "APEXDEV"."NET1#RDF_CRS_URI$"               293.2 KB    5688 rows
. . imported "APEXDEV"."NET1#RDF_DELTA$":"MODEL_0"           0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_GRANT_INFO$"                0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_HIST$"                  7.328 KB       1 rows
. . imported "APEXDEV"."NET1#RDF_LINK$":"MODEL_0"            0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_LINK$":"MODEL_1"        12.67 KB      29 rows
. . imported "APEXDEV"."NET1#RDF_MODEL$_TBL"             8.109 KB       1 rows
. . imported "APEXDEV"."NET1#RDF_MODEL_INTERNAL$"        8.093 KB       1 rows
. . imported "APEXDEV"."NET1#RDF_NAMESPACE$"                 0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_NETWORK_INDEX_INTERNAL$"  14.77 KB      12 rows
. . imported "APEXDEV"."NET1#RDF_PARAMETER"              6.492 KB       2 rows
. . imported "APEXDEV"."NET1#RDF_PRECOMP$"               6.828 KB       1 rows
. . imported "APEXDEV"."NET1#RDF_PRECOMP_DEP$"           5.968 KB       3 rows
. . imported "APEXDEV"."NET1#RDF_PRED_STATS$":"MODEL_0"      0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_PRED_STATS$":"MODEL_1"      0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_RI_SHAD_2$"                 0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_RULE$"                  19.73 KB      20 rows
. . imported "APEXDEV"."NET1#RDF_SYSTEM_EVENT$"              0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_TERM_STATS$":"MODEL_0"      0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_TERM_STATS$":"MODEL_1"      0 KB       0 rows
. . imported "APEXDEV"."NET1#RDF_TS$"                        0 KB       0 rows
. . imported "APEXDEV"."NET1#SEM_INDEXTYPE_METADATA$"        0 KB       0 rows
Processing object type SCHEMA_EXPORT/TABLE/GRANT/OWNER_GRANT/OBJECT_GRANT
Processing object type SCHEMA_EXPORT/PROCEDURE/PROCEDURE
ORA-31684: Object type PROCEDURE:"APEXDEV"."NET1#SDO_RDF_PROC_DR_TRUNC_1"
already exists

Processing object type SCHEMA_EXPORT/PROCEDURE/GRANT/OWNER_GRANT/OBJECT_GRANT
Processing object type SCHEMA_EXPORT/PROCEDURE/ALTER_PROCEDURE
ORA-39111: Dependent object type
ALTER_PROCEDURE:"APEXDEV"."NET1#SDO_RDF_PROC_DR_TRUNC_1" skipped, base object
type PROCEDURE:"APEXDEV"."NET1#SDO_RDF_PROC_DR_TRUNC_1" already exists

Processing object type SCHEMA_EXPORT/VIEW/VIEW
ORA-31684: Object type VIEW:"APEXDEV"."FAMILY_RDF_DATA" already exists

Processing object type SCHEMA_EXPORT/VIEW/GRANT/OWNER_GRANT/OBJECT_GRANT
Processing object type SCHEMA_EXPORT/TABLE/INDEX/INDEX
Processing object type SCHEMA_EXPORT/TABLE/INDEX/FUNCTIONAL_INDEX/INDEX
Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type
SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICS
Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINT
Processing object type SCHEMA_EXPORT/VIEW/TRIGGER
Processing object type SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type SCHEMA_EXPORT/STATISTICS/MARKER
ORA-39082: Object type TRIGGER:"APEXDEV"."NET1#SDO_RDF_TRIG_AVIEW_1" created
with compilation warnings

*** Job percent done = 100
Job "ADMIN"."IMP_APEXDEV" completed with 4 error(s) at Thu Dec 2 06:54:12 2021
elapsed 0 00:00:25
Job has completed
Final job state = COMPLETED


PL/SQL procedure successfully completed.

Elapsed: 00:00:31.075

以上でRDFのデータのインポートが完了しました。


APEXアプリケーションの作成


人の名前を与えて、その人の子供と孫を一覧するAPEXアプリケーションを作成します。

アプリケーション作成ウィザードを実行し、空のアプリケーションを作成します。名前バインド変数を使ったRDF問合せとします。アプリケーションの作成をクリックします。


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

リージョンの作成を行います。識別名前Childrenタイプ対話モード・レポートを選択します。ソース位置ローカル・データベースタイプSQL問合せを選択します。SQL問合せには、Oracle Database 21c XEで生成した子供を一覧するSQLを設定します。


ページ・アイテムの作成を行います。識別名前P1_PERSONタイプテキスト・フィールドとします。ラベル人名とします。


RDFグラフを検索するSELECT文に与える値を保持するページ・アイテムP1_VIDP1_TERMを作成します。これらの値はP1_PERSONから導出されます。タイプ非表示にし、設置保護された値OFFにします。


対話モード・レポートChildrenを選択し、送信するページ・アイテムとしてP1_VIDP1_TERMを指定します。


もう一つ対話モード・レポートのリージョンを作成します。識別名前Grandchildrenタイプ対話モード・レポートとし、SQL問合せとして、孫を一覧するSQLを設定します。送信するページ・アイテムとしてP1_VIDP1_TERMを指定します。


ページ・アイテムP1_PERSONに値が設定されたら、それを元にP1_VIDとP1_TERMを設定する動的アクションを作成します。

ページ・アイテムP1_PERSON上で動的アイテムの作成を実行します。識別名前検索条件の導出とします。タイミングはデフォルトで、イベント変更選択タイプアイテムアイテムP1_PERSONになります。P1_PERSONが変更されたときに、アクションが実行されます。


TRUEアクション識別アクションとして、サーバー側のコードを実行を選びます。設定言語としてPL/SQLを選択し、PL/SQLコードとして以下を記述します。


送信するアイテムとしてP1_PERSON戻すアイテムとしてP1_VIDP1_TERMを指定します。


バインド変数に与える値がP1_VID、P1_TERMに設定されたので、対話モード・レポートのリージョンChildren、Grandchildrenをリフレッシュします。

TRUEアクションを作成し、識別アクションリフレッシュ影響を受ける要素タイプリージョンリージョンとしてChildrenを選択します。もうひとつ同じTRUEアクションを作成し、そちらはリージョンとしてGrandchildrenを選択します。


以上でアプリケーションは完成です。実行すると最初のGIF画面のように動作します。

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

Oracle RDF Graph Serverを実装すると、RDFを扱う一連の操作をREST API経由で呼び出すことができるようです。Autonomous Database単体では実行できなかったSPARQLも実行できます。Oracle RDF Graph Serverについてのマニュアルの記載はこちらです。SPARQL問合せキャッシュを参照すると、変換されたSQLにもアクセスできそうです。

グラフとして扱うことが自然なデータであれば、グラフとして扱う方がローコードのスタイルに合うように思います。幸いオラクル・データベースではグラフを扱うことができます。とはいえ、グラフの勉強は必要になります。

以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

追記
  1. DataPumpを使ったエクスポート/インポートを行う際に、ローカルのデータベースとAutonomous Databaseでバージョンを合わせていませんでした。ローカルが21c、Autonomous Databaseは19cです。しかし、19cのExpress Editionはリリースされていないので、21cか18cかの選択になります。ライセンスを購入している場合は19cを使うと良いかと思います。MDSYS.SDO_RDFパッケージに含まれるプロシージャに違いがあるようです。
  2. やはりDataPumpでのデータ移行は強引なので(当然、サポートもされない)、Autonomous Databaseではユーザーがルールベースを追加することはできない前提でRDFナレッジ・グラフを使うのが妥当と言えます。