7 RDF Semantic Graph support for Apache Jena

RDF Semantic Graph support for Apache Jena(以降、support for Apache Jenaとも記載)は、既知のJenaグラフ、JenaモデルおよびJena DatasetGraphのAPIを実装することによって、Oracle Spatial and Graph RDF Semantic GraphへのJavaベースのインタフェースを提供します。

ノート:

この機能は、以前はJena Adapter for Oracle DatabaseまたはJena Adapterと呼ばれていました。

support for Apache Jenaは、Oracle Database RDF/OWLのセマンティク・データ管理機能を拡張します。

(Apache Jenaはオープン・ソース・フレームワークです。ライセンスと著作権条件は、http://www.apache.org/licenses/およびhttp://www.apache.org/licenses/LICENSE-2.0を参照してください

名前付きグラフ・データ(クワッドとも呼ぶ)の管理には、DatasetGraph APIを使用します。また、RDF Semantic Graph support for Apache JenaをOracle Spatial and Graphのネットワークデータ・モデル・グラフ機能と統合することで、セマンティク・データでのネットワーク解析機能を利用できます。

「RDFセマンティク・グラフの概要」「OWLの概要」で説明されている主要な概念について十分に理解していることが前提です。Jena Javaフレームワークの性能全体についても十分に理解していることを前提とします。Jenaフレームワークの詳細は、http://jena.apache.org/の、特にJenaドキュメントのページを参照してください。ネットワーク分析関数を使用するには、『Oracle Spatial and Graphトポロジ・データ・モデルおよびネットワーク・データ・モデル・グラフ開発者ガイド』に説明されている、ネットワーク・データ・モデルのグラフ機能についても理解しておく必要があります。

ノート:

RDF Semantic Graph support for Apache Jenaの現在のリリースは、Apache Jena 3.1.0に対してテストされ、リリース19cデータベースのRDFスキーマプライベート・ネットワーク環境をサポートしています。オープンソース・プロジェクトの性質上、これよりも新しいバージョンのJenaでは、このSupport for Apache Jenaを使用しないでください。

Apache Josekiのサポートは、リリース19cデータベースのサポートとともにアダプタ・バージョン3.1.0のOTNキット・ディストリビューションに含まれていますが、推奨されていません。リリース19cではこのマニュアルからJosekiへの参照部分が削除されましたが、Josekiに関する情報は以前のバージョンのマニュアルに記載されています。

7.1 ソフトウェア環境の設定

Support for Apache Jenaを使用するには、システム環境に必要なソフトウェア(RDFセマンティク・グラフのサポートが有効になっているOracle Database、Apache Jena 3.12.0、JDK 1.8以降など)があることを、最初に確認しておく必要があります。

次の手順を実行することによって、ソフトウェア環境を設定できます。

  1. Oracle Database Enterprise Editionと、Oracle Spatial and GraphオプションおよびOracle Partitioningオプションをインストールします。

  2. 「RDFセマンティク・グラフ・サポートの有効化」の説明のとおり、RDFセマンティク・グラフのサポートを有効にします。

  3. RDF Semantic Graph support for Apache Jenaは、Oracle Software Delivery Cloudからダウンロードできます。

  4. キットを/tmp/jena_adapter(Linuxシステム)などの一時ディレクトリに解凍します。(この一時ディレクトリがまだ作成されていない場合は、解凍操作の前に作成します。)

    RDF Semantic Graph Support for Apache Jenaには、最上位に次のようなディレクトリがあります。

      |-- examples
      |-- fuseki
      |-- fuseki_web_app
      |-- jar
      |-- javadoc
      |-- joseki
      |-- joseki_web_app
      |-- protege_plugin
      |-- README
      |-- sparqlgateway_web_app
    
  5. JDK 1.8以上をインストールします(まだインストールされていない場合)。

  6. JAVA_HOME環境変数がJDKのインストールを参照していることを確認します。たとえば、次のようにします。

    setenv JAVA_HOME /usr/local/packages/jdk18/
    
  7. SPARQLプロトコルをサポートするためのSPARQLサービスが設定されていない場合は、「SPARQLサービスの設定」の説明のとおりに設定します。

ソフトウェア環境を設定した後、「RDFセマンティク・グラフ環境の設定」の説明にあるとおり、RDFセマンティク・グラフ環境でsupport for Apache Jenaを使用して問合せを実行できることを確認します。

7.1.1 以前のバージョンのsupport for Apache Jenaを使用していた場合

以前のバージョンのSupport for Apache Jenaを使用した場合は、ユーザースキーマで以前のJena Adapterによってインストールされたすべての関数/プロシージャを削除する必要があります。新しいキットをインストールすると、19cの新しいRDFスキーマプライベート・ネットワークと以前のリリースでのサポートの両方に対応する更新された関数とプロシージャが自動的にロードされます。

以前のJena Adapterで使用したユーザー・スキーマに接続し、次のコマンドを実行して内部関数およびプロシージャをクリーン・アップします。(これらのコマンドで参照される関数とプロシージャの一部が以前のインストールに存在しない場合もあるため、失敗したコマンドは無視して構いません。)

drop procedure ORACLE_ORARDF_S2SGETSRC;
drop procedure ORACLE_ORARDF_S2SGETSRCCLOB;
drop procedure ORACLE_ORARDF_S2SSVR;
drop procedure ORACLE_ORARDF_S2SSVRNG;
drop procedure ORACLE_ORARDF_S2SSVRNGCLOB;
drop procedure ORACLE_ORARDF_GRANT;
drop procedure ORACLE_ORARDF_VID2NAME_TYPE;
drop procedure ORACLE_ORARDF_S2SSVRNGNPV;
drop procedure ORACLE_ORARDF_S2SSVRNGCLOBNPV;
drop function ORACLE_ORARDF_SGC;
drop function ORACLE_ORARDF_SGCCLOB;
drop function ORACLE_ORARDF_S2SUSR;
drop function ORACLE_ORARDF_S2SUSRNG;
drop function ORACLE_ORARDF_S2SUSRNGL;
drop function ORACLE_ORARDF_S2SUSRNGCLOB;
drop function ORACLE_ORARDF_S2SLG;
drop function ORACLE_ORARDF_GETPLIST;
drop function ORACLE_ORARDF_RES2VID;
drop function ORACLE_ORARDF_VID2URI;

7.2 SPARQLサービスの設定

この項では、fuseki.warファイルをWebLogic Serverにデプロイすることにより、SPARQL Webサービス・エンドポイントを設定する方法について説明します。

WebLogic Serverにアプリケーションをデプロイするにはいくつかの方法がありますが、このトピックではautodeployオプションを取り上げます。

ノート:

WebLogic ServerのかわりにApache TomcatにFusekiをデプロイする場合は、「Apache TomcatにおけるFusekiのデプロイ」を参照してください。

  1. Oracle WebLogic Server 12c以上をダウンロードしてインストールします。

  2. Java 8以上がインストールされていることを確認します。

  3. Fusekiの構成ファイルの場所を定義するFUSEKI_BASEパラメータを設定します。デフォルトでは、このパラメータは/etc/fusekiに設定されています。

    このパラメータを、ダウンロードしたOTNキットのfusekiフォルダに設定できます。このフォルダにはfusekiの構成ファイルがすでに格納されています。詳細は、Jena Fusekiのドキュメント( https://jena.apache.org/documentation/fuseki2/fuseki-layout.html)を参照してください。

  4. fusekiの構成ファイル(config.ttl)でOracleデータセットを構成します。

    1. Fusekiの構成ファイルを編集する前に、RDFスキーマプライベート・ネットワークを作成します(「スキーマプライベート・セマンティク・ネットワーク」を参照)。たとえば、ネットワーク名をSAMPLE_NET、ユーザー・スキーマをRDFUSER、表領域をRDFTBSとした場合、次のコマンドを使用してセマンティク・ネットワークを作成します。

      EXECUTE SEM_APIS.CREATE_SEM_NETWORK('RDFTBS', options=>'MODEL_PARTITIONING=BY_HASH_P MODEL_PARTITIONS=16', network_owner=>'RDFUSER', network_name=>'SAMPLE_NET' );
    2. config.ttlファイルを編集し、M_NAMED_GRAPHSという名前のモデルを使用してoracle:Dataset定義を追加します。次のスニペットは、構成を示しています。oracle:allGraphsの述語は、SPARQLサービス・エンドポイントがM_NAMED_GRAPHSモデルに格納されているすべてのグラフを使用して問合せを提供することを意味します。

      <#oracle> rdf:type oracle:Dataset;
      oracle:connection
      [ a oracle:OracleConnection ;
      oracle:jdbcURL "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=<host>)(PORT=<port>))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=<service_name>)))";
      oracle:User "RDFUSER"
      oracle:Password "<password>"
      ];
      oracle:allGraphs [ oracle:firstModel "M_NAMED_GRAPHS";    
                         oracle:networkOwner "RDFUSER"; 
                         oracle:networkName "SAMPLE_NET"] .
      
    3. Fuseki構成ファイルのserviceセクションで、Oracleデータセットをリンクします。

      <#service> rdf:type fuseki:Service ;
          # URI of the dataset -- http://host:port/ds
          fuseki:name                     "oracle" ;
      
          # SPARQL query services e.g. http://host:port/ds/sparql?query=...
          fuseki:serviceQuery             "sparql" ;
          fuseki:serviceQuery             "query" ;
          # SPARQL Update service -- http://host:port/ds/update?request=...
          fuseki:serviceUpdate            "update" ;   # SPARQL query service -- /ds/update
      
          # Upload service -- http://host:port/ds/upload?graph=default or ?graph=URI or ?default
          # followed by a multipart body, each part being RDF syntax.
          # Syntax determined by the file name extension.
          fuseki:serviceUpload            "upload" ;   # Non-SPARQL upload service
      
          # SPARQL Graph store protocol (read and write)
          # GET, PUT, POST DELETE to http://host:port/ds/data?graph= or ?default=
          fuseki:serviceReadWriteGraphStore      "data" ;
      
          # A separate read-only graph store endpoint:
          fuseki:serviceReadGraphStore       "get" ;   # Graph store protocol (read only) -- /ds/get
      
          fuseki:dataset           <#oracle> ;
          .
      

    M_NAMED_GRAPHSモデルがまだ存在しない場合は、初回のSPARQL問合せリクエスト時に自動的に作成されます。トリプルおよびクワッドの例をいくつか追加して、名前付きグラフ機能をテストできます。たとえば、リリース19.3より前のデータベースの場合は次のようにします。

    SQL> CONNECT username/password
    SQL> INSERT INTO m_named_graphs_tpl VALUES(sdo_rdf_triple_s('m_named_graphs','<urn:s>','<urn:p>','<urn:o>'));
    SQL> INSERT INTO m_named_graphs_tpl VALUES(sdo_rdf_triple_s('m_named_graphs:<urn:G1>','<urn:g1_s>','<urn:g1_p>','<urn:g1_o>'));
    SQL> INSERT INTO m_named_graphs_tpl VALUES(sdo_rdf_triple_s('m_named_graphs:<urn:G2>','<urn:g2_s>','<urn:g2_p>','<urn:g2_o>'));
    SQL> COMMIT;
    
  5. 次のとおり、WebLogic Serverのautodeployディレクトリに移動し、ファイルをコピーします。(開発ドメインでのアプリケーションの自動デプロイに関する詳細は、http://docs.oracle.com/cd/E24329_01/web.1211/e24443/autodeploy.htmを参照してください。)

    cd <domain_name>/autodeploy
    cp -rf  /tmp/jena_adapter/fuseki_web_app/fuseki.war  <domain_name>/autodeploy
    

    前述の例で、<domain_name>はWebLogic Serverドメインの名前です。

    WebLogic Serverドメインを2つの異なるモード(開発とプロダクション)で実行することはできますが、autodeploy機能を使用できるのは開発モードのみであることに注意してください。

  6. Webブラウザを使用し、URLに書式http://< hostname>:7001/fusekiで接続して(Webアプリケーションはポート7001でデプロイされているとします)、デプロイメントを確認します。

    「Apache Jena Fuseki」というタイトルのページと、サーバー上のデータセットのリストが表示されます。この例では、/oracleデータセットが表示されます。

  7. 次の問合せを入力して/oracleデータセットの「問合せ」ボタンをクリックし、問合せを実行します。
    SELECT ?g ?s ?p ?o
    WHERE 
    { GRAPH ?g { ?s ?p ?o} }
    

    結果は、4つの列と2つの結果バインディング・セットがあるHTML表になります。

7.2.1 クライアント識別子

support for Apache Jenaによって作成または使用されたデータベース接続ごとに、クライアント識別子が関連付けられます。クライアント識別子は、特に、Real Application Cluster (Oracle RAC)環境で、パフォーマンス分析とチューニングを実行しているときに、他のデータベース・アクティビティからRDF Semantic Graph support for Apache Jena関連のアクティビティを特定するために有用です。

デフォルトで割り当てられるクライアント識別子は、JenaAdapterです。ただし、次の形式を使用してJava VM clientIdentifierプロパティを設定することによって、異なる値を指定できます。

-Doracle.spatial.rdf.client.jena.clientIdentifier=<identificationString>

データベース側でRDF Semantic Graph support for Apache Jena関連のアクティビティのみのトレースを開始するには、DBMS_MONITOR.CLIENT_ID_TRACE_ENABLEプロシージャを使用します。たとえば、次のようにします。

SQL> EXECUTE DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE('JenaAdapter', true, true);

7.2.2 アプリケーション表およびステージング表に対するOLTP圧縮の使用

デフォルトでは、RDF Semantic Graph support for Apache Jenaは、次の構文に従った基本的な表圧縮を使用して、アプリケーション表と任意のステージング表(後者は「 RDF Semantic Graph support for Apache Jenaを使用したバルク・ロード」で説明されているようにバルク・ロードに使用される)を作成します。

CREATE TABLE .... (... column definitions ...) ... compress;

ただし、データベースにOracle Advanced Compressionオプションの使用がライセンスされている場合は、次のJVMプロパティを設定してOLTP圧縮をオンにできますので、これによって基礎となるアプリケーション表とステージング表に対するすべてのDML操作時にデータが圧縮されます。

-Doracle.spatial.rdf.client.jena.advancedCompression="compress for oltp"

7.2.3 非ASCII文字のN-Tripleエンコーディング

RDFリソースの字句表現において、文字がOracle Databaseに挿入される際に、非ASCII文字に対して\uHHHH N-Tripleエンコーディングが使用されます。(N-Tripleエンコーディングの詳細は、http://www.w3.org/TR/rdf-testcases/#ntrip_grammarを参照してください。)SPARQL問合せの通常リソースのエンコーディングは、同じように処理されます。

\uHHHH N-Tripleエンコーディングを使用することによって、サポートされるUnicode文字セットがOracle Databaseで使用されていない場合でも、国際的な文字(ノルウェー語とスウェーデンの文字の混在など)のサポートが可能になります。

7.3 RDFセマンティク・グラフ環境の設定

support for Apache Jenaを使用して問合せを実行するには、(適切な権限を持つ)任意のユーザーで接続し、セマンティク・ネットワークで任意のモデルを使用します。

RDFセマンティク・グラフ環境が要件をすでに満たしている場合は、support for Apache Jenaを使用するJavaコードを直接コンパイルして実行することができます。support for Apache Jenaを使用できるようにRDFセマンティク・グラフ環境を設定していない場合は、次の例のようなステップを実行します。

  1. SYSTEMとして接続します。
    sqlplus system/<password-for-system>
    
  2. システム表の表領域を作成します。たとえば、次のようにします。
    CREATE TABLESPACE rdf_users datafile 'rdf_users01.dbf' 
        size 128M reuse autoextend on next 64M 
        maxsize unlimited segment space management auto;
    
  3. (セマンティク・ネットワークとsupport for Apache Jenaを使用するデータベースに接続するための)データベース・ユーザーを作成します。たとえば、次のようにします。
    CREATE USER rdfusr IDENTIFIED BY <password-for-udfusr>
                       DEFAULT TABLESPACE rdf_users;
    
  4. このデータベース・ユーザーに必要な権限を付与します。たとえば、次のようにします。
    GRANT connect, resource TO rdfusr;
    
  5. セマンティク・ネットワークを作成します。たとえば、次のようにします。

    Oracle Database 18c以前(すべてのRDFメタデータがMDSYSスキーマに格納される)の場合は、次のようにします。

    EXECUTE sem_apis.create_sem_network('RDF_USERS');
    

    Oracle Database 19c以降(スキーマプライベート・ネットワーク)の場合は、次のようにします。

    EXECUTE sem_apis.create_sem_network('RDF_USERS', network_owner=>'RDFUSR', network_name=>'LOCALNET');
    
  6. 自身のセマンティク・データとともにsupport for Apache Jenaを使用するには、「セマンティク・データの使用に関するクイック・スタート」の説明のとおり、適切なステップを実行してデータを格納し、モデルを作成して、データベース索引を作成します。その後、Javaコードをコンパイルして実行することで問合せを実行します;問合せの例については、「RDF Semantic Graph Support for Apache Jenaを使用した問合せの例」を参照してください。

    Support for Apache Jenaと提供されたサンプル・データの使用については、「RDF Semantic Graph Support for Apache Jenaを使用した問合せの例」を参照してください。

7.4 SEM_MATCHおよびRDF Semantic Graph support for Apache Jenaの問合せの比較

Oracle Databaseに格納されているセマンティク・データを問い合せる方法は2つあります。SEM_MATCHベースのSQL文とsupport for Apache Jenaを介したSPARQL問合せです。

それぞれの方法での問合せは、表面的には類似していますが、動作には重要な違いがあります。アプリケーションの動作を一貫性のあるものにするには、SEM_MATCH問合せとSPARQL問合せの違いを理解し、その問合せ結果を処理する際に注意が必要です。

2つの方法を次の簡単な例で示します。

問合せ1 (SEM_MATCHベース)

select s, p, o
    from table(sem_match('{?s ?p ?o}', sem_models('Test_Model'), ....))

問合せ2 (support for Apache Jenaを介したSPARQL問合せ)

select ?s ?p ?o
where {?s ?p ?o}

これらの2つの問合せは、同じ種類の機能を実行しますが、いくつか重要な違いがあります。問合せ1 (SEM_MATCHベース)は次のようになります。

  • Test_Modelからすべてのトリプルを読み取ります。

  • URI、bNode、プレーン・リテラルおよび型付きリテラルを区別せず、長いリテラルを処理しません。

  • 特定の文字('\n'など)はエスケープ解除しません。

問合せ2 (support for Apache Jenaを介して実行されるSPARQL問合せ)もTest_Modelからすべてのトリプルを読み取ります(同じ基礎となるTest_Modelを参照しているModelOracleSemに対してコールを実行したと想定します)。ただし、問合せ2は次のようになります。

  • (SEM_MATCH表関数による、spおよびo列のみではなく)追加の列を読み取り、URI、bNodes、プレーン・リテラル、型付きリテラルおよび長いリテラルを区別します。これによって、Jena Nodeオブジェクトを適切に作成できるようになります。

  • Oracle Databaseに格納される際にエスケープされる文字をエスケープ解除します。

2つの方法でのもう1つの違いは、空白ノード処理です。

  • SEM_MATCHベースの問合せにおいて、空白ノードは常に定数とみなされます。

  • SPARQL問合せにおいて、<>で囲まれていない 空白ノードは、問合せがSupport for Apache Jenaを介して実行されるときに変数とみなされます。これは、SPARQL標準セマンティクと一致します。ただし、<>囲まれた空白ノードは、問合せが実行されるときに定数とみなされ、空白ノード・ラベルには、基礎となるデータのモデル化で必要とされる適切な接頭辞が、support for Apache Jenaによって追加されます。

support for Apache Jena APIを使用して作成されるセマンティク・モデルの名前の最大長は、22文字です。

7.5 SEM_MATCHまたはSQLベースの問合せ結果からのユーザーフレンドリJavaオブジェクトの取得

セマンティク・グラフの問合せは、次のいずれかの方法を用いて行うことができます。

  • SPARQL (JavaメソッドまたはWebサービス・エンドポイントを使用)

  • SEM_MATCH (SPARQL問合せを埋め込んだ表関数)

  • SQL (MDSYS.RDFM_<model>ビューを問い合せて、MDSYS.RDF_VALUE$や他の表と結合する。スキーマプライベート・ネットワークを使用する場合は、<user>.<network_name>#RDFM<model>ビューを問い合せて、<user>.<network_name>#RDF_VALUE$や他の表と結合する)

Java開発者が使用しやすいのは、最初の方法の結果です。2番目と3番目の方法の結果はJava開発者にとっては使用しにくいかもしれません。型付きRDFリテラルとマッピングされた適切な型付きJavaオブジェクトを取得するために、様々な列を解析する必要があるためです。RDF Semantic Graph support for Apache Jenaは、JDBC結果セットから適切な型付きJavaオブジェクトを取得するタスクを簡略化できるように、いくつかのメソッドとヘルパー関数をサポートしています。これらのメソッドとヘルパー関数を次の例に示します。

これらの例は、次のコードのように一連の型付きリテラルが追加されるテスト表TGRAPH_TPLを使用しています(また、それに基づいてTGRAPHをモデル化しています)。

create table tgraph_tpl(triple sdo_rdf_triple_s);
exec sem_apis.create_sem_model('tgraph','tgraph_tpl','triple');
truncate table tgraph_tpl;
 
-- Add some triples
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s1>','<urn:p1>', '<urn:o1>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s2>','<urn:p2>', '"hello world"'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s3>','<urn:p3>', '"hello world"@en'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s4>','<urn:p4>', '" o1o "^^<http://www.w3.org/2001/XMLSchema#string>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s4>','<urn:p4>', '"xyz"^^<http://mytype>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s5>','<urn:p5>', '"123"^^<http://www.w3.org/2001/XMLSchema#integer>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s5>','<urn:p5>', '"123.456"^^<http://www.w3.org/2001/XMLSchema#double>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph','<urn:s6>','<urn:p6>', '_:bn1'));
 
-- Add some quads
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g1>','<urn:s1>','<urn:p1>', '<urn:o1>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s1>','<urn:p1>', '<urn:o1>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s2>','<urn:p2>', '"hello world"'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s3>','<urn:p3>', '"hello world"@en'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s4>','<urn:p4>', '" o1o "^^<http://www.w3.org/2001/XMLSchema#string>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s4>','<urn:p4>', '"xyz"^^<http://mytype>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s5>','<urn:p5>', '"123"^^<http://www.w3.org/2001/XMLSchema#integer>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s5>','<urn:p5>', '"123.456"^^<http://www.w3.org/2001/XMLSchema#double>'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s6>','<urn:p6>', '_:bn1'));
insert into tgraph_tpl values(sdo_rdf_triple_s('tgraph:<urn:g2>','<urn:s7>','<urn:p7>', '"2002-10-10T12:00:00-05:00"^^<http://www.w3.org/2001/XMLSchema#dateTime>'));

例7-1 SQLベースのグラフ問合せ

例7-1は、純粋なSQLベースのグラフ問合せを実行して、Jenaオブジェクトを作成します。

iTimeout = 0; // no time out
iDOP = 1;     // degree of parallelism
iStartColPos = 2;
queryString = "select 'hello'||rownum as extra, o.VALUE_TYPE,o.LITERAL_TYPE,o.LANGUAGE_TYPE,o.LONG_VALUE,o.VALUE_NAME "
            + "  from mdsys.rdfm_tgraph g, mdsys.rdf_value$ o where g.canon_end_node_id = o.value_id";
 
rs = oracle.executeQuery(queryString, iTimeout, iDOP, bindValues);  
 
while (rs.next()) {
  node = OracleSemIterator.retrieveNodeFromRS(rs, iStartColPos, OracleSemQueryPlan.CONST_FIVE_COL, translator);
  System.out.println("Result " + node.getClass().getName() + " = " + node + " " + rs.getString(1));
}

例7-1では、次のような出力が生成されます。

Result org.apache.jena.graph.Node_Literal = "123"^^http://www.w3.org/2001/XMLSchema#decimal hello1
Result org.apache.jena.graph.Node_Literal = "123"^^http://www.w3.org/2001/XMLSchema#decimal hello2
Result org.apache.jena.graph.Node_URI = urn:o1 hello3
Result org.apache.jena.graph.Node_URI = urn:o1 hello4
Result org.apache.jena.graph.Node_URI = urn:o1 hello5
Result org.apache.jena.graph.Node_Literal = "hello world" hello6
Result org.apache.jena.graph.Node_Literal = "hello world" hello7
Result org.apache.jena.graph.Node_Literal = "hello world"@en hello8
Result org.apache.jena.graph.Node_Literal = "hello world"@en hello9
Result org.apache.jena.graph.Node_Literal = " o1o " hello10
Result org.apache.jena.graph.Node_Literal = " o1o " hello11
Result org.apache.jena.graph.Node_Literal = "xyz"^^http://mytype hello12
Result org.apache.jena.graph.Node_Literal = "xyz"^^http://mytype hello13
Result org.apache.jena.graph.Node_Literal = "1.23456E2"^^http://www.w3.org/2001/XMLSchema#double hello14
Result org.apache.jena.graph.Node_Literal = "1.23456E2"^^http://www.w3.org/2001/XMLSchema#double hello15
Result org.apache.jena.graph.Node_Blank = m15mbn1 hello16
Result org.apache.jena.graph.Node_Blank = m15g3C75726E3A67323Egmbn1 hello17
Result org.apache.jena.graph.Node_Literal = "2002-10-10T17:00:00Z"^^http://www.w3.org/2001/XMLSchema#dateTime hello18

例7-2 SEM_MATCHと標準SQL構成要素が混在するハイブリッド問合せ

例7-2は、OracleSemIterator.retrieveNodeFromRS APIを使用してJenaオブジェクトを作成します。連続した5個の列を(値タイプ、リテラル・タイプ、言語タイプ、ロング値、値名の順序どおりに)読み取り、必要なエスケープ解除とオブジェクトのインスタンス化を実行します。この例では、SEM_MATCHを省いて、グラフ・ビューをMDSYS.RDF_VALUE$と直接結合しています。

iStartColPos = 1;
queryString = "select  g$RDFVTYP, g, count(1) as cnt " 
            + "  from table(sem_match('{ GRAPH ?g { ?s ?p ?o . } }',sem_models('tgraph'),null,null,null,null,null)) "
            + " group by g$RDFVTYP, g";
 
rs = oracle.executeQuery(queryString, iTimeout, iDOP, bindValues);  
while (rs.next()) {
  node = OracleSemIterator.retrieveNodeFromRS(rs, iStartColPos, OracleSemQueryPlan.CONST_TWO_COL, translator);
  System.out.println("Result " + node.getClass().getName() + " = " + node + " " + rs.getInt(iStartColPos + 2));
}

例7-2では、次のような出力が生成されます。

Result org.apache.jena.graph.Node_URI = urn:g2 9
Result org.apache.jena.graph.Node_URI = urn:g1 1

例7-2の内容:

  • Oracleクラスのヘルパー関数executeQueryがSQL文の実行に使用され、OracleSemIterator.retrieveNodeFromRS API (例7-1でも使用されている)がJenaオブジェクトの作成に使用されます。

  • 出力では2つの列、値タイプ(g$RDFVTYP)と値名(g)のみが使用されます。このg変数は決してリテラルRDFリソースにはならないことが明らかです。

  • 列の順序は重要です。2列からなる変数の場合、最初の列が値タイプ、2番目の列が値名であることが必要です。

例7-3 SEM_MATCH問合せ

例7-3は、SEM_MATCH問合せを実行し、Jenaオブジェクトのリストを返すイテレータ(OracleSemIteratorのインスタンス)を返します。

queryString = "select  g$RDFVTYP, g, s$RDFVTYP, s, p$RDFVTYP, p, o$RDFVTYP,o$RDFLTYP,o$RDFLANG,o$RDFCLOB,o "
            + "  from table(sem_match('{ GRAPH ?g { ?s ?p ?o . } }',sem_models('tgraph'),null,null,null,null,null))";
 
guide = new ArrayList<String>();
guide.add(OracleSemQueryPlan.CONST_TWO_COL);
guide.add(OracleSemQueryPlan.CONST_TWO_COL);
guide.add(OracleSemQueryPlan.CONST_TWO_COL);
guide.add(OracleSemQueryPlan.CONST_FIVE_COL);
 
rs = oracle.executeQuery(queryString, iTimeout, iDOP, bindValues); 
osi = new OracleSemIterator(rs); 
osi.setGuide(guide); 
osi.setTranslator(translator);
 
while (osi.hasNext()) {
  result = osi.next();
  System.out.println("Result " + result.getClass().getName() + " = " + result);
}

例7-3では、次のような出力が生成されます。

Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s5 2:urn:p5 3:"123"^^http://www.w3.org/2001/XMLSchema#decimal>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s5 2:urn:p5 3:"1.23456E2"^^http://www.w3.org/2001/XMLSchema#double>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s7 2:urn:p7 3:"2002-10-10T17:00:00Z"^^http://www.w3.org/2001/XMLSchema#dateTime>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s2 2:urn:p2 3:"hello world">
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s4 2:urn:p4 3:" o1o ">
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s4 2:urn:p4 3:"xyz"^^http://mytype>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s6 2:urn:p6 3:m15g3C75726E3A67323Egmbn1>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s1 2:urn:p1 3:urn:o1>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g1 1:urn:s1 2:urn:p1 3:urn:o1>
Result oracle.spatial.rdf.client.jena.Domain = <domain 0:urn:g2 1:urn:s3 2:urn:p3 3:"hello world"@en>

例7-3の内容:

  • OracleSemIteratorがJDBC結果セットを取得します。OracleSemIteratorには、SPARQL変数のバインド値を表すすべての列を解析するためのガイダンスが必要です。ガイドは単純な文字列値のリストです。2列の変数(主語と述語の位置)と5列の変数(目的語の位置)を区別する2つの定数が定義されています。トランスレータも必要です。

  • 出力では4つの変数が使用されます。最初の3つの変数はRDFリテラル・リソースではないため、CONST_TWO_COLがガイドとして使用されます。最後の変数はRDFリテラル・リソースになることができます。したがって、CONST_FIVE_COLがガイドとして使用されます。

  • 列の順序が重要です。例のとおりにする必要があります。

7.6 SPARQL問合せ処理の最適化

この項では、SPARQL問合せ処理を強化できる、support for Apache Jenaのいくつかのパフォーマンス関連機能について説明します。これらの機能は、デフォルトでは自動的に実行されます。

ここでは、CONSTRUCT機能およびプロパティ・パスなど、SPARQLについての知識があることを前提としています。

7.6.1 単一のSEM_MATCHコールへのSPARQL問合せのコンパイル

DISTINCT、OPTIONAL、FILTER、UNION、ORDER BYおよびLIMITを含むSPARQL問合せは、単一のOracle SEM_MATCH表関数に変換されます。SEM_MATCHでサポートされないSPARQL機能(CONSTRUCTなど)を使用しているために、問合せを直接SEM_MATCHに変換できない場合、support for Apache Jenaは、ハイブリッドな方法を採用し、単一のSEM_MATCH関数を使用して問合せの最も大きな部分を実行しながら、Jena ARQ問合せエンジンを使用して残りの部分の実行を試みます。

たとえば、次のSPARQL問合せは、単一のSEM_MATCH表関数に直接変換されます。

PREFIX dc:  <http://purl.org/dc/elements/1.1/> 
PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> 
PREFIX foaf: <http://xmlns.com/foaf/0.1/> 
SELECT ?person ?name 
  WHERE {
                 {?alice foaf:knows ?person . }
               UNION { 
                 ?person ?p ?name. OPTIONAL { ?person ?x ?name1 } 
                     }
        }

一方、次の問合せ例は、CONSTRUCTキーワードのため、単一のSEM_MATCH表関数に直接変換できません。

PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#> 
CONSTRUCT  { <http://example.org/person#Alice> vcard:FN ?obj } 
  WHERE  { { ?x <http://pred/a> ?obj.}
         UNION
         { ?x <http://pred/b> ?obj.}  }

この場合、support for Apache Jenaは、内側のUNION問合せを単一のSEM_MATCH表関数に変換し、その結果セットをさらに評価するため、Jena ARQ問合せエンジンに渡します。

7.6.2 プロパティ・パスの処理の最適化

Jenaに定義されているとおり、プロパティ・パスは2つのグラフ・ノード間でRDFグラフを介して使用可能なルートです。プロパティ・パスはSPARQLの拡張機能であり、RDFグラフのパターン・マッチングでプロパティに正規表現を使用できるため、基本的なグラフ・パターン問合せよりも表現力が豊かです。プロパティ・パスの詳細は、Jena ARQ問合せエンジンのドキュメントを参照してください。

support for Apache Jenaは、Jena ARQ問合せエンジンとの統合によって、すべてのJenaプロパティ・パス・タイプをサポートしますが、いくつかの一般的なパス・タイプは、パフォーマンスを向上するため、ネイティブSQL階層問合せ(SEM_MATCHベースではない)に直接変換されます。次のタイプのプロパティ・パスは、トリプル・データを処理する際、support for Apache JenaによってSQLに直接変換されます。

  • 述語選択肢: (p1 | p2 | … | pn) piはプロパティURIです。

  • 述語順序: (p1 / p2 / … / pn) piはプロパティURIです。

  • 逆パス: ( ^ p ) pは述語URIです。

  • 複雑なパス: p+、p*、p{0, n} pは選択肢、順序、逆パスまたはプロパティURIです。

この文法で対応できないパス表現は、support for Apache JenaによってSQLに直接変換することができず、Jena問合せエンジンを使用して応答されます。

次の例では、パス順序を使用したプロパティ・パス表現を使用するコード・スニペットが含まれます。

String m = "PROP_PATH";
 
ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, m);
 
GraphOracleSem graph = new GraphOracleSem(oracle, m);    
    
// populate the RDF Graph
    graph.add(Triple.create(Node.createURI("http://a"),
    Node.createURI("http://p1"),
    Node.createURI("http://b")));
 
graph.add(Triple.create(Node.createURI("http://b"),
 Node.createURI("http://p2"),
 Node.createURI("http://c")));
 
graph.add(Triple.create(Node.createURI("http://c"),
 Node.createURI("http://p5"),
 Node.createURI("http://d")));
 
String query =
" SELECT ?s  " +
" WHERE {?s (<http://p1>/<http://p2>/<http://p5>)+ <http://d>.}";
   
QueryExecution qexec = 
      QueryExecutionFactory.create(QueryFactory.create(query, 
 Syntax.syntaxARQ), model);
 
try {
  ResultSet results = qexec.execSelect();
  ResultSetFormatter.out(System.out, results);
}
finally {
  if (qexec != null)
    qexec.close();
}
     
OracleUtils.dropSemanticModel(oracle, m);
model.close();

7.7 他の機能をサポートするためのSPARQL構文への追加

RDF Semantic Graph support for Apache Jenaでは、ヒントと追加の問合せオプションを渡すことができます。問合せオプションを含むOracle固有の名前空間を使用してSPARQL名前空間接頭辞の構文をオーバーロードすることで、これらの機能を実装できます。

PREFIX ORACLE_SEM_xx_NS形式の名前空間では、xxの部分に機能の種別(ヒントならHTAPなら追加の述語など)が表されています。

7.7.1 SQLヒント

SQLヒントは、次の形式の行を含むSEM_MATCH問合せに渡すことができます。

PREFIX ORACLE_SEM_HT_NS: <http://oracle.com/semtech#hint>

ここでのhintは、SEM_MATCHによってサポートされる任意のヒントです。たとえば、次のようにします。

PREFIX ORACLE_SEM_HT_NS: <http://oracle.com/semtech#leading(t0,t1)> 
SELECT ?book ?title ?isbn     
WHERE { ?book <http://title> ?title. ?book <http://ISBN> ?isbn }

この例で、t0,t1は、問合せの1番目と2番目のパターンを指します。

SEM_MATCHと比較すると、ヒントの指定に若干違いがあることに注意してください。名前空間の値の構文の制限により、カンマ(,)は空白ではなく、t0t1(または他のヒント・コンポーネント)を区切るために使用されます。

SQLヒントの使用に関する詳細は、「SEM_MATCH表関数を使用したセマンティク・データの問合せ」を、特にoptions属性でのHINT0キーワードに関する資料を参照してください。

7.7.2 SPARQL問合せでのバインド変数の使用

Oracle Databaseでバインド変数を使用すると、問合せの解析時間を削減し、問合せ効率と同時実行性を向上させることができます。SPARQL問合せにおけるバインド変数のサポートは、ORACLE_SEM_FS_NSと同様の名前空間プラグマ指定を通して提供されます。

アプリケーションが2つのSPARQL問合せを実行する事例について考えます(2番目(問合せ2)は、1番目(問合せ1)の結果の一部または全部に応じて異なります)。バインド変数を必要としないいくつかの方法を次に示します。

  • 問合せ1の結果を繰り返して、1組の問合せを生成します。(ただし、この方法は、問合せ1の結果の数と同数の問合せを必要とします。)

  • 問合せ1の結果に基づいて、SPARQLフィルタ式を構築します。

  • 問合せ1を副問合せとみなします。

この場合のもう1つの方法は、次のサンプル・シナリオのように、バインド変数を使用することです。

Query 1:
 
  SELECT ?x
    WHERE { ... <some complex query> ... };
 
 
Query 2:
 
  SELECT ?subject ?x
    WHERE {?subject <urn:related> ?x .};

次の例は、support for Apache Jenaでバインド変数を使用するための構文を含む問合せ2を示します。

PREFIX ORACLE_SEM_FS_NS: <http://oracle.com/semtech#no_fall_back,s2s>
PREFIX ORACLE_SEM_UEAP_NS: <http://oracle.com/semtech#x$RDFVID%20in(?,?,?)>
PREFIX ORACLE_SEM_UEPJ_NS: <http://oracle.com/semtech#x$RDFVID>
PREFIX ORACLE_SEM_UEBV_NS: <http://oracle.com/semtech#1,2,3>
SELECT ?subject ?x
WHERE {
  ?subject <urn:related>  ?x
};

この構文では、次の名前空間が使用されています。

  • ORACLE_SEM_UEAP_NSはORACLE_SEM_AP_NSと同様ですが、ORACLE_SEM_UEAP_NSの値の部分はURLエンコードされています。値の部分は、使用する前にURLデコードされている必要があり、それによってSPARQL問合せへの追加述語とみなされます。

    この例では、URLデコーディングの後に、このORACLE_SEM_UEAP_NS接頭辞の値部分(#文字の後)が「x$RDFVID in(?,?,?)」になります。3つの疑問符は、問合せ1の3つの値に対する暗黙的なバインディングを示します。

  • ORACLE_SEM_UEPJ_NSは、関係する追加の投影を指定します。この場合、ORACLE_SEM_UEAP_NSがx$RDFVID列(問合せのSELECT句には出現しません)を参照するため、指定する必要があります。複数の投影は、カンマで区切ります。

  • ORACLE_SEM_UEBV_NSは、最初にURLエンコードされ、次に連結されてカンマで区切られたバインド値のリストを指定します。

前述の問合せ例は、次の非SPARQL構文の問合せと概念的に同じであり、1、2および3はバインド値とみなされます。

SELECT ?subject ?x
  WHERE {
    ?subject <urn:related>  ?x
  }
  AND ?x$RDFVID in (1,2,3);

前述の問合せ2のSPARQLの例で、3つの整数1、2および3は問合せ1からのものです。oext:build-uri-for-id関数を使用して、そのような内部の整数IDをRDFリソースのために生成することができます。次の例は、問合せ1からの内部整数IDを取得します。

PREFIX oext: <http://oracle.com/semtech/jena-adaptor/ext/function#>
SELECT ?x  (oext:build-uri-for-id(?x) as ?xid)
WHERE { ... <some complex query> ... };

?xidの値は、<rdfvid:整数値>の形式です。アプリケーションは、山カッコと文字列rdfvid:を無視して整数値を取り出し、これらを問合せ2に渡します。

もう1つの例として、単一の問合せ構造だが、多数の異なる定数がある可能性がある場合について考えてみます。たとえば、次のSPARQL問合せは、趣味を持っており、アプリケーションにログインしている各ユーザーの趣味を検索します。アプリケーションのユーザーは異なるURIを使用して表現されるため、このSPARQL問合せに対して様々なユーザーから様々な<uri>値が提供されます。

SELECT ?hobby
  WHERE { <uri> <urn:hasHobby> ?hobby };

1つの方法(バインド変数を使用しない)は、異なる<uri>値ごとに異なるSPARQL問合せを生成することです。たとえば、ユーザーJane Doeが、次のSPARQL問合せの実行をトリガーするとします。

SELECT ?hobby WHERE {
<http://www.example.com/Jane_Doe> <urn:hasHobby> ?hobby };

一方、もう1つの方法は、ユーザーJane Doeを指定している次の例のように、バインド変数を使用することです。

PREFIX ORACLE_SEM_FS_NS: <http://oracle.com/semtech#no_fall_back,s2s>
PREFIX ORACLE_SEM_UEAP_NS: <http://oracle.com/semtech#subject$RDFVID%20in(ORACLE_ORARDF_RES2VID(?))>
PREFIX ORACLE_SEM_UEPJ_NS: <http://oracle.com/semtech#subject$RDFVID>
PREFIX ORACLE_SEM_UEBV_NS: <http://oracle.com/semtech#http%3a%2f%2fwww.example.com%2fJohn_Doe>
SELECT ?subject ?hobby
  WHERE {
    ?subject <urn:hasHobby>  ?hobby
  };

前述の問合せ例は、次の非SPARQL構文の問合せと概念的に同じであり、http://www.example.com/Jane_Doeはバインド変数とみなされます。

SELECT ?subject ?hobby
WHERE {
  ?subject <urn:hasHobby>  ?hobby
}
AND ?subject$RDFVID in (ORACLE_ORARDF_RES2VID('http://www.example.com/Jane_Doe'));

この例で、ORACLE_ORARDF_RES2VIDは、URIとリテラルを内部整数ID表現に変換する関数です。この関数が自動的に作成されるのは、Oracleデータベースに接続するためにsupport for Apache Jenaが使用されるときです。

7.7.3 追加のWHERE句述語

SEM_MATCH filter属性に、WHEREキーワードのないWHERE句の形式の文字列として、追加の選択基準を指定することができます。次の形式の1行を含めることで、追加のWHERE句述語をSEM_MATCH問合せに渡すことができます。

PREFIX ORACLE_SEM_AP_NS: <http://oracle.com/semtech#pred>

predは、問合せに追加されるWHERE句の内容を反映します。たとえば、次のようにします。

PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ORACLE_SEM_AP_NS:<http://www.oracle.com/semtech#label$RDFLANG='fr'>  
SELECT DISTINCT ?inst ?label
  WHERE { ?inst a <http://someCLass>. ?inst rdfs:label ?label . }
  ORDER BY (?label) LIMIT 20

この例では、ラベル変数の言語形式が'fr'である必要がある問合せに、制限事項が追加されます。

7.7.4 追加の問合せオプション

追加の問合せオプションは、次の形式の行を含むSEM_MATCH問合せに渡すことができます。

PREFIX ORACLE_SEM_FS_NS: <http://oracle.com/semtech#option>

optionは、問合せに追加される問合せオプション(またはカンマで区切られた複数の問合せオプション)を反映します。たとえば、次のようにします。

PREFIX ORACLE_SEM_FS_NS:   
<http://oracle.com/semtech#timeout=3,dop=4,INF_ONLY,ORDERED,ALLOW_DUP=T>
SELECT * WHERE {?subject ?property ?object }

次の問合せオプションがサポートされています。

  • ALLOW_DUP=tは、結果が重複する場合がありますが、複数のセマンティク・モデルを問い合せるための、より高速な方法です。

  • BEST_EFFORT_QUERY=tが、TIMEOUT=nオプションとともに使用されると、SPARQL問合せに対して、n秒以内に検出されるすべての一致が返されます。

  • DEGREE=nは、問合せに対して並列度(n)を文レベルで指定します。マルチコアまたはマルチCPUのプロセッサで、異なるDOP値(4または8など)を試すと、パフォーマンスが向上する場合があります。

    DEGREEと比較すると、DOPはセッション・レベルで並列度を指定します。DEGREEDOPよりも処理オーバーヘッドが少ないため、support for Apache Jenaでの使用にはDEGREEが推奨されます。

  • DOP=nは、問合せに対して並列度(n)をセッション・レベルで指定します。マルチコアまたはマルチCPUのプロセッサで、異なるDOP値(4または8など)を試すと、パフォーマンスが向上する場合があります。

  • FETCH_SIZE=nは、JDBCフェッチ・サイズ・パラメータ(データベースとの1回の通信で結果セットから読み取ってメモリーに格納する行数)を指定します。このパラメータを使用してパフォーマンスを向上させることができます。値を大きくすると、すべての結果を取得するのに必要なデータベースとの通信回数が減少します。デフォルト値は1000です。

  • INF_ONLYを指定すると、推論されたモデルのみが問い合せられます。

  • JENA_EXECUTORは、SEM_MATCH (またはネイティブSQL)に対してSPARQL問合せのコンパイルを無効にし、かわりにJenaネイティブ問合せエグゼキュータが使用されます。

  • JOIN=nは、SPARQL SERVICEコールからフェデレーテッド問合せへの結果を、どのように問合せの他の部分と結合できるかを指定します。フェデレーテッド問合せとJOINオプションに関する詳細は、「JOINオプションとフェデレーテッド問合せ」を参照してください。

  • NO_FALL_BACKは、SQL例外が発生したときに、基礎となる問合せ実行エンジンがJena実行メカニズムにフォール・バックしないようにします。

  • ODS=nは、動的なサンプリングのレベルを、文レベルで指定します。(動的なサンプリングの説明については、『Oracle Database SQLチューニング・ガイド』の動的なサンプリングによる統計の見積りに関する説明を参照してください。)nの有効値は、1から10までです。たとえば、複雑な問合せにはODS=3を試すことができます。

  • ORDEREDは、最後に必要なRDF_VALUE$結合を実行している間、問合せトリプル・パターン結合のために主要なSQLヒントに変換されます。

  • PLAIN_SQL_OPT=Fは問合せを直接SQLにネイティブ・コンパイルする機能を無効にします。

  • QID=nは、問合せID番号を指定します。この機能は、問合せに応答がない場合、その問合せを取り消すために使用できます。

  • RESULT_CACHEは、問合せにOracle RESULT_CACHEディレクティブを使用します。

  • REWRITE=Fは、SEM_MATCH表関数に対してODCI_Table_Rewriteを無効にします。

  • S2S (SPARQL to pure SQL)は、基礎となるSEM_MATCHベースの問合せ、またはSPARQL問合せに基づいて生成された問合せを、SEM_MATCH表関数を使用しないで、さらにSQL問合せに変換します。結果として生成されたSQL問合せはOracleコストベース・オプティマイザによって実行され、その結果はクライアントに渡される前にsupport for Apache Jenaによって処理されます。S2Sオプションの利点と使用方法などの詳細は、「S2Sオプションの利点と使用方法の情報」を参照してください。

    S2Sは、すべてのSPARQL問合せについてデフォルトで使用可能です。S2Sを無効にするには、次のJVMシステム・プロパティを設定します。

    -Doracle.spatial.rdf.client.jena.defaultS2S=false
    
  • SKIP_CLOB=Tは、問合せに対してCLOB値が戻されないようにします。

  • STRICT_DEFAULT=Fは、デフォルトのグラフがトリプルを名前付きグラフに含められるようにします。デフォルトでは、STRICT_DEFAULT=Tは、データセット情報が指定されない場合に、デフォルト・グラフを名前なしトリプルに制限します。

  • TIMEOUT=n(問合せタイムアウト)は、問合せが、終了されるまで実行する秒数(n)を指定します。SPARQL問合せから生成される基礎となるSQLは、多数の一致を戻し、副問合せおよび割当てと同様の機能を使用することができ、これらのすべてには、多くの時間がかかることがあります。TIMEOUTBEST_EFFORT_QUERY=tオプションは、問合せに過度な処理時間がかからないようにするために使用できます。

7.7.4.1 JOINオプションとフェデレーテッド問合せ

SPARQLフェデレーテッド問合せは、W3Cドキュメントの説明のとおり、分散データ上の問合せであり、そのために、1つのソースを問い合せて、取得した情報を使用して次のソースの問合せを制約します。詳細は、『SPARQL 1.1フェデレーテッド問合せ』 (http://www.w3.org/2009/sparql/docs/fed/service)を参照してください。

JOINオプション(「追加の問合せオプション」を参照)とSERVICEキーワードは、Support for Apache Jenaを使用するフェデレーテッド問合せで使用できます。たとえば、次の問合せを考えてみます。

SELECT ?s ?s1 ?o
 WHERE { ?s1 ?p1 ?s .
                    {
                     SERVICE <http://sparql.org/books> { ?s ?p ?o }
                    }
                 }

ローカルの問合せ部分(?s1 ?p1 ?s,)が非常に選択的である場合、次の問合せに示すように、join=2を指定します。

PREFIX ORACLE_SEM_FS_NS:   <http://oracle.com/semtech#join=2>
SELECT ?s ?s1 ?o
 WHERE { ?s1 ?p1 ?s .
                    {
                     SERVICE <http://sparql.org/books> { ?s ?p ?o }
                    }
                 }

この場合、ローカルの問合せ部(?s1 ?p1 ?s,)は、Oracle Databaseに対してローカルに実行されます。その後、結果からの?sの各バインディングは、SERVICE部分(リモート問合せ部)にプッシュされ、コールはエンドポイントが指定したサービスに行われます。この方法は、概念的にはネステッド・ループ結合と似ています。

リモート問合せ部(?s ?s1 ?o)が非常に選択的である場合、次の問合せに示すように、join=3を指定することで、リモート部分は最初に実行され、結果はローカルの部分の実行をドライブするために使用されます。

PREFIX ORACLE_SEM_FS_NS:   <http://oracle.com/semtech#join=3>
SELECT ?s ?s1 ?o
 WHERE { ?s1 ?p1 ?s .
                    {
                     SERVICE <http://sparql.org/books> { ?s ?p ?o }
                    }
                  }

この場合、単一のコールは遠隔サービス・エンドポイントに対して行われ、?sの各バインディングは、ローカルの問合せをトリガーします。join=2と同様に、この方法は概念的にはネステッド・ループベースの結合ですが、違いは順序が切り替えられるということです。

ローカルの問合せ部もリモートの問合せ部もあまり選択的でない場合、次の問合せに示すように、join=1を選択できます。

PREFIX ORACLE_SEM_FS_NS:   <http://oracle.com/semtech#join=1>
SELECT ?s ?s1 ?o
 WHERE { ?s1 ?p1 ?s .
                    {
                     SERVICE <http://sparql.org/books> { ?s ?p ?o }
                    }
                }

この場合、リモートの問合せ部とローカルの部分は独立して実行され、結果はJenaによって結合されます。この方法は、概念的にはハッシュ結合と似ています。

フェデレーテッド問合せをデバッグまたはトレースするには、Oracle JDeveloperのHTTPアナライザを使用して、基礎となるSERVICEコールを調べます。

7.7.4.2 S2Sオプションの利点と使用方法の情報

S2Sオプション(「追加の問合せオプション」参照)には、潜在的に次の利点があります。

  • RESULT_CACHEオプションと連携し、問合せパフォーマンスを向上します。S2SRESULT_CACHEオプションを使用すると、特に、頻繁に実行される問合せに有用です。

  • SEM_MATCH表関数の解析時間を削減し、多くの動的に生成されたSPARQL問合せを含むアプリケーションに有用です。

  • 問合せ本体の4000バイトの制限(SEM_MATCH表関数の最初のパラメータ)を除外し、より長く複雑な問合せがサポートされます。

S2Sオプションは、内部のインメモリー・キャッシュを、変換されたSQL問合せ文で使用されるようにします。この内部キャッシュのデフォルト・サイズは1024(つまり1024のSQL問合せ)ですが、サイズは、次のJava VMプロパティを使用して調整できます。

-Doracle.spatial.rdf.client.jena.queryCacheSize=<size>

7.7.5 中間層リソース・キャッシング

セマンティク・データが格納されると、すべてのリソース値がIDにハッシュされ、トリプル表に格納されます。値IDからの完全なリソース値へのマッピングは、MDSYS.RDF_VALUE$表またはスキーマプライベートのRDF_VALUE$表に格納されます。問合せ時、選択された変数ごとにOracle DatabaseでRDF_VALUE$表による結合を実行し、リソースを取得する必要があります。

ただし、結合の数を減らすため、中間層キャッシュ・オプションを使用して、値IDとリソース値の間のマッピングを格納するために中間層でのインメモリー・キャッシュが使用されるようにできます。この機能を使用するには、SPARQL問合せに次のPREFIXプラグマを含めます。

PREFIX ORACLE_SEM_FS_NS: <http://oracle.com/semtech#midtier_cache>

インメモリー・キャッシュの最大サイズ(バイト)を制御するには、oracle.spatial.rdf.client.jena.cacheMaxSizeシステム・プロパティを使用します。デフォルトの最大キャッシュ・サイズは、1GBです。

中間層リソース・キャッシングは、ORDER BYまたはDISTINCT(あるいは両方とも)構造体を使用した問合せ、または複数の投影変数による問合せに最も効果的です。中間層キャッシュは、「追加の問合せオプション」で指定したその他のオプションと組み合せることができます。

モデルのすべてのリソースをキャッシュに事前挿入するには、GraphOracleSem.populateCacheメソッドまたはDatasetGraphOracleSem.populateCacheメソッドを使用します。どちらのメソッドも、内部の中間層キャッシュを構築するために使用されるスレッドの数を指定するパラメータをとります。どちらかのメソッドをパラレルに実行すると、複数のCPU(コア)を搭載したマシンで、パフォーマンスを構築しているキャッシュを大幅に増やすことができます。

7.8 RDF Semantic Graph support for Apache JenaによりSPARQL問合せでサポートされる関数

Support for Apache Jenaを介したSPARQL問合せでは、次に示す種類の関数を使用できます。

  • Jena ARQ問合せエンジンの関数ライブラリにある関数

  • 投影変数に関するOracle Databaseのネイティブ関数

  • ユーザー定義関数

7.8.1 ARQ関数ライブラリの関数

support for Apache Jenaを介したSPARQL問合せは、Jena ARQ問合せエンジンの関数ライブラリにある関数を使用できます。これらの問合せは、中間層で実行されます。

次の例は、upper-case関数とnamespace関数を使用します。これらの例で、接頭辞fn<http://www.w3.org/2005/xpath-functions#>、接頭辞afn<http://jena.hpl.hp.com/ARQ/function#>です。

PREFIX  fn: <http://www.w3.org/2005/xpath-functions#>
PREFIX  afn: <http://jena.hpl.hp.com/ARQ/function#>
SELECT (fn:upper-case(?object) as ?object1)
WHERE { ?subject dc:title ?object }

PREFIX  fn: <http://www.w3.org/2005/xpath-functions#>
PREFIX  afn: <http://jena.hpl.hp.com/ARQ/function#>
SELECT ?subject (afn:namespace(?object) as ?object1)
WHERE { ?subject <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ?object } 

7.8.2 射影された変数に対するネイティブなOracle Database関数

support for Apache Jenaを介したSPARQL問合せは、射影変数に関するOracle Databaseのネイティブ・ファンクションを使用できます。これらの問合せとファンクションは、データベース内部で実行されます。この項で説明したファンクションは、ARQファンクション(「ARQファンクション・ライブラリのファンクション」を参照)とともには使用しないでください。

この項では、サポートされるネイティブ関数の一覧と、いくつかの例を示します。例で、接頭辞oextは、< http://oracle.com/semtech/jena-adaptor/ext/function#>です。

ノート:

前述のURLで、jena-adaptorの綴りに注意してください(これは、既存のアプリケーションとの互換性のために保持されており、問合せではこれを使用する必要があります)。Oracleドキュメントのスタイル・ガイドに従い、通常のテキストではadapterいう綴りを使用します。

  • oext:upper-literalは、リテラル値(長いリテラルを除く)を大文字に変換します。たとえば、次のようにします。

    PREFIX  oext: <http://oracle.com/semtech/jena-adaptor/ext/function#>
    SELECT (oext:upper-literal(?object) as ?object1)
    WHERE { ?subject dc:title ?object }
    
  • oext:lower-literalは、リテラル値(長いリテラルを除く)を小文字に変換します。たとえば、次のようにします。

    PREFIX  oext: <http://oracle.com/semtech/jena-adaptor/ext/function#>
    SELECT (oext:lower-literal(?object) as ?object1)
    WHERE { ?subject dc:title ?object }
    
  • oext:build-uri-for-idは、URI、bNodeまたはリテラルの値IDをURIフォームに変換します。たとえば、次のようにします。

    PREFIX  oext: <http://oracle.com/semtech/jena-adaptor/ext/function#>
    SELECT (oext:build-uri-for-id(?object) as ?object1)
    WHERE { ?subject dc:title ?object }
    

    出力の例は、次のようになります。<rdfvid:1716368199350136353>

    この関数の1つの用途は、Javaアプリケーションがメモリー内で、それらの値IDからURI、bNodeまたはリテラルの字句形式へのマッピングを保持できるようにすることです。MDSYS.RDF_VALUE$表は、Oracle Databaseでこのようなマッピングを提供します。

    変数?varについて、oext:build-uri-for-id(?var)のみが投影される場合、問合せへの応答に必要な内部表の結合操作がより少なくなるため、問合せのパフォーマンスはより高速になります。

  • oext:literal-strlenは、リテラル値(長いリテラルを除く)の長さを返します。たとえば、次のようにします。

    PREFIX  oext: <http://oracle.com/semtech/jena-adaptor/ext/function#>
    SELECT (oext:literal-strlen(?object) as ?objlen)
    WHERE { ?subject dc:title ?object }

7.8.3 ユーザー定義関数

support for Apache Jenaを介したSPARQL問合せは、データベースに格納されているユーザー定義関数を使用できます。

次の例で、長いリテラル(CLOB)および短いリテラルを処理する文字列長関数(my_strlen)を定義するとします。SPARQL問合せ側では、この関数をouext(http://oracle.com/semtech/jena-adaptor/ext/user-def-function#)という名前空間の下で参照できます。

PREFIX  ouext: <http://oracle.com/semtech/jena-adaptor/ext/user-def-function#>
SELECT ?subject ?object (ouext:my_strlen(?object) as ?obj1)
WHERE { ?subject dc:title ?object }

データベース内では、この機能を実装するために、my_strlenmy_strlen_clmy_strlen_lamy_strlen_ltおよびmy_strlen_vtの関数を定義します。概念的に、これらのファンクションの戻り値は、表7-1に示すようにマップされます。

表7-1 my_strlenの関数と戻り値の例

関数名 戻り値

my_strlen

<VAR>

my_strlen_cl

<VAR>$RDFCLOB

my_strlen_la

<VAR>$RDFLANG

my_strlen_lt

<VAR>$RDFLTYP

my_strlen_vt

<VAR>$RDFVTYP

SPARQLから参照できる1つのユーザー定義関数は、(MDSYS.RDF_VALUE$内の)RDFリソースの内部表現で位置合せするため、これを実装するために一連の関数(合計5つ)が使用されます。RDFリソースの値、言語、リテラル・タイプ、LONG値および値タイプに関して記述する5つの主要な列があり、これらの5つの列はSEM_MATCHを使用して選択されます。この場合、ユーザー定義関数は、5つの列で表される1つのRDFリソースをもう1つのRDFリソースに単純に変換します。

これらの関数は、次のように定義されます。

create or replace function my_strlen(rdfvtyp in varchar2,
                              rdfltyp in varchar2,
                              rdflang in varchar2,
                              rdfclob in clob,
                              value   in varchar2
                              ) return varchar2
 as
   ret_val  varchar2(4000);
 begin
   -- value
   if (rdfvtyp = 'LIT') then
     if (rdfclob is null) then
       return length(value);
     else
       return dbms_lob.getlength(rdfclob);
     end if;
   else
     -- Assign -1 for non-literal values so that application can
     -- easily differentiate
     return '-1';
   end if;
 end;
 /
 
 create or replace function my_strlen_cl(rdfvtyp in varchar2,
                              rdfltyp in varchar2,
                              rdflang in varchar2,
                              rdfclob in clob,
                              value   in varchar2
                              ) return clob
 as
 begin
   return null;
 end;
 /
 
 create or replace function my_strlen_la(rdfvtyp in varchar2,
                              rdfltyp in varchar2,
                              rdflang in varchar2,
                              rdfclob in clob,
                              value   in varchar2
                              ) return varchar2
 as
 begin
   return null;
 end;
 /
 
 create or replace function my_strlen_lt(rdfvtyp in varchar2,
                              rdfltyp in varchar2,
                              rdflang in varchar2,
                              rdfclob in clob,
                              value   in varchar2
                              ) return varchar2
 as
   ret_val  varchar2(4000);
 begin
   -- literal type
   return 'http://www.w3.org/2001/XMLSchema#integer';
 end;
 /
 
 create or replace function my_strlen_vt(rdfvtyp in varchar2,
                              rdfltyp in varchar2,
                              rdflang in varchar2,
                              rdfclob in clob,
                              value   in varchar2
                              ) return varchar2
 as
   ret_val  varchar2(3);
 begin
   return 'LIT';
 end;
 /

ユーザー定義関数には、VARCHAR2タイプのパラメータを指定することもできます。次の5つの関数は、ともに、部分文字列に整数(VARCHAR2形式)を受け入れ、部分文字列を戻すmy_shorten_str関数を定義します。(この例の部分文字列は12文字で、4000バイト以下である必要があります。)

-- SPARQL query that returns the first 12 characters of literal values.
-- 
PREFIX  ouext: <http://oracle.com/semtech/jena-adaptor/ext/user-def-function#>
SELECT (ouext:my_shorten_str(?object, "12") as ?obj1) ?subject
WHERE { ?subject dc:title ?object }
 
create or replace function my_shorten_str(rdfvtyp in varchar2,
                            rdfltyp in varchar2,
                            rdflang in varchar2,
                            rdfclob in clob,
                            value   in varchar2,
                            arg     in varchar2
                            ) return varchar2
as
 ret_val  varchar2(4000);
begin
 -- value
 if (rdfvtyp = 'LIT') then
   if (rdfclob is null) then
     return substr(value, 1, to_number(arg));
   else
     return dbms_lob.substr(rdfclob, to_number(arg), 1);
   end if;
 else
   return null;
 end if;
end;
/
 
create or replace function my_shorten_str_cl(rdfvtyp in varchar2,
                            rdfltyp in varchar2,
                            rdflang in varchar2,
                            rdfclob in clob,
                            value   in varchar2,
                            arg     in varchar2
                            ) return clob
as
 ret_val  clob;
begin
 -- lob
 return null;
end;
/
 
create or replace function my_shorten_str_la(rdfvtyp in varchar2,
                            rdfltyp in varchar2,
                            rdflang in varchar2,
                            rdfclob in clob,
                            value   in varchar2,
                            arg     in varchar2
                            ) return varchar2
as
 ret_val  varchar2(4000);
begin
 -- lang
 if (rdfvtyp = 'LIT') then
   return rdflang;
 else
   return null;
 end if;
end;
/
 
create or replace function my_shorten_str_lt(rdfvtyp in varchar2,
                            rdfltyp in varchar2,
                            rdflang in varchar2,
                            rdfclob in clob,
                            value   in varchar2,
                            arg     in varchar2
                            ) return varchar2
as
 ret_val  varchar2(4000);
begin
 -- literal type
 ret_val := rdfltyp;
 return ret_val;
end;
/
 
create or replace function my_shorten_str_vt(rdfvtyp in varchar2,
                            rdfltyp in varchar2,
                            rdflang in varchar2,
                            rdfclob in clob,
                            value   in varchar2,
                            arg     in varchar2
                            ) return varchar2
as
 ret_val  varchar2(3);
begin
 return 'LIT';
end;
/

7.9 SPARQLの更新のサポート

RDF Semantic Graph support for Apache Jenaは、SPARQLの更新(SPARULとも呼ばれる)(http://www.w3.org/TR/sparql11-update/)をサポートしています。

主要なプログラミングAPIには、Jenaクラスorg.apache.jena.update.UpdateAction、RDF Semantic Graph support for Apache JenaクラスGraphOracleSemおよびDatasetGraphOracleSemが含まれます。例7-4は、SPARQLの更新操作によって、名前付きグラフ<http://example/graph>のすべてのトリプルが、データベースに格納されている関連したモデルから削除されることを示しています。

例7-4 単純なSPARQL更新

GraphOracleSem graphOracleSem = .... ;
DatasetGraphOracleSem dsgos = DatasetGraphOracleSem.createFrom(graphOracleSem);
 
// SPARQL Update operation
String szUpdateAction = "DROP GRAPH <http://example/graph>";
 
// Execute the Update against a DatasetGraph instance (can be a Jena Model as well)
UpdateAction.parseExecute(szUpdateAction, dsgos);

Oracle Databaseでは、空の名前付きグラフに関する情報は保持されないことに注意してください。このことは、このグラフにトリプルを追加せずにCREATE GRAPH <graph_name>を起動した場合、アプリケーション表または基礎となるRDF_LINK$表に、追加の行が作成されないことを意味します。Oracle Databaseに対しては、例7-4の例に示すとおり、CREATE GRAPHのステップを安全にスキップできます。

例7-5 挿入と削除の操作によるSPARQLの更新

例7-5は、複数の挿入操作と削除操作を含むSPARQLの更新操作(ARQ 2.8.8から)を示しています。

PREFIX : <http://example/>
CREATE GRAPH <http://example/graph> ;
INSERT DATA { :r :p 123 } ;
INSERT DATA { :r :p 1066 } ;
DELETE DATA { :r :p 1066 } ;
INSERT DATA {
  GRAPH <http://example/graph> { :r :p 123 . :r :p 1066 }
} ;
DELETE DATA {
  GRAPH <http://example/graph>  { :r :p 123 }
}

空のDatasetGraphOracleSemに対して例7-5での更新操作を実行した後、SPARQL問合せSELECT ?s ?p ?o WHERE {?s ?p ?o}を実行すると、次のレスポンスが生成されます。

-----------------------------------------------------------------------------------------------
| s                  | p                  | o                                                 |
===============================================================================================
| <http://example/r> | <http://example/p> | "123"^^<http://www.w3.org/2001/XMLSchema#decimal> |
-----------------------------------------------------------------------------------------------

同じデータを使用して、SPARQL問合せSELECT ?g ?s ?p ?o where {GRAPH ?g {?s ?p ?o}}を実行するとは、次のレスポンスが生成されます。

-------------------------------------------------------------------------------------------------------------------------
| g                      | s                  | p                  | o                                                  |
=========================================================================================================================
| <http://example/graph> | <http://example/r> | <http://example/p> | "1066"^^<http://www.w3.org/2001/XMLSchema#decimal> |
-------------------------------------------------------------------------------------------------------------------------

7.10 RDFデータの解析関数

oracle.spatial.rdf.client.jenaパッケージのSemNetworkAnalystクラスを使用して、RDFデータで解析関数を実行することができます。

このサポートは、ネットワークデータ・モデル・グラフのロジックを基礎となるRDFデータ構造体に統合します。そのため、RDFデータでの解析関数を使用するには、『Oracle Spatial and Graphトポロジ・データ・モデルおよびネットワーク・データ・モデル・グラフ開発者ガイド』に説明されている、ネットワーク・データ・モデルのグラフ機能についても理解しておく必要があります。

必要なNDM Javaライブラリ(sdonm.jarsdoutl.jarを含む)は、ディレクトリ$ORACLE_HOME/md/jlibの下にあります。xmlparserv2.jar($ORACLE_HOME/xdk/libの下)を、classpath定義に含める必要があることに注意してください。

例7-6 RDFデータ上の解析ファンクションの実行

例7-6では、SemNetworkAnalystクラス(NDM NetworkAnalyst APIを内部的に使用)を使用します。

Oracle oracle = new Oracle(jdbcUrl, user, password);
GraphOracleSem graph = new GraphOracleSem(oracle, modelName);
 
Node nodeA = Node.createURI("http://A");
Node nodeB = Node.createURI("http://B");
Node nodeC = Node.createURI("http://C");
Node nodeD = Node.createURI("http://D");
Node nodeE = Node.createURI("http://E");
Node nodeF = Node.createURI("http://F");
Node nodeG = Node.createURI("http://G");
Node nodeX = Node.createURI("http://X");
 
// An anonymous node
Node ano = Node.createAnon(new AnonId("m1"));
 
Node relL = Node.createURI("http://likes");
Node relD = Node.createURI("http://dislikes");
Node relK = Node.createURI("http://knows");
Node relC = Node.createURI("http://differs");
 
graph.add(new Triple(nodeA, relL, nodeB));
graph.add(new Triple(nodeA, relC, nodeD));
graph.add(new Triple(nodeB, relL, nodeC));
graph.add(new Triple(nodeA, relD, nodeC));
 
graph.add(new Triple(nodeB, relD, ano));
graph.add(new Triple(nodeC, relL, nodeD));
graph.add(new Triple(nodeC, relK, nodeE));
graph.add(new Triple(ano,   relL, nodeD));
graph.add(new Triple(ano,   relL, nodeF));
graph.add(new Triple(ano,   relD, nodeB));
 
// X only likes itself
graph.add(new Triple(nodeX, relL, nodeX));
 
graph.commitTransaction();
HashMap<Node, Double> costMap = new HashMap<Node, Double>();
costMap.put(relL, Double.valueOf((double)0.5));
costMap.put(relD, Double.valueOf((double)1.5));
costMap.put(relC, Double.valueOf((double)5.5));
 
graph.setDOP(4); // this allows the underlying LINK/NODE tables
                 // and indexes to be created in parallel.
 
SemNetworkAnalyst sna = SemNetworkAnalyst.getInstance(
    graph,   // network data source
    true,    // directed graph
    true,    // cleanup existing NODE and LINK table
    costMap
    );
 
psOut.println("From nodeA to nodeC");
Node[] nodeArray = sna.shortestPathDijkstra(nodeA, nodeC);
printNodeArray(nodeArray, psOut);
 
psOut.println("From nodeA to nodeD"); 
nodeArray = sna.shortestPathDijkstra( nodeA, nodeD);
printNodeArray(nodeArray, psOut);
 
psOut.println("From nodeA to nodeF");
nodeArray = sna.shortestPathAStar(nodeA, nodeF);
printNodeArray(nodeArray, psOut);
 
psOut.println("From ano to nodeC");
nodeArray = sna.shortestPathAStar(ano, nodeC);
printNodeArray(nodeArray, psOut);
 
psOut.println("From ano to nodeX");
nodeArray = sna.shortestPathAStar(ano, nodeX);
printNodeArray(nodeArray, psOut);
 
graph.close();
oracle.dispose();
...
...
   
// A helper function to print out a path
public static void printNodeArray(Node[] nodeArray, PrintStream psOut)
{
  if (nodeArray == null) {
    psOut.println("Node Array is null");
    return;
  }
  if (nodeArray.length == 0) {psOut.println("Node Array is empty"); }
  int iFlag = 0;
  psOut.println("printNodeArray: full path starts");
  for (int iHops = 0; iHops < nodeArray.length; iHops++) {
    psOut.println("printNodeArray: full path item " + iHops + " = "
        + ((iFlag == 0) ? "[n] ":"[e] ") + nodeArray[iHops]);
    iFlag = 1 - iFlag;
  }
}

例7-6の内容:

  • GraphOracleSemオブジェクトが構築され、少数のトリプルがGraphOracleSemオブジェクトに追加されます。これらのトリプルは、複数の個人と、好き嫌い知人および他人などの関係を記述します。

  • コスト・マッピングは、数値コストの値を異なるリンク/述語(RDFグラフの)に割り当てるために作成されます。この場合、0.5、1.5および5.5は、それぞれに述語likesdislikesおよびdiffersに割り当てられます。このコスト・マッピングはオプションです。マッピングがないとき、すべての述語は同じコスト1に割り当てられることになります。コスト・マッピングが指定される際、このマッピングは完全である必要はなく、コスト・マッピングに含まれない述語には、デフォルト値の1が割り当てられます。

例7-6の出力は、次のとおりです。この出力には、指定された開始ノードと終了ノードの最短経路が示されます。sna.shortestPathAStar(ano, nodeX)の戻り値は、これらの2つのノード間には経路がないため、NULLになります。

From nodeA to nodeC
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://A           ## "n" denotes Node             
printNodeArray: full path item 1 = [e] http://likes       ## "e" denotes Edge (Link)
printNodeArray: full path item 2 = [n] http://B
printNodeArray: full path item 3 = [e] http://likes
printNodeArray: full path item 4 = [n] http://C
 
From nodeA to nodeD
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://A
printNodeArray: full path item 1 = [e] http://likes
printNodeArray: full path item 2 = [n] http://B
printNodeArray: full path item 3 = [e] http://likes
printNodeArray: full path item 4 = [n] http://C
printNodeArray: full path item 5 = [e] http://likes
printNodeArray: full path item 6 = [n] http://D
 
From nodeA to nodeF
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://A
printNodeArray: full path item 1 = [e] http://likes
printNodeArray: full path item 2 = [n] http://B
printNodeArray: full path item 3 = [e] http://dislikes
printNodeArray: full path item 4 = [n] m1
printNodeArray: full path item 5 = [e] http://likes
printNodeArray: full path item 6 = [n] http://F
 
From ano to nodeC
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] m1
printNodeArray: full path item 1 = [e] http://dislikes
printNodeArray: full path item 2 = [n] http://B
printNodeArray: full path item 3 = [e] http://likes
printNodeArray: full path item 4 = [n] http://C
 
From ano to nodeX
Node Array is null

基礎となるRDFグラフ・ビュー(SEMM_<model_name>またはRDFM_<model_name>)は、NDM関数で直接使用することができないため、必要な表(指定されたRDFグラフに導出されるノードとリンクを含む)は、SemNetworkAnalystで作成されます。これらの表は、RDFグラフが変更されても自動更新されないため、SemNetworkAnalyst.getInstancecleanupパラメータにtrueを設定し、古いノードを削除して表をリンクさせ、更新された表を再構築します。

例7-7 セマンティク・データ上のNDM nearestNeighborsファンクションの実装

例7-7は、セマンティク・データでのNDM nearestNeighborsファンクションを実装します。これによってSemNetworkAnalystインスタンスからNetworkAnalystオブジェクトを取得し、ノードIDを取得してPointOnNetオブジェクトを作成し、LogicalSubPathオブジェクトを処理します。

%cat TestNearestNeighbor.java 
 
import java.io.*;
import java.util.*;
import org.apache.jena.graph.*;
import org.apache.jena.update.*;
import oracle.spatial.rdf.client.jena.*;
import oracle.spatial.rdf.client.jena.SemNetworkAnalyst;
import oracle.spatial.network.lod.LODGoalNode;
import oracle.spatial.network.lod.LODNetworkConstraint;
import oracle.spatial.network.lod.NetworkAnalyst;
import oracle.spatial.network.lod.PointOnNet;
import oracle.spatial.network.lod.LogicalSubPath;
 
/**
 * This class implements a nearestNeighbors function on top of semantic data
 * using public APIs provided in SemNetworkAnalyst and Oracle Spatial NDM
 */
public class TestNearestNeighbor
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
 
    PrintStream psOut = System.out;
 
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    
    String szModelName = "test_nn";
    // First construct a TBox and load a few axioms
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
    String insertString =  
      " PREFIX my:  <http://my.com/> " +
      " INSERT DATA "                             +
      " { my:A   my:likes my:B .                " +
      "   my:A   my:likes my:C .                " +
      "   my:A   my:knows my:D .                " +
      "   my:A   my:dislikes my:X .             " +
      "   my:A   my:dislikes my:Y .             " +
      "   my:C   my:likes my:E .                " +
      "   my:C   my:likes my:F .                " +
      "   my:C   my:dislikes my:M .             " +
      "   my:D   my:likes my:G .                " +
      "   my:D   my:likes my:H .                " +
      "   my:F   my:likes my:M .                " +
      " }   ";
    UpdateAction.parseExecute(insertString,  model);
 
    GraphOracleSem g = model.getGraph();
    g.commitTransaction();
    g.setDOP(4);
 
    HashMap<Node, Double> costMap = new HashMap<Node, Double>();
    costMap.put(Node.createURI("http://my.com/likes"),    Double.valueOf(1.0));
    costMap.put(Node.createURI("http://my.com/dislikes"), Double.valueOf(4.0));
    costMap.put(Node.createURI("http://my.com/knows"),    Double.valueOf(2.0));
 
    SemNetworkAnalyst sna = SemNetworkAnalyst.getInstance(
        g,     // source RDF graph
        true,  // directed graph
        true,  // cleanup old Node/Link tables
        costMap
        );
 
    Node nodeStart = Node.createURI("http://my.com/A");
    long origNodeID = sna.getNodeID(nodeStart);
 
    long[] lIDs = {origNodeID};
 
    // translate from the original ID
    long nodeID = (sna.mapNodeIDs(lIDs))[0]; 
 
    NetworkAnalyst networkAnalyst = sna.getEmbeddedNetworkAnalyst();
 
    LogicalSubPath[] lsps = networkAnalyst.nearestNeighbors(
      new PointOnNet(nodeID),      // startPoint
      6,                           // numberOfNeighbors
      1,                           // searchLinkLevel
      1,                           // targetLinkLevel
      (LODNetworkConstraint) null, // constraint
      (LODGoalNode) null           // goalNodeFilter
      );
 
    if (lsps != null) {
      for (int idx = 0; idx < lsps.length; idx++) {
        LogicalSubPath lsp = lsps[idx];
        Node[] nodePath = sna.processLogicalSubPath(lsp, nodeStart);
        psOut.println("Path " + idx);
        printNodeArray(nodePath, psOut);
      }
    }
 
    g.close();
    sna.close();
    oracle.dispose();
  }
 
 
  public static void printNodeArray(Node[] nodeArray, PrintStream psOut)
  {
    if (nodeArray == null) {
      psOut.println("Node Array is null");
      return;
    }
    if (nodeArray.length == 0) {
      psOut.println("Node Array is empty");
    }
    int iFlag = 0;
    psOut.println("printNodeArray: full path starts");
    for (int iHops = 0; iHops < nodeArray.length; iHops++) {
      psOut.println("printNodeArray: full path item " + iHops + " = "
          + ((iFlag == 0) ? "[n] ":"[e] ") + nodeArray[iHops]);
      iFlag = 1 - iFlag;
    }
  }
}

例7-7の出力は、次のとおりです。

Path 0
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://my.com/A
printNodeArray: full path item 1 = [e] http://my.com/likes
printNodeArray: full path item 2 = [n] http://my.com/C
 
Path 1
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://my.com/A
printNodeArray: full path item 1 = [e] http://my.com/likes
printNodeArray: full path item 2 = [n] http://my.com/B
 
Path 2
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://my.com/A
printNodeArray: full path item 1 = [e] http://my.com/knows
printNodeArray: full path item 2 = [n] http://my.com/D
 
Path 3
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://my.com/A
printNodeArray: full path item 1 = [e] http://my.com/likes
printNodeArray: full path item 2 = [n] http://my.com/C
printNodeArray: full path item 3 = [e] http://my.com/likes
printNodeArray: full path item 4 = [n] http://my.com/E
 
Path 4
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://my.com/A
printNodeArray: full path item 1 = [e] http://my.com/likes
printNodeArray: full path item 2 = [n] http://my.com/C
printNodeArray: full path item 3 = [e] http://my.com/likes
printNodeArray: full path item 4 = [n] http://my.com/F
 
Path 5
printNodeArray: full path starts
printNodeArray: full path item 0 = [n] http://my.com/A
printNodeArray: full path item 1 = [e] http://my.com/knows
printNodeArray: full path item 2 = [n] http://my.com/D
printNodeArray: full path item 3 = [e] http://my.com/likes
printNodeArray: full path item 4 = [n] http://my.com/H

7.10.1 グラフにおけるパスの文脈情報の生成

パスそのものに加え、グラフ内のパスに関する文脈情報を参照することは、有用な場合があります。SemNetworkAnalystクラスのbuildSurroundingSubGraphメソッドは、DOTファイル(グラフ記述言語ファイル、拡張子.gv)を、指定されたWriterオブジェクトに出力できます。パスのノードごとに、最大10の直接の隣接が、パス周辺のサブグラフを生成するために使用されます。次の例は、文脈情報(特に例7-6で使用する解析ファンクションからの出力)を持つDOTファイルの作成方法を示したものです。

nodeArray = sna.shortestPathDijkstra(nodeA, nodeD);
printNodeArray(nodeArray, psOut);
 
FileWriter dotWriter = new FileWriter("Shortest_Path_A_to_D.gv");
sna.buildSurroundingSubGraph(nodeArray, dotWriter);

先の例から生成される出力DOTファイルは、次の例に示すように単純な形をしています。

% cat Shortest_Path_A_to_D.gv
digraph { rankdir = LR; charset="utf-8"; 
 
"Rhttp://A" [ label="http://A" shape=rectangle,color=red,style = filled, ];
"Rhttp://B" [ label="http://B" shape=rectangle,color=red,style = filled, ];
"Rhttp://A" -> "Rhttp://B" [ label="http://likes"  color=red, style=bold, ];
"Rhttp://C" [ label="http://C" shape=rectangle,color=red,style = filled, ];
"Rhttp://A" -> "Rhttp://C" [ label="http://dislikes" ];
"Rhttp://D" [ label="http://D" shape=rectangle,color=red,style = filled, ];
"Rhttp://A" -> "Rhttp://D" [ label="http://differs" ];
"Rhttp://B" -> "Rhttp://C" [ label="http://likes"  color=red, style=bold, ];
"Rm1" [ label="m1" shape=ellipse,color=blue, ];
"Rhttp://B" -> "Rm1" [ label="http://dislikes" ];
"Rm1" -> "Rhttp://B" [ label="http://dislikes" ];
"Rhttp://C" -> "Rhttp://D" [ label="http://likes"  color=red, style=bold, ];
"Rhttp://E" [ label="http://E" shape=ellipse,color=blue, ];
"Rhttp://C" -> "Rhttp://E" [ label="http://knows" ];
"Rm1" -> "Rhttp://D" [ label="http://likes" ];
}

SemNetworkAnalystクラスとGraphOracleSemクラスのメソッドを使用して、より洗練された視覚表現で解析関数出力を生成することもできます。

前述のDOTファイルを、様々なイメージ形式に変換できます。図7-1は、前述のDOTファイルの情報を表現するイメージです。

図7-1 解析関数出力のビジュアル表示

図7-1の説明が続きます
「図7-1 解析ファンクション出力の視覚表現」の説明

7.11 サーバー側APIのサポート

この項では、RDF Semantic Graph support for Apache Jenaによって使用可能になるRDFセマンティク・グラフ機能の一部について説明します。

使用可能な機能をサポートするAPIコールの包括的なドキュメントについては、RDF Semantic Graph support for Apache Jenaのリファレンス情報(Javadoc)を参照してください。support for Apache Jenaによって使用可能なサーバー側機能の詳細は、このマニュアルの関連する章を参照してください。

7.11.1 仮想モデルのサポート

仮想モデル(「仮想モデル」を参照)は、GraphOracleSemコンストラクタで指定され、透過的に処理されます。仮想モデルがモデルとルールベースの組合せのために存在している場合には、問合せの応答に使用されます。そのような仮想モデルが存在しない場合には、データベースに作成されます。

ノート:

support for Apache Jenaを介した仮想モデルのサポートは、Oracle Databaseリリース11.2以上でのみ使用可能です。

次の例では、既存の仮想モデルを再利用します。

String modelName = "EX";
String m1 = "EX_1";
 
ModelOracleSem defaultModel = 
  ModelOracleSem.createOracleSemModel(oracle, modelName);
 
// create these models in case they don't exist
ModelOracleSem model1 = ModelOracleSem.createOracleSemModel(oracle, m1);
 
String vmName = "VM_" + modelName;
 
 
//create a virtual model containing EX and EX_1
oracle.executeCall(
"begin sem_apis.create_virtual_model(?,sem_models('"+ m1 + "','"+ modelName+"'),null); end;",vmName);
 
String[] modelNames = {m1};
String[] rulebaseNames = {};
 
Attachment attachment = Attachment.createInstance(modelNames, rulebaseNames, 
InferenceMaintenanceMode.NO_UPDATE, QueryOptions.ALLOW_QUERY_VALID_AND_DUP);
 
// vmName is passed to the constructor, so GraphOracleSem will use the virtual 
// model named vmname (if the current user has read privileges on it)
GraphOracleSem graph = new GraphOracleSem(oracle, modelName, attachment, vmName);
graph.add(Triple.create(Node.createURI("urn:alice"),
                        Node.createURI("http://xmlns.com/foaf/0.1/mbox"),
                        Node.createURI("mailto:alice@example")));
ModelOracleSem model = new ModelOracleSem(graph);	  
 
String queryString =
 
   " SELECT ?subject ?object WHERE { ?subject ?p ?object } ";
 
Query query = QueryFactory.create(queryString) ;
QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
 
try {
   ResultSet results = qexec.execSelect() ;
   for ( ; results.hasNext() ; ) {
      QuerySolution soln = results.nextSolution() ;
      psOut.println("soln " + soln);
   }
} 
finally { 
   qexec.close() ; 
}
 
OracleUtils.dropSemanticModel(oracle, modelName);
OracleUtils.dropSemanticModel(oracle, m1);
 
oracle.dispose();

また、次の例のように、GraphOracleSemコンストラクタを使用して仮想モデルを作成することもできます。

GraphOracleSem graph = new GraphOracleSem(oracle, modelName, attachment, true);

この例で、4つ目のパラメータ(true)は、指定されたmodelNameattachmentのために仮想モデルを作成する必要があることを示しています。

7.11.2 接続プーリングのサポート

Oracle Database接続プーリングは、support for Apache JenaのOraclePoolクラスを介して提供されます。このクラスが初期化されると、使用可能な接続のプールからOracleオブジェクトを戻すことができます。Oracleオブジェクトは、基本的にデータベース接続ラッパーです。disposeがOracleオブジェクトでコールされた後、接続がプールに戻されます。OraclePoolの使用に関する詳細は、APIリファレンス情報(Javadoc)を参照してください。

次の例では、5つの初期接続を使用するOraclePoolオブジェクトを設定します。

public static void main(String[] args) throws Exception
  {	
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
 
    // test with connection properties 
    java.util.Properties prop = new java.util.Properties();
    prop.setProperty("MinLimit", "2");     // the cache size is 2 at least 
    prop.setProperty("MaxLimit", "10");
    prop.setProperty("InitialLimit", "2"); // create 2 connections at startup
    prop.setProperty("InactivityTimeout", "1800");    //  seconds
    prop.setProperty("AbandonedConnectionTimeout", "900");  //  seconds
    prop.setProperty("MaxStatementsLimit", "10");
    prop.setProperty("PropertyCheckInterval", "60"); // seconds
 
    System.out.println("Creating OraclePool");
    OraclePool op = new OraclePool(szJdbcURL, szUser, szPasswd, prop, 
               "OracleSemConnPool");
    System.out.println("Done creating OraclePool");
 
    // grab an Oracle and do something with it
    System.out.println("Getting an Oracle from OraclePool");
    Oracle oracle = op.getOracle();
    System.out.println("Done");
    System.out.println("Is logical connection:" +
        oracle.getConnection().isLogicalConnection());
    GraphOracleSem g = new GraphOracleSem(oracle, szModelName);
    g.add(Triple.create(Node.createURI("u:John"), 
                        Node.createURI("u:parentOf"), 
                        Node.createURI("u:Mary")));
    g.close();
    // return the Oracle back to the pool
    oracle.dispose();
    
    // grab another Oracle and do something else 
    System.out.println("Getting an Oracle from OraclePool");
    oracle = op.getOracle();
    System.out.println("Done");
    System.out.println("Is logical connection:" +
        oracle.getConnection().isLogicalConnection());
    g = new GraphOracleSem(oracle, szModelName);
    g.add(Triple.create(Node.createURI("u:John"), 
                        Node.createURI("u:parentOf"), 
                        Node.createURI("u:Jack")));
    g.close();
    
    OracleUtils.dropSemanticModel(oracle, szModelName); 
    
    // return the Oracle object back to the pool
    oracle.dispose();
}

7.11.3 セマンティク・モデルのPL/SQLインタフェース

support for Apache Jenaを介して、複数のセマンティクPL/SQLサブプログラムを使用することができます。表7-2に、サブプログラムと、対応するJavaクラスおよびメソッドを示します。

表7-2 PL/SQLサブプログラムと対応するRDF Semantic Graph support for Apache JenaのJavaクラスおよびメソッド

PL/SQLサブプログラム 対応するJavaクラスとメソッド

SEM_APIS.DROP_SEM_MODEL

OracleUtils.dropSemanticModel

SEM_APIS.MERGE_MODELS

OracleUtils.mergeModels

SEM_APIS.SWAP_NAMES

OracleUtils.swapNames

SEM_APIS.REMOVE_DUPLICATES

OracleUtils.removeDuplicates

SEM_APIS.RENAME_MODEL

OracleUtils.renameModels

これらのPL/SQLユーティリティ・サブプログラムの詳細は、「SEM_APISパッケージのサブプログラム」のリファレンス情報を参照してください。対応するJavaクラスとメソッドの詳細は、RDF Semantic Graph support for Apache JenaのAPIリファレンス・ドキュメント(Javadoc)を参照してください。

7.11.4 推論オプション

Attachmentクラス(パッケージoracle.spatial.rdf.client.jena)の次のメソッドを使用し、伴意コールにオプションを追加できます。

public void setUseLocalInference(boolean useLocalInference)
public boolean getUseLocalInference()
 
public void setDefGraphForLocalInference(String defaultGraphName)
public String getDefGraphForLocalInference()
 
public String getInferenceOption()
public void setInferenceOption(String inferenceOption)

例7-8 推論オプションの指定

これらのメソッドの詳細は、Javadocを参照してください。

例7-8では、伴意の作成に、パラレル推論(並列度4を使用)とRAW形式を有効化しています。また、伴意の作成に、performInferenceメソッドを使用(SEM_APIS.CREATE_ENTAILMENT PL/SQLプロシージャの使用と同じ)します。

import java.io.*;
import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.update.*;
import org.apache.jena.sparql.core.DatasetImpl;
 
public class TestNewInference
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
 
    PrintStream psOut = System.out;
 
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    
    String szTBoxName = "test_new_tbox";
    {
      // First construct a TBox and load a few axioms
      ModelOracleSem modelTBox = ModelOracleSem.createOracleSemModel(oracle, szTBoxName);
      String insertString =  
        " PREFIX my:  <http://my.com/> " +
        " PREFIX rdfs:  <http://www.w3.org/2000/01/rdf-schema#> " +
        " INSERT DATA "                                     +
        " { my:C1  rdfs:subClassOf my:C2 .                " +
        "   my:C2  rdfs:subClassOf my:C3 .                " +
        "   my:C3  rdfs:subClassOf my:C4 .                " +
        " }   ";
      UpdateAction.parseExecute(insertString,  modelTBox);
      modelTBox.close();
    }
 
    String szABoxName = "test_new_abox";
    {
      // Construct an ABox and load a few quads
      ModelOracleSem modelABox = ModelOracleSem.createOracleSemModel(oracle, szABoxName);
      DatasetGraphOracleSem dataset = DatasetGraphOracleSem.createFrom(modelABox.getGraph());
      modelABox.close();
 
      String insertString =  
        " PREFIX my:    <http://my.com/> " +
        " PREFIX rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
        " INSERT DATA                                                 " +
        " { GRAPH my:G1 { my:I1  rdf:type my:C1 .                     " +
        "                 my:I2  rdf:type my:C2 .                     " +
        "               }                                             " +
        " };                                                          " +
        " INSERT DATA                                                 " +
        " { GRAPH my:G2 { my:J1  rdf:type my:C3 .                     " +
        "               }                                             " +
        " }    ";
      UpdateAction.parseExecute(insertString,  dataset);
      dataset.close();
    }
 
 
    String[] attachedModels = new String[1];
    attachedModels[0] = szTBoxName;
 
    String[] attachedRBs = {"OWL2RL"};
 
    Attachment attachment = Attachment.createInstance(
        attachedModels, attachedRBs,
        InferenceMaintenanceMode.NO_UPDATE,
        QueryOptions.ALLOW_QUERY_INVALID);
 
    // We are going to run named graph based local inference
    attachment.setUseLocalInference(true);
 
    // Set the default graph (TBox)
    attachment.setDefGraphForLocalInference(szTBoxName);
 
    // Set the inference option to use parallel inference 
    // with a degree of 4, and RAW format.
    attachment.setInferenceOption("DOP=4,RAW8=T");
 
    GraphOracleSem graph = new GraphOracleSem(
        oracle, 
        szABoxName, 
        attachment
        );
    DatasetGraphOracleSem dsgos = DatasetGraphOracleSem.createFrom(graph);
    graph.close();
 
    // Invoke create_entailment PL/SQL API
    dsgos.performInference();
 
    psOut.println("TestNewInference: # of inferred graph " + 
        Long.toString(dsgos.getInferredGraphSize()));
 
    String queryString = 
      " SELECT ?g ?s ?p ?o WHERE {  GRAPH ?g {?s ?p ?o } } " ;
 
    Query query = QueryFactory.create(queryString, Syntax.syntaxARQ);
    QueryExecution qexec = QueryExecutionFactory.create(
        query, DatasetImpl.wrap(dsgos));
    ResultSet results = qexec.execSelect();
 
    ResultSetFormatter.out(psOut, results);
 
    dsgos.close();
    oracle.dispose();
  }
}

例7-8の出力は、次のとおりです。

TestNewInference: # of inferred graph 9
 
--------------------------------------------------------------------------------------------------------------------
| g                  | s                  | p                                                 | o                  |
====================================================================================================================
| <http://my.com/G1> | <http://my.com/I2> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C3> |
| <http://my.com/G1> | <http://my.com/I2> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C2> |
| <http://my.com/G1> | <http://my.com/I2> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C4> |
| <http://my.com/G1> | <http://my.com/I1> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C3> |
| <http://my.com/G1> | <http://my.com/I1> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C1> |
| <http://my.com/G1> | <http://my.com/I1> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C2> |
| <http://my.com/G1> | <http://my.com/I1> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C4> |
| <http://my.com/G2> | <http://my.com/J1> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C3> |
| <http://my.com/G2> | <http://my.com/J1> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://my.com/C4> |
--------------------------------------------------------------------------------------------------------------------

推論の詳しい使用方法は、「OWL推論の使用方法」を参照してください。

7.11.5 非推奨となったPelletInfGraphクラスのサポート

support for Apache JenaでのPelletInfGraphクラスのサポートは、非推奨になりました。かわりに、Oracle Database向けのリーゾナPelletDb OWL 2を介して、より最適化されたOracle/Pellet統合を使用する必要があります。

7.12 RDF Semantic Graph Support for Apache Jenaを使用したバルク・ロード

膨大な数のRDF/OWLデータ・ファイルをOracle Databaseに簡単にロードするには、OracleBulkUpdateHandler JavaクラスのprepareBulkメソッドとcompleteBulkメソッドを使用します。

OracleBulkUpdateHandlerクラスのaddInBulkメソッドは、グラフまたはモデルのトリプルをOracle Databaseにバルクロード方式でロードできます。グラフまたはモデルがJenaインメモリー・グラフまたはモデルである場合、操作は物理メモリーのサイズによって制限されます。prepareBulkメソッドは、Jenaのインメモリー・グラフまたはモデルをバイパスし、RDFデータ・ファイルへの直接入力ストリームを取得してデータを解析し、基礎となるステージング表にトリプルをロードします。ステージング表および長いリテラルを格納するための添付表が存在しない場合は、自動的に作成されます。

prepareBulkメソッドは、複数のデータ・ファイルを同じ基礎となるステージング表へロードするために複数回コールできます。ハードウェア・システムがロードバランシングされており、複数のCPUコアによる十分なI/Oキャパシティが確保されている場合には、同時にコールすることもできます。

すべてのデータ・ファイルがprepareBulkメソッドによって処理されると、completeBulkを起動して、すべてのデータをセマンティク・ネットワークにロードできます。

例7-9 ステージング表へのデータのロード(prepareBulk)

例7-9に、ディレクトリdir_1のすべてデータ・ファイルを、基礎となるステージング表へロードする方法を示します。長いリテラルがサポートされ、別々の表に格納されます。記憶域容量を節約するため、GZIPを使用してデータ・ファイルを圧縮することができ、そのデータ・ファイルがGZIPを使用して圧縮されているかどうかを、prepareBulkメソッドで自動的に検出できます。

Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
GraphOracleSem graph = new GraphOracleSem(oracle, szModelName);
 
PrintStream psOut = System.out;

String dirname = "dir_1";
File fileDir = new File(dirname);
String[] szAllFiles = fileDir.list();

// loop through all the files in a directory
for (int idx = 0; idx < szAllFiles.length; idx++) {
  String szIndFileName = dirname + File.separator + szAllFiles[idx];
  psOut.println("process to [ID = " + idx + " ] file " + szIndFileName);
  psOut.flush();
 
  try {
    InputStream is = new FileInputStream(szIndFileName);
    graph.getBulkUpdateHandler().prepareBulk(
        is,                    // input stream
        "http://example.com",  // base URI
        "RDF/XML",             // data file type: can be RDF/XML, N-TRIPLE, etc.
        "SEMTS",               // tablespace
        null,                  // flags
        null,                  // listener
        null                   // staging table name.
        );
    is.close();
  }
  catch (Throwable t) {
    psOut.println("Hit exception " + t.getMessage());
  }
}
 
graph.close();
oracle.dispose();

例7-9のコードは、新しいOracleオブジェクトの作成から、そのOracleオブジェクトを破棄して終了するまでをパラレルで実行できます。データベースのハードウェア・システム上に、クワッド-コアCPUと十分なI/O容量がある場合、すべてのデータ・ファイルを4つの別々のディレクトリ(dir_1dir_2dir_3およびdir_4)に分割して保存することができます。4つのJavaプロセス・スレッドを起動し、それらのディレクトリで別々に同時に動作させることができます。(詳細は、「パラレル(マルチスレッド)モードでのprepareBulkの使用」を参照してください。)

例7-10 ステージング表からセマンティク・ネットワーク(completeBulk)へのデータのロード

すべてのデータ・ファイルが処理された後、1回だけcompleteBulkメソッドを起動し、例7-10に示すとおり、ステージング表のデータをセマンティク・ネットワークにロードできます。長いリテラルを持つトリプルもロードされます。

graph.getBulkUpdateHandler().completeBulk(
  null,  // flags for invoking SEM_APIS.bulk_load_from_staging_table
  null   // staging table name
);

また、prepareBulkメソッドは、Jenaモデルも入力データソースとして取得でき、この場合は、そのJenaモデルのトリプルを基礎となるステージング表にロードします。詳細は、Javadocを参照してください。

例7-11 prepareBulkでのRDFaの使用

Jenaモデルとデータ・ファイルからトリプルをロードする以外に、prepareBulkメソッドは、例7-11に示すとおり、RDFaをサポートします。(RDFaについては、http://www.w3.org/TR/xhtml-rdfa-primer/を参照してください。)

graph.getBulkUpdateHandler().prepareBulk(
  rdfaUrl,   // url to a web page using RDFa
  "SEMTS",   // tablespace
  null,      // flags
  null,      // listener
  null       // staging table name
);

RDFaを解析するには、関連するjava-rdfaライブラリをクラスパスに含める必要があります。その他の設定やAPIコールは必要ありません。(java-rdfaの詳細は、http://www.rootdev.net/maven/projects/java-rdfa/およびProject Informationの下のその他のトピックを参照してください。)

rdfaUrlがファイアウォールの外側にある場合は、次のHTTPプロキシ関連のJava VMプロパティを設定する必要がある場合があります。

-Dhttp.proxyPort=...
-Dhttp.proxyHost=...

例7-12 DatasetGraphへのクワッドのロード

この項の前述の例では、トリプル・データを単一のグラフにロードします。複数の名前付きグラフにわたる場合がある(NQUADS形式のデータなど)クワッド・データのロードには、DatasetGraphOracleSemクラスの使用が必要です。例7-12に示すとおり、DatasetGraphOracleSemクラスはBulkUpdateHandler APIを使用しませんが、同様のprepareBulkインタフェースとcompleteBulkインタフェースを提供します。

Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
 
// Can only create DatasetGraphOracleSem from an existing GraphOracleSem
GraphOracleSem graph = new GraphOracleSem(oracle, szModelName);
DatasetGraphOracleSem dataset = DatasetGraphOracleSem.createFrom(graph);
 
// Don't need graph anymore, close it to free resources
graph.close();
 
try {
    InputStream is = new FileInputStream(szFileName);
    // load NQUADS file into a staging table. This file can be gzipp'ed.
    dataset.prepareBulk(
        is,                // input stream
        "http://my.base/", // base URI
        "N-QUADS",         // data file type; can be "TRIG"
        "SEMTS",           // tablespace
        null,              // flags
        null,              // listener
        null,              // staging table name
        false              // truncate staging table before load
    );
    // Load quads from staging table into the dataset
    dataset.completeBulk(
        null, // flags; can be "PARSE PARALLEL_CREATE_INDEX PARALLEL=4 
              //                mbv_method=shadow" on a quad core machine
        null  // staging table name
    );
} 
catch (Throwable t) {
    System.out.println("Hit exception " + t.getMessage());
}
finally {
    dataset.close();
    oracle.dispose();
}

7.12.1 パラレル(マルチスレッド)・モードでのprepareBulkの使用

例7-9には、ファイル・システム・ディレクトリ下の一連のファイルを、Oracle Database表(ステージング表)へ、順番にロードする方法が示されていました。例7-13では、一連のファイルを同時にOracle表(ステージング表)にロードします。並列度は、入力パラメータiMaxThreadsによって制御されます。

4つ以上のCPUコアによる均等なハードウェア設定で、iMaxThreadsに8(または16)を設定すると、多数のデータ・ファイルが処理される場合に、prepareBulk操作の速度を大幅に向上できます。

例7-13 iMaxThreadsによるprepareBulkの使用

public void testPrepareInParallel(String jdbcUrl, String user,
                       String password, String modelName,
                       String lang,
                       String tbs,
                       String dirname,
                       int iMaxThreads,
                       PrintStream psOut)
   throws SQLException, IOException,  InterruptedException
 {
   File dir = new File(dirname);
   File[] files = dir.listFiles();

   // create a set of physical Oracle connections and graph objects
   Oracle[] oracles = new Oracle[iMaxThreads];
   GraphOracleSem[] graphs = new GraphOracleSem[iMaxThreads];
   for (int idx = 0; idx < iMaxThreads; idx++) {
     oracles[idx] = new Oracle(jdbcUrl, user, password);
     graphs[idx] = new GraphOracleSem(oracles[idx], modelName);
   }

   PrepareWorker[] workers = new PrepareWorker[iMaxThreads];
   Thread[] threads = new Thread[iMaxThreads];
   for (int idx = 0; idx < iMaxThreads; idx++) {
     workers[idx] = new PrepareWorker(
         graphs[idx],
         files,
         idx,
         iMaxThreads,
         lang,
         tbs,
         psOut
         );
     threads[idx] = new Thread(workers[idx], workers[idx].getName());
     psOut.println("testPrepareInParallel: PrepareWorker " + idx + " running");
     threads[idx].start();
   }

   psOut.println("testPrepareInParallel: all threads started");

   for (int idx = 0; idx < iMaxThreads; idx++) {
     threads[idx].join();
   }
   for (int idx = 0; idx < iMaxThreads; idx++) {
     graphs[idx].close();
     oracles[idx].dispose();
   }
 }

 static class PrepareWorker implements Runnable
 {
   GraphOracleSem graph = null;
   int idx;
   int threads;
   File[] files = null;
   String lang = null;
   String tbs  = null;
   PrintStream psOut;

   public void run()
   {
     long lStartTime = System.currentTimeMillis();
     for (int idxFile = idx; idxFile < files.length; idxFile += threads) {
       File file = files[idxFile];
       try {
         FileInputStream fis = new FileInputStream(file);
         graph.getBulkUpdateHandler().prepareBulk(
             fis,
             "http://base.com/",
             lang,
             tbs,
             null,                   // flags
             new MyListener(psOut),  // listener
             null                    // table name
             );
         fis.close();
       }
       catch (Exception e) {
         psOut.println("PrepareWorker: thread ["+getName()+"] error "+ e.getMessage());
       }
       psOut.println("PrepareWorker: thread ["+getName()+"] done to "
           + idxFile + ", file = " + file.toString()
           + " in (ms) " + (System.currentTimeMillis() - lStartTime));
     }
   }

   public PrepareWorker(GraphOracleSem graph,
                        File[] files,
                        int idx,
                        int threads,
                        String lang,
                        String tbs,
                        PrintStream psOut)
   {
     this.graph   = graph;
     this.files   = files;
     this.psOut   = psOut;
     this.idx     = idx;
     this.threads = threads;
     this.files   = files;
     this.lang    = lang;
     this.tbs     = tbs ;
   }

   public String getName()
   {
     return "PrepareWorker" + idx;
   }
 } 
 
 static class MyListener implements StatusListener 
 {
   PrintStream m_ps = null;
   public MyListener(PrintStream ps) { m_ps = ps; }
   long lLastBatch = 0;

   public void statusChanged(long count)
   {
     if (count - lLastBatch >= 10000) {
       m_ps.println("process to " + Long.toString(count));
       lLastBatch = count;
     }
   }

   public int illegalStmtEncountered(Node graphNode, Triple triple, long count)
   {
     m_ps.println("hit illegal statement with object " + triple.getObject().toString());
     return 0; // skip it
   }
 }

7.12.2 データ・ロード中の無効な構文の処理

prepareBulkを使用すると、無効なトリプルとクワッドをスキップできます。この機能は、ソースのRDFデータに構文エラーが含まれることが考えられる場合に有用です。例7-14で、StatusListenerインタフェースのカスタマイズされた実装(パッケージoracle.spatial.rdf.client.jenaで定義される)が、prepareBulkへのパラメータとして渡されます。この例で、illegalStmtEncounteredメソッドは、prepareBulkが無効なトリプルをスキップして進むことができるように、その無効なトリプルのオブジェクト・フィールドを出力し、0を戻します。

例7-14 無効な構文によるトリプルのスキップ

....
 
Oracle oracle = new Oracle(jdbcUrl, user, password);
GraphOracleSem graph = new GraphOracleSem(oracle, modelName);
PrintStream psOut = System.err;
 
graph.getBulkUpdateHandler().prepareBulk(
  new FileInputStream(rdfDataFilename),
  "http://base.com/",    // base 
  lang,                  // data format, can be "N-TRIPLES" "RDF/XML" ...
  tbs,                   // tablespace name
  null,                  // flags
  new MyListener(psOut), // call back to show progress and also process illegal triples/quads
  null,                  // tableName, if null use default names
  false                  // truncate existing staging tables
  );
 
 graph.close();
 oracle.dispose();
 .... 
 
 // A customized StatusListener interface implementation
  public class MyListener implements StatusListener
 {
   PrintStream m_ps = null;
   public MyListener(PrintStream ps) { m_ps = ps; }
 
   public void statusChanged(long count)
   {
     // m_ps.println("process to " + Long.toString(count));
   }
 
  public int illegalStmtEncountered(Node graphNode, Triple triple, long count)
  {
    m_ps.println("hit illegal statement with object " + triple.getObject().toString());
    return 0; // skip it
  }
 }

7.13 変数名の自動変更

以前は正常終了しなかった特定の問合せが、変数名の自動変更で使用できるようになることがあります。

以前、SPARQL問合せで使用された変数名は、SQL文の一部としてOracle Databaseに直接渡されました。変数名にSQLまたはPL/SQLの予約済キーワードを含めると、その問合せは実行できませんでした。たとえば、次のSPARQL問合せは、dateという語がOracle DatabaseのSQL処理エンジンにとって特別な意味であることが理由で失敗していました。

select ?date { :event  :happenedOn ?date }

現在では、この問合せは失敗しません。問合せが、実行のためにOracle Databaseに送信される前にスマート・スキャンが実行され、特定の予約済変数名(あるいは非常に長い変数名)の自動置換が実行されるためです。置換は、予約されたキーワード・リスト(sdordfclient.jar内の次のファイルに格納されている)に基づいています。

oracle/spatial/rdf/client/jena/oracle_sem_reserved_keywords.lst

このファイルには100以上のエントリが含まれており、必要に応じてファイルを編集し、エントリを追加できます。

次は、SQLまたはPL/SQLの予約済キーワードを変数として使用するSPARQL問合せの例で、変数名が自動的に変更されるため、正常に実行されます。

  • 変数名にSELECTを使用する問合せ:

    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    select ?SELECT ?z
    where
    {    ?SELECT foaf:name ?y.
         optional {?SELECT foaf:knows ?z.}
    }
    
  • 変数名にARRAYDATEを使用する問合せ:

    PREFIX x:    <http://example.com#>
    construct {
        ?ARRAY x:date ?date .
    }
    where {
        ?ARRAY x:happenedOn ?date .
    }

7.14 JavaScript Object Notation (JSON)形式のサポート

JavaScript Object Notation (JSON)形式は、SPARQL問合せのレスポンスのためにサポートされます。JSONデータ形式は単純、コンパクトで、JavaScriptプログラムに適しています。

たとえば、次のJavaコード・スニペット(ResultSetFormatter.outputAsJSONメソッドをコールする)があるとします。

Oracle oracle = new Oracle(jdbcUrl, user, password);
 
GraphOracleSem graph = new GraphOracleSem(oracle, modelName);
ModelOracleSem model = new ModelOracleSem(graph);
 
graph.add(new Triple(
                   Node.createURI("http://ds1"),
                   Node.createURI("http://dp1"),
                   Node.createURI("http://do1")
                   )
         );
 
graph.add(new Triple(
                   Node.createURI("http://ds2"),
                   Node.createURI("http://dp2"),
                   Node.createURI("http://do2")
                   )
         );
graph.commitTransaction();
 
Query q = QueryFactory.create("select ?s ?p ?o where {?s ?p ?o}",
                              Syntax.syntaxARQ);
QueryExecution qexec = QueryExecutionFactory.create(q, model);
 
ResultSet results = qexec.execSelect();
ResultSetFormatter.outputAsJSON(System.out, results);

JSON出力は次のとおりです。

{
  "head": {
    "vars": [ "s" , "p" , "o" ]
  } ,
  "results": {
    "bindings": [
      {
        "s": { "type": "uri" , "value": "http://ds1" } ,
        "p": { "type": "uri" , "value": "http://dp1" } ,
        "o": { "type": "uri" , "value": "http://do1" }
      } ,
      {
        "s": { "type": "uri" , "value": "http://ds2" } ,
        "p": { "type": "uri" , "value": "http://dp2" } ,
        "o": { "type": "uri" , "value": "http://do2" }
      }
    ]
  }
}

前述の例を、Oracle Databaseに対して直接ではなく、リモートのSPARQLエンドポイントを問い合せるようにするには、次のように変更します。(リモートのSPARQLエンドポイントがファイアウォールの外側にある場合、HTTPプロキシを設定する必要があります。)

Query q = QueryFactory.create("select ?s ?p ?o where {?s ?p ?o}",
                              Syntax.syntaxARQ);
QueryExecution qe = QueryExecutionFactory.sparqlService(sparqlURL, q);
 
ResultSet results = qexec.execSelect();
ResultSetFormatter.outputAsJSON(System.out, results);

この項の最初の例を名前付きグラフに拡張するため、次のコード・スニペットでは、同じOracleモデルに2つのクワッドを追加し、名前付きグラフベースのSPARQL問合せを実行して、問合せ出力をJSON形式にシリアライズします。

DatasetGraphOracleSem dsgos = DatasetGraphOracleSem.createFrom(graph);
graph.close();
 
dsgos.add(new Quad(Node.createURI("http://g1"),
                   Node.createURI("http://s1"),
                   Node.createURI("http://p1"),
                   Node.createURI("http://o1")
                   )
         );
dsgos.add(new Quad(Node.createURI("http://g2"),
                   Node.createURI("http://s2"),
                   Node.createURI("http://p2"),
                   Node.createURI("http://o2")
                   )
         );
 
Query q1 = QueryFactory.create(
  "select ?g ?s ?p ?o where { GRAPH ?g {?s ?p ?o} }");
 
QueryExecution qexec1 = QueryExecutionFactory.create(q1,
    DatasetImpl.wrap(dsgos));
 
ResultSet results1 = qexec1.execSelect();
ResultSetFormatter.outputAsJSON(System.out, results1);
 
dsgos.close();
oracle.dispose();

JSON出力は次のとおりです。

{
  "head": {
    "vars": [ "g" , "s" , "p" , "o" ]
  } ,
  "results": {
    "bindings": [
      {
        "g": { "type": "uri" , "value": "http://g1" } ,
        "s": { "type": "uri" , "value": "http://s1" } ,
        "p": { "type": "uri" , "value": "http://p1" } ,
        "o": { "type": "uri" , "value": "http://o1" }
      } ,
      {
        "g": { "type": "uri" , "value": "http://g2" } ,
        "s": { "type": "uri" , "value": "http://s2" } ,
        "p": { "type": "uri" , "value": "http://p2" } ,
        "o": { "type": "uri" , "value": "http://o2" }
      }
    ]
  }
}

また、次の例のように、FusekiベースのSPARQLエンドポイントに対してHTTPを通してJSONレスポンスを取得することもできます。通常、SPARQL Webサービス・エンドポイントに対してSPARQL問合せを実行する場合は、Accept request-headフィールドはapplication/sparql-results+xmlに設定されます。JSON出力形式の場合は、Accept request-headフィールドをapplication/sparql-results+jsonに置き換えます。

http://hostname:7001/fuseki/oracle?query=<URL_ENCODED_SPARQL_QUERY>&output=json

7.15 その他の推奨事項とガイドライン

この項では、SPARQL問合せに関連する様々な推奨事項およびその他の情報について説明します。

7.15.1 EXISTSまたはNOT EXISTSにかわるBOUNDまたは!BOUNDの使用

パフォーマンスをさらに向上するには、EXISTSNOT EXISTSのかわりに、BOUND!BOUNDを使用します。

7.15.2 SPARQL 1.1のSELECT式

関数が現在Oracle Databaseでサポートされていなくても、パフォーマンスに大きなオーバーヘッドを発生させることなく、SPARQL 1.1のSELECT表現を使用できます。例には次のものがあります。

-- Query using MD5 and SHA1 functions
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
PREFIX eg:   <http://biometrics.example/ns#>
SELECT ?name (md5(?name) as ?name_in_md5) (sha1(?email) as ?sha1) 
WHERE 
{ 
  ?x foaf:name  ?name ; eg:email ?email .
}

-- Query using CONCAT function
PREFIX foaf:   <http://xmlns.com/foaf/0.1/>
SELECT ( CONCAT(?G, " ", ?S) AS ?name )
WHERE  
{ 
  ?P foaf:givenName ?G ; foaf:surname ?S 
}

7.15.3 bnode(空白ノード)を含む構文

bnodeを含む構文を問合せパターンの中で自由に使用できます。たとえば、次のbnode関連の構文はパーサー・レベルでサポートされるため、各構文はそのトリプル問合せパターンベースの完全なバージョンと同等です。

:x :q [ :p "v" ] .
 
(1 ?x 3 4) :p "w" .
 
(1 [:p :q] ( 2 ) ) .

7.15.4 SERVICE句の制限

SPARQL 1.1のフェデレーテッド問合せを記述する際に、SERVICE句内の副問合せで戻される行に対して制限を設定できます。これによって、ローカルのリポジトリとリモートのSPARQLエンドポイントの間で転送されるデータ量を効果的に制限できます。

たとえば、次の問合せでは、SERVICE句の副問合せでlimit 100を指定しています。

PREFIX : <http://example.com/>
SELECT ?s ?o 
 WHERE 
     { 
       ?s :name "CA"  
       SERVICE <http://REMOTE_SPARQL_ENDPOINT_HERE>
          {
             select ?s  ?o 
                 {?s :info ?o} 
              limit 100 
           } 
     }

7.15.5 OracleGraphWrapperForOntModelクラスによるパフォーマンス向上

Jena OntModelクラスを使用すると、Jenaモデルに格納されているオントロジを作成、変更および分析できます。ただし、OntModelの実装は、データベースに格納されているセマンティク・データにとって最適とはいえません。OracleモデルでOntModelを使用する場合は、次善のパフォーマンスとなります。そのため、クラスOracleGraphWrapperForOntModelは、このようなパフォーマンスの問題を軽減するために作成されました。

OracleGraphWrapperForOntModelクラスは、Jena Graphインタフェースを実装し、Jena OntModel APIで使用するためのOracle RDF/OWLモデルに基づくグラフを表します。OracleGraphWrapperForOntModelクラスは、問合せに対する持続的な変更と応答のため、2つのセマンティク・ストアを組み合せて使用します。両方のセマンティク・ストアに同じデータが含まれますが、一方はメモリーに存在し、他方はOracle Databaseに存在します。

OntModelを介して問合せがあると、OracleGraphWrapperForOntModelグラフは、パフォーマンスを向上するためにインメモリー・ストアに対して問合せを実行します。ただしOracleGraphWrapperForOntModelクラスでは、クラスの追加または削除など、両方のストアに変更を適用することによって、OntModelを介して変更を行います。

ハイブリッドな方法であることから、OracleGraphWrapperForOntModelグラフでは、メモリーにオントロジのコピーを格納するため、十分なメモリーがJVMに割り当てられている必要があります。内部の実験では、およそ3,000,000のトリプルによるオントロジで、6GB以上の物理メモリーを必要とすることが分かっています。

例7-15 OntModelとOracle Databaseに格納されたオントロジの使用

例7-15では、OntModel APIとOracleモデルに格納されている既存のオントロジを使用する方法を表示します

// Set up connection to Oracle semantic store and the Oracle model
// containing the ontology
Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
GraphOracleSem oracleGraph = new GraphOracleSem(oracle, szModelName);
 
// Create a new hybrid graph using the oracle graph to persist
// changes.  This method will copy all the data from the oracle graph
// into an in-memory graph, which may significantly increase JVM memory
// usage.
Graph hybridGraph = OracleGraphWrapperForOntModel.getInstance(oracleGraph);
 
// Build a model around the hybrid graph and wrap the model with Jena's
// OntModel
Model model = ModelFactory.createModelForGraph(hybridGraph);
OntModel ontModel = ModelFactory.createOntologyModel(ontModelSpec, model);
 
// Perform operations on the ontology
OntClass personClass = ontModel.createClass("<http://someuri/person>");
ontModel.createIndividual(personClass);
 
// Close resources (will also close oracleGraph)!
hybridGraph.close();
ontModel.close();

OracleGraphWrapperForOntModelを使用して作成されたOntModelオブジェクトは、(別のOntModel、または同じ基礎となるモデルを参照する別のOracleグラフを介した)別のプロセスによって行われた、基礎となるOracleモデルへの変更を反映しません。オントロジへのすべての変更は、モデルまたはグラフが閉じられるまで、単一のOntModelオブジェクトと、その基礎となるOracleGraphWrapperForOntModelグラフを介して実行される必要があります。

例7-16 カスタム・インメモリー・グラフの使用

OracleGraphWrapperForOntModelによって使用されるデフォルトのインメモリー・セマンティク・ストアが、オントロジとシステムにとって十分ではない場合、インメモリー・ストアとして使用するカスタム・グラフを指定するためのインタフェースが、クラスによって提供されます。例7-16に、カスタム・インメモリー・グラフを使用してOntModelからの問合せに応答するOracleGraphWrapperForOntModelを作成する方法を示します。

// Set up connection to Oracle semantic store and the Oracle model
// containing the ontology
Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
GraphOracleSem oracleGraph = new GraphOracleSem(oracle, szModelName);
 
// Create a custom in-memory graph to use instead of the default
// Jena in-memory graph for quickly answering OntModel queries.
// Note that this graph does not *need* to be in-memory, but in-memory
// is preferred.
GraphBase queryGraph = new CustomInMemoryGraphImpl();
 
// Create a new hybrid graph using the oracle graph to persist
// changes and the custom in-memory graph to answer queries. 
// Also set the degree of parallelism to use when copying data from
// the oracle graph to the querying graph.
int degreeOfParallelism = 4;
Graph hybridGraph = OracleGraphWrapperForOntModel.getInstance(oracleGraph, queryGraph, degreeOfParallelism);
 
// Build a model and wrap the model with Jena's OntModel
Model model = ModelFactory.createModelForGraph(hybridGraph);
OntModel ontModel = ModelFactory.createOntologyModel(ontModelSpec, model);
 
// Perform operations on the ontology
// ...
 
// Close resources (will close oracleGraph and queryGraph)!
hybridGraph.close();
ontModel.close();

7.16 RDF Semantic Graph support for Apache Jenaを使用する問合せ例

この項では、support for Apache Jenaを使用した問合せの例を示します。それぞれの例は個々に完結しており、通常はモデルの作成、トリプルの作成、推論を含む可能性のある問合せの実行、結果の表示およびモデルの削除を行います。

この項では、次の手順を実行する問合せについて説明します。

  • URLによるオントロジの参照、およびローカル・ファイルからオントロジをバルク・ロードする方法の両方によって、universityオントロジの例で表明されたトリプルと表明に加えて推論されたトリプルをカウントします。

  • familyオントロジを使用して複数のSPARQL問合せを実行します(LIMIT、OFFSET、TIMEOUT、DOP (並列度)、ASK、DESCRIBE、CONSTRUCT、GRAPH、ALLOW_DUP (複数のモデルによる重複トリプル)、SPARUL (データの挿入)などの機能が含まれます)。

  • ARQ組込み関数を使用します。

  • SELECTキャスト問合せを使用します。

  • OracleConnectionを使用してOracle Databaseをインスタンス化します。

  • Oracle Database接続プーリングを使用します。

問合せを実行するには、次の手順を実行する必要があります。

  1. コードをJavaソース・ファイルに含めます。この項で使用される例は、support for Apache Jenaダウンロードのexamplesディレクトリのファイルで提供されています。

  2. Javaソース・ファイルをコンパイルします。たとえば、次のようにします。

    > javac -classpath ../jar/'*' Test.java
    

    ノート:

    javacコマンドとjavaコマンドは、それぞれ1行のコマンドである必要があります。

  3. コンパイルされたファイルを実行します。たとえば、次のようにします。

    > java -classpath ./:../jar/'*'  Test jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1

ノート:

次の各サブトピックの例は、いずれもMDSYSスキーマに格納されているRDFメタデータに基づいています。Oracle Database 19c以降でサポートされているスキーマプライベート・ネットワークについては、同等のJavaテスト・ファイルがOTNキットに含まれています。

主な違いは、スキーマプライベート・ネットワークの場合はセマンティク・モデル・オブジェクトを作成する際にネットワーク所有者とネットワーク名を定義する追加のパラメータを使用することです。

7.16.1 Test.java: 家族関係の問合せ

例7-17 家族関係を問い合せる

例7-17は、JohnがMaryの父であることを指定し、次に、各fatherOf関係で主語と目的語を選択して表示します。

import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.graph.*;
import org.apache.jena.query.*;
public class Test {
 
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
	  
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    Model model = ModelOracleSem.createOracleSemModel(
      oracle, szModelName);
 
    model.getGraph().add(Triple.create(
          Node.createURI("http://example.com/John"),
          Node.createURI("http://example.com/fatherOf"),
          Node.createURI("http://example.com/Mary")));
    Query query = QueryFactory.create(
        "select ?f ?k WHERE {?f <http://example.com/fatherOf> ?k .}");
    QueryExecution qexec = QueryExecutionFactory.create(query, model);
    ResultSet results = qexec.execSelect();
    ResultSetFormatter.out(System.out, results, query);
    model.close();
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-17をコンパイルして実行します。

javac -classpath ../jar/'*' Test.java
java -classpath ./:../jar/'*'  Test jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
 
---------------------------------------------------------
| f                         | k                         |
=========================================================
| <http://example.com/John> | <http://example.com/Mary> |
---------------------------------------------------------

7.16.2 Test6.java: OWLオントロジのロードとOWLPrime推論の実行

例7-18は、OWLオントロジをロードして、OWLPrime推論を実行します。OWLオントロジがRDF/XML形式であり、Oracleにロードされた後にN-triple形式でシリアライズされることに注意してください。また、表明および推論されたトリプルの数を問い合せます。

この例のオントロジはhttp://swat.cse.lehigh.edu/onto/univ-bench.owlから入手することができます。そこには、ロール、リソースおよび大学環境での関係が記述されています。

例7-18 OWLオントロジをロードし、OWLPrime推論を実行する

import java.io.*;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.util.FileManager;
import oracle.spatial.rdf.client.jena.*;
public class Test6 {
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    Model model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
        
    // load UNIV ontology
    InputStream in = FileManager.get().open("./univ-bench.owl" );
    model.read(in, null);
    OutputStream os = new FileOutputStream("./univ-bench.nt");
    model.write(os, "N-TRIPLE");
    os.close();
 
    String queryString =
      " SELECT ?subject ?prop ?object WHERE { ?subject ?prop ?object } ";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
 
    try {
      int iTriplesCount = 0;
      ResultSet results = qexec.execSelect() ;
      for ( ; results.hasNext() ; ) {
        QuerySolution soln = results.nextSolution() ;
        iTriplesCount++;
      }
      System.out.println("Asserted  triples count: " + iTriplesCount);
    } 
    finally { 
      qexec.close() ; 
    }
    
    Attachment attachment = Attachment.createInstance(
        new String[] {}, "OWLPRIME",
        InferenceMaintenanceMode.NO_UPDATE, QueryOptions.DEFAULT);
 
    GraphOracleSem graph = new GraphOracleSem(oracle, szModelName, attachment);
    graph.analyze();
    graph.performInference();
 
    query = QueryFactory.create(queryString) ;
    qexec = QueryExecutionFactory.create(query,new ModelOracleSem(graph)) ;
 
    try {
      int iTriplesCount = 0;
      ResultSet results = qexec.execSelect() ;
      for ( ; results.hasNext() ; ) {
        QuerySolution soln = results.nextSolution() ;
        iTriplesCount++;
      }
      System.out.println("Asserted + Infered triples count: " + iTriplesCount);
    } 
    finally { 
      qexec.close() ; 
    }
    model.close();    
 
    OracleUtils.dropSemanticModel(oracle, szModelName);    
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-18をコンパイルして実行します。

javac -classpath ../jar/'*' Test6.java
java -classpath ./:../jar/'*'  Test6 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
Asserted  triples count: 293
Asserted + Infered triples count: 340

この出力には、以前のバージョンのLUBMオントロジが反映されていることに注意してください。最新バージョンのオントロジには、より多くのトリプルがあります。

7.16.3 Test7.java: OWLオントロジをバルク・ロードしてOWLPrime推論を実行する

例7-19では、「Test6.java: OWLオントロジのロードとOWLPrime推論の実行」と同じOWLオントロジがロードされますが、バルク・ローダーの使用によりローカル・ファイルに保存されます。オントロジは増分およびバッチ・ローダーを使用してロードすることもでき、例にはこれらの2つの方法も示されています。

例7-19 OWLオントロジをバルク・ロードし、OWLPrime推論を実行する

import java.io.*;
import org.apache.jena.graph.*;
import org.apache.jena.rdf.model.*;
import org.apache.jena.util.*;
import oracle.spatial.rdf.client.jena.*;
 
public class Test7 
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    // in memory Jena Model
    Model model = ModelFactory.createDefaultModel();
    InputStream is = FileManager.get().open("./univ-bench.owl");
    model.read(is, "", "RDF/XML");
    is.close();
 
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem modelDest = ModelOracleSem.createOracleSemModel(oracle, 
szModelName);
 
    GraphOracleSem g = modelDest.getGraph();
    g.dropApplicationTableIndex();
 
    int method = 2; // try bulk loader
    String tbs = "SYSAUX"; // can be customized
    if (method == 0) {
      System.out.println("start incremental");
      modelDest.add(model);
      System.out.println("end size " + modelDest.size());
    }
    else if (method == 1) {
      System.out.println("start batch load");
      g.getBulkUpdateHandler().addInBatch(
          GraphUtil.findAll(model.getGraph()), tbs);
      System.out.println("end size " + modelDest.size());
    }
    else {
      System.out.println("start bulk load");
      g.getBulkUpdateHandler().addInBulk(
          GraphUtil.findAll(model.getGraph()), tbs);
      System.out.println("end size " + modelDest.size());
    }
    g.rebuildApplicationTableIndex();
 
    long lCount = g.getCount(Triple.ANY);
    System.out.println("Asserted  triples count: " + lCount);
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-19をコンパイルして実行します。

javac -classpath ../jar/'*' Test7.java
java -classpath ./:../jar/'*'  Test7 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
start bulk load
end size 293
Asserted  triples count: 293

この出力には、以前のバージョンのLUBMオントロジが反映されていることに注意してください。最新バージョンのオントロジには、より多くのトリプルがあります。

7.16.4 Test8.java: SPARQL OPTIONAL問合せ

例7-20には、SPARQL OPTIONAL問合せが示されています。次の条件を持つトリプルを挿入します。

  • Johnは、Maryの親です。

  • Johnは、Jackの親です。

  • Maryは、Jillの親です。

次に親子関係を見つけ、必要に応じて孫(gkid)の関係も含めます。

例7-20 SPARQLのOPTIONAL問合せ

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;

public class Test8 
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, 
szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(
          Node.createURI("u:John"), Node.createURI("u:parentOf"), 
Node.createURI("u:Mary")));
    g.add(Triple.create(
          Node.createURI("u:John"), Node.createURI("u:parentOf"), 
Node.createURI("u:Jack")));
    g.add(Triple.create(
          Node.createURI("u:Mary"), Node.createURI("u:parentOf"), 
Node.createURI("u:Jill")));
        
    String queryString =
  " SELECT ?s ?o ?gkid " +
  " WHERE { ?s <u:parentOf> ?o . OPTIONAL {?o <u:parentOf> ?gkid }} ";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
 
    try {
      int iMatchCount = 0;
      ResultSet results = qexec.execSelect() ;
      ResultSetFormatter.out(System.out, results, query);
    } 
    finally { 
      qexec.close() ; 
    }
    model.close();    
 
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-20をコンパイルして実行します。

javac -classpath ../jar/'*' Test8.java
java -classpath ./:../jar/'*'  Test8 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
----------------------------------
| s        | o        | gkid     |
==================================
| <u:John> | <u:Mary> | <u:Jill> |
| <u:Mary> | <u:Jill> |          |
| <u:John> | <u:Jack> |          |
---------------------------------- 

7.16.5 Test9.java: SPARQL問合せでのLIMITおよびOFFSETの使用

例7-21には、LIMITおよびOFFSETを使用するSPARQL問合せが示されています。次の条件を持つトリプルを挿入します。

  • Johnは、Maryの親です。

  • Johnは、Jackの親です。

  • Maryは、Jillの親です。

次に1つの親子関係(LIMIT 1)を検出し、出現した最初の2つの親子関係をスキップ(OFFSET 2)し、必要に応じて、検出された孫(gkid)の関係を含めます。

例7-21 SPARQL問合せでのLIMITおよびOFFSETの使用

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
public class Test9 
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, 
szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"),
                    Node.createURI("u:Mary")));
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"),
                    Node.createURI("u:Jack")));
    g.add(Triple.create(Node.createURI("u:Mary"), Node.createURI("u:parentOf"),              
                    Node.createURI("u:Jill")));
        
    String queryString =
      " SELECT ?s ?o ?gkid " +
      " WHERE { ?s <u:parentOf> ?o . OPTIONAL {?o <u:parentOf> ?gkid }} " +
      " LIMIT 1 OFFSET 2";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
 
    int iMatchCount = 0;
    ResultSet results = qexec.execSelect() ;
    ResultSetFormatter.out(System.out, results, query);
    qexec.close() ; 
    model.close();    
 
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-21をコンパイルして実行します。

javac -classpath ../jar/'*' Test9.java
java -classpath ./:../jar/'*'  Test9 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
------------------------------
| s        | o        | gkid |
==============================
| <u:John> | <u:Jack> |      |
------------------------------ 

7.16.6 Test10.java: SPARQL問合せでのTIMEOUTおよびDOPの使用

例7-22では、「Test9.java: LIMITおよびOFFSETを使用するSPARQL問合せ」のSPARQL問合せに、タイムアウト設定(TIMEOUT=1、秒単位)およびパラレル実行設定(DOP=4)などの機能を追加した例を示します。

例7-22 SPARQL問合せでのTIMEOUTおよびDOPの使用

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
 
public class Test10 {
  public static void main(String[] args) throws Exception  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                            Node.createURI("u:Mary")));
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                        Node.createURI("u:Jack")));
    g.add(Triple.create(Node.createURI("u:Mary"), Node.createURI("u:parentOf"), 
                        Node.createURI("u:Jill")));
    String queryString =
        " PREFIX ORACLE_SEM_FS_NS: <http://oracle.com/semtech#dop=4,timeout=1> " 
      + " SELECT ?s ?o ?gkid WHERE { ?s <u:parentOf> ?o . " 
      + " OPTIONAL {?o <u:parentOf> ?gkid }} "
      + " LIMIT 1 OFFSET 2";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
 
    int iMatchCount = 0;
    ResultSet results = qexec.execSelect() ;
    ResultSetFormatter.out(System.out, results, query);
    qexec.close() ; 
    model.close();    
 
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-22をコンパイルして実行します。

javac -classpath ../jar/'*' Test10.java
java -classpath ./:../jar/'*'  Test10 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
------------------------------
| s        | o        | gkid |
==============================
| <u:John> | <u:Jack> |      |
------------------------------

7.16.7 Test11.java: 名前付きグラフを含む問合せ

例7-23には、名前付きグラフを含む問合せが示されています。これには、名前付きグラフのURIとその作成者の情報を持つデフォルト・グラフが含まれます。問合せで、グラフ名、グラフの作成者およびfoaf:mbox述語を使用して各名前付きグラフのメールボックスの値を確認できます。

例7-23 名前付きグラフ・ベースの問合せ

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
 
public class Test11
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    GraphOracleSem graph = new GraphOracleSem(oracle, szModelName);
    DatasetGraphOracleSem dataset = DatasetGraphOracleSem.createFrom(graph);
    
    // don't need the GraphOracleSem anymore, release resources
    graph.close();
    
    // add data to the default graph
    dataset.add(new Quad(
          Quad.defaultGraphIRI, // specifies default graph
          Node.createURI("http://example.org/bob"),
          Node.createURI("http://purl.org/dc/elements/1.1/publisher"),
          Node.createLiteral("Bob Hacker")));
    dataset.add(new Quad(
          Quad.defaultGraphIRI, // specifies default graph
          Node.createURI("http://example.org/alice"),
          Node.createURI("http://purl.org/dc/elements/1.1/publisher"),
          Node.createLiteral("alice Hacker")));
    
    // add data to the bob named graph
    dataset.add(new Quad(
          Node.createURI("http://example.org/bob"), // graph name
          Node.createURI("urn:bob"),
          Node.createURI("http://xmlns.com/foaf/0.1/name"),
          Node.createLiteral("Bob")));
    dataset.add(new Quad(
          Node.createURI("http://example.org/bob"), // graph name
          Node.createURI("urn:bob"),
          Node.createURI("http://xmlns.com/foaf/0.1/mbox"),
          Node.createURI("mailto:bob@example")));
    
    // add data to the alice named graph
    dataset.add(new Quad(
          Node.createURI("http://example.org/alice"), // graph name
          Node.createURI("urn:alice"),
          Node.createURI("http://xmlns.com/foaf/0.1/name"),
          Node.createLiteral("Alice")));
    dataset.add(new Quad(
          Node.createURI("http://example.org/alice"), // graph name
          Node.createURI("urn:alice"),
          Node.createURI("http://xmlns.com/foaf/0.1/mbox"),
          Node.createURI("mailto:alice@example")));
    
    DataSource ds = DatasetFactory.create(dataset);
    
    String queryString =  
          " PREFIX foaf: <http://xmlns.com/foaf/0.1/> "
        + " PREFIX dc: <http://purl.org/dc/elements/1.1/> "
        + " SELECT ?who ?graph ?mbox "
        + " FROM NAMED <http://example.org/alice> "
        + " FROM NAMED <http://example.org/bob> "
        + " WHERE "
        + " { " 
        + "    ?graph dc:publisher ?who . "
        + "    GRAPH ?graph { ?x foaf:mbox ?mbox } "
        + " } ";
    
    Query query = QueryFactory.create(queryString);
    QueryExecution qexec = QueryExecutionFactory.create(query, ds);
    
    ResultSet results = qexec.execSelect();
    ResultSetFormatter.out(System.out, results, query);
    
    qexec.close();
    dataset.close();
    
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-23をコンパイルして実行します。

javac -classpath ./:./jena-2.6.4.jar:./sdordfclient.jar:./ojdbc6.jar:./slf4j-api-1.5.8.jar:./slf4j-log4j12-1.5.8.jar:./arq-2.8.8.jar:./xercesImpl-2.7.1.jar Test11.java
java -classpath ./:../jar/'*'  Test11 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
------------------------------------------------------------------------
| who            | graph                      | mbox                   |
========================================================================
| "alice Hacker" | <http://example.org/alice> | <mailto:alice@example> |
| "Bob Hacker"   | <http://example.org/bob>   | <mailto:bob@example>   |
------------------------------------------------------------------------ 

7.16.8 Test12.java: SPARQL ASK問合せ

例7-24には、SPARQL ASK問合せが示されています。JohnがMaryの親であるという条件のトリプルを挿入します。次にJohnがMaryの親かどうかを確認します。

例7-24 SPARQLのASK問合せ

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, 
          szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                        Node.createURI("u:Mary")));
    String queryString = " ASK { <u:John> <u:parentOf> <u:Mary> } ";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
    boolean b = qexec.execAsk();
    System.out.println("ask result = " + ((b)?"TRUE":"FALSE"));
    qexec.close() ; 
    
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-24をコンパイルして実行します。

javac -classpath ../jar/'*' Test12.java
java -classpath ./:../jar/'*'  Test12 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
ask result = TRUE

7.16.9 Test13.java: SPARQL DESCRIBE問合せ

例7-25には、SPARQL DESCRIBE問合せが示されています。次の条件を持つトリプルを挿入します。

  • Johnは、Maryの親です。

  • Johnは、Jackの親です。

  • Amyは、Jackの親です。

次に、Jackの親を含むすべての関係を検出します。

例7-25 SPARQLのDESCRIBE問合せ

import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
 
public class Test13
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                    Node.createURI("u:Mary")));
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
 Node.createURI("u:Jack")));
    g.add(Triple.create(Node.createURI("u:Amy"), Node.createURI("u:parentOf"), 
 Node.createURI("u:Jack")));
    String queryString = " DESCRIBE ?x WHERE {?x <u:parentOf> <u:Jack>}";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
    Model m = qexec.execDescribe();
    System.out.println("describe result = " + m.toString());
 
    qexec.close() ; 
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-25をコンパイルして実行します。

javac -classpath ../jar/'*' Test13.java
java -classpath ./:../jar/'*'  Test13 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
describe result = <ModelCom   {u:Amy @u:parentOf u:Jack; 
     u:John @u:parentOf u:Jack; u:John @u:parentOf u:Mary} |  [u:Amy, u:parentOf, u:Jack] [u:John, u:parentOf,
       u:Jack] [u:John, u:parentOf, u:Mary]>

7.16.10 Test14.java: SPARQL CONSTRUCT問合せ

例7-26には、SPARQL CONSTRUCT問合せが示されています。次の条件を持つトリプルを挿入します。

  • Johnは、Maryの親です。

  • Johnは、Jackの親です。

  • Amyは、Jackの親です。

  • それぞれの親は、自分の子ども全員を愛しています。

次に、誰が誰を愛しているかについての情報でRDFグラフを構築します。

例7-26 SPARQLのCONSTRUCT問合せ

import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
 
public class Test14
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
 Node.createURI("u:Mary")));
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
 Node.createURI("u:Jack")));
    g.add(Triple.create(Node.createURI("u:Amy"), Node.createURI("u:parentOf"), 
 Node.createURI("u:Jack")));
    String queryString = " CONSTRUCT { ?s <u:loves> ?o } WHERE {?s <u:parentOf> ?o}";
 
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
    Model m = qexec.execConstruct();
    System.out.println("Construct result = " + m.toString());
 
    qexec.close() ; 
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-26をコンパイルして実行します。

javac -classpath ../jar/'*' Test14.java
java -classpath ./:../jar/'*'  Test14 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
Construct result = <ModelCom   {u:Amy @u:loves u:Jack; 
  u:John @u:loves u:Jack; u:John @u:loves u:Mary} |  [u:Amy, u:loves, u:Jack] [u:John, u:loves,
    u:Jack] [u:John, u:loves, u:Mary]>

7.16.11 Test15.java: 複数のモデルを問い合せ、重複の許可を指定する

例7-27では、複数のモデルを問い合せて重複の許可を使用します。次の条件を持つトリプルを挿入します。

  • Johnは、Jackの親です(モデル1)。

  • Maryは、Jackの親です(モデル2)。

  • それぞれの親は、自分の子ども全員を愛しています。

次に、誰が誰を愛しているかについて検出します。両方のモデルを検索し、モデルでの重複トリプルを(この例に重複はありませんが)許可します。

例7-27 複数のモデルを問い合せて重複の許可を指定する

import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
 
public class Test15
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName1 = args[3];
    String szModelName2 = args[4];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model1 = ModelOracleSem.createOracleSemModel(oracle, szModelName1);
    model1.getGraph().add(Triple.create(Node.createURI("u:John"), 
                     Node.createURI("u:parentOf"), Node.createURI("u:Jack")));
    model1.close();
 
    ModelOracleSem model2 = ModelOracleSem.createOracleSemModel(oracle, szModelName2);
    model2.getGraph().add(Triple.create(Node.createURI("u:Mary"), 
                     Node.createURI("u:parentOf"), Node.createURI("u:Jack")));
    model2.close();
 
    String[] modelNamesList = {szModelName2};
    String[] rulebasesList  = {};
    Attachment attachment = Attachment.createInstance(modelNamesList, rulebasesList, 
              InferenceMaintenanceMode.NO_UPDATE,
              QueryOptions.ALLOW_QUERY_VALID_AND_DUP);
 
    GraphOracleSem graph = new GraphOracleSem(oracle, szModelName1, attachment);
    ModelOracleSem model = new ModelOracleSem(graph);
 
    String queryString = " CONSTRUCT { ?s <u:loves> ?o } WHERE {?s <u:parentOf> ?o}";
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
    Model m = qexec.execConstruct();
    System.out.println("Construct result = " + m.toString());
 
    qexec.close() ; 
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName1);
    OracleUtils.dropSemanticModel(oracle, szModelName2);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-27をコンパイルして実行します。

javac -classpath ../jar/'*' Test15.java
java -classpath ./:../jar/'*'  Test15 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1 M2
Construct result = <ModelCom   {u:Mary @u:loves u:Jack; u:John @u:loves u:Jack} |  [u:Mary, u:loves, u:Jack] [u:John, u:loves, u:Jack]>

7.16.12 Test16.java: SPARQLの更新

例7-28では、2つのトリプルをモデルに挿入します。

例7-28 SPARQLの更新

import org.apache.jena.util.iterator.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.graph.*;
import org.apache.jena.update.*;
 
public class Test16
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
    GraphOracleSem g = model.getGraph();
    String insertString =  
      " PREFIX dc: <http://purl.org/dc/elements/1.1/> "         + 
      " INSERT DATA "                                           +
      " { <http://example/book3> dc:title    \"A new book\" ; " +
      "                         dc:creator  \"A.N.Other\" . "   + 
      " }   ";
 
    UpdateAction.parseExecute(insertString,  model);
    ExtendedIterator ei = GraphUtil.findAll(g);
    while (ei.hasNext()) {
      System.out.println("Triple " + ei.next().toString());
    }
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-28をコンパイルして実行します。

javac -classpath ../jar/'*' Test16.java
java -classpath ./:../jar/'*'  Test16 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
Triple http://example/book3 @dc:title "A new book"
Triple http://example/book3 @dc:creator "A.N.Other"

7.16.13 Test17.java: SPARQL問合せでのARQ組込み関数の使用

例7-29では、2冊の本に関するデータを挿入し、本のタイトル(すべて大文字で)と、各タイトル文字列の長さを表示します。

例7-29 SPARQL問合せでのARQ組込み関数の使用

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.update.*;
 
public class Test17 {
  public static void main(String[] args) throws Exception  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, szModelName);
    GraphOracleSem g = model.getGraph();
    String insertString =  
      " PREFIX dc: <http://purl.org/dc/elements/1.1/> "         + 
      " INSERT DATA "                                           +
      " { <http://example/book3> dc:title    \"A new book\" ; " +
      "                         dc:creator  \"A.N.Other\" . "   + 
      "   <http://example/book4> dc:title    \"Semantic Web Rocks\" ; " +
      "                         dc:creator  \"TB\" . "   + 
      " }   ";
 
    UpdateAction.parseExecute(insertString,  model);
    String queryString = "PREFIX  dc:   <http://purl.org/dc/elements/1.1/> " +
      " PREFIX  fn: <http://www.w3.org/2005/xpath-functions#> " + 
      " SELECT ?subject (fn:upper-case(?object) as ?object1)  " + 
      "                 (fn:string-length(?object) as ?strlen) " + 
      " WHERE { ?subject dc:title ?object } " 
      ;
    Query query = QueryFactory.create(queryString, Syntax.syntaxARQ);
    QueryExecution qexec = QueryExecutionFactory.create(query, model);
    ResultSet results = qexec.execSelect();
    ResultSetFormatter.out(System.out, results, query);
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-29をコンパイルして実行します。

javac -classpath ../jar/'*' Test17.java
java -classpath ./:../jar/'*'  Test17 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
----------------------------------------------------------
| subject                | object1              | strlen |
==========================================================
| <http://example/book3> | "A NEW BOOK"         | 10     |
| <http://example/book4> | "SEMANTIC WEB ROCKS" | 18     |
----------------------------------------------------------

7.16.14 Test18.java: SELECTキャスト問合せ

例7-30では、2つの華氏温度(18.1と32.0)を摂氏温度に変換します。

例7-30 SELECTキャスト問合せ

import org.apache.jena.query.*;
import oracle.spatial.rdf.client.jena.*;
import org.apache.jena.update.*;
 
public class Test18 {
  public static void main(String[] args) throws Exception  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
    
    Oracle oracle = new Oracle(szJdbcURL, szUser, szPasswd);
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, 
szModelName);
    GraphOracleSem g = model.getGraph();
    String insertString =  
      " PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
      " INSERT DATA "                                     +
      " { <u:Object1> <u:temp>    \"18.1\"^^xsd:float ; " +
      "               <u:name>    \"Foo... \" . "         + 
      "   <u:Object2> <u:temp>    \"32.0\"^^xsd:float ; " +
      "               <u:name>    \"Bar... \" . "         + 
      " }   ";
 
    UpdateAction.parseExecute(insertString,  model);
    String queryString = 
      " PREFIX  fn: <http://www.w3.org/2005/xpath-functions#> " + 
      " SELECT ?subject ((?temp - 32.0)*5/9 as ?celsius_temp) " +
      "WHERE { ?subject <u:temp> ?temp } " 
      ;
    Query query = QueryFactory.create(queryString, Syntax.syntaxARQ);
    QueryExecution qexec = QueryExecutionFactory.create(query, model);
    ResultSet results = qexec.execSelect();
    ResultSetFormatter.out(System.out, results, query);
 
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-30をコンパイルして実行します。

javac -classpath ../jar/'*' Test18.java
java -classpath ./:../jar/'*'  Test18 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
------------------------------------------------------------------------
| subject     | celsius_temp                                           |
========================================================================
| <u:Object1> | "-7.7222223"^^<http://www.w3.org/2001/XMLSchema#float> |
| <u:Object2> | "0.0"^^<http://www.w3.org/2001/XMLSchema#float>        |
------------------------------------------------------------------------

7.16.15 Test19.java: OracleConnectionを使用したOracle Databaseのインスタンス化

例7-31では、指定されたOracleConnectionオブジェクトを使用して、異なる方法でOracleオブジェクトをインスタンス化しています。(J2EE Webアプリケーションでは、ユーザーは通常、J2EEデータソースからOracleConnectionオブジェクトを取得できます。)

例7-31 OracleConnectionを使用してOracle Databaseをインスタンス化する

import org.apache.jena.query.*;
import org.apache.jena.graph.*;
import oracle.spatial.rdf.client.jena.*;
import oracle.jdbc.pool.*;
import oracle.jdbc.*;
	
public class Test19 {
  public static void main(String[] args) throws Exception {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
 
    OracleDataSource ds = new OracleDataSource();
    ds.setURL(szJdbcURL);
    ds.setUser(szUser);
    ds.setPassword(szPasswd);
    OracleConnection conn = (OracleConnection) ds.getConnection();
    Oracle oracle = new Oracle(conn);
 
    ModelOracleSem model = ModelOracleSem.createOracleSemModel(oracle, 
szModelName);
    GraphOracleSem g = model.getGraph();
 
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                        Node.createURI("u:Mary")));
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"),   
                        Node.createURI("u:Jack")));
    g.add(Triple.create(Node.createURI("u:Mary"), Node.createURI("u:parentOf"), 
         Node.createURI("u:Jill")));
    String queryString =
       " SELECT ?s ?o  WHERE { ?s <u:parentOf> ?o .} ";
    Query query = QueryFactory.create(queryString) ;
    QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
 
    ResultSet results = qexec.execSelect() ;
    ResultSetFormatter.out(System.out, results, query);
    qexec.close() ; 
    model.close();    
    OracleUtils.dropSemanticModel(oracle, szModelName);
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-31をコンパイルして実行します。

javac -classpath ../jar/'*' Test19.java
java -classpath ./:../jar/'*'  Test19 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
-----------------------
| s        | o        |
=======================
| <u:John> | <u:Mary> |
| <u:John> | <u:Jack> |
| <u:Mary> | <u:Jill> |
-----------------------

7.16.16 Test20.java: Oracle Database接続プーリング

例7-32では、Oracle Database接続プーリングを使用します。

例7-32 Oracle Database接続プーリング

import org.apache.jena.graph.*;
import oracle.spatial.rdf.client.jena.*;
 
public class Test20
{
  public static void main(String[] args) throws Exception
  {
    String szJdbcURL = args[0];
    String szUser    = args[1];
    String szPasswd  = args[2];
    String szModelName = args[3];
 
    // test with connection properties (taken from some example)
    java.util.Properties prop = new java.util.Properties();
    prop.setProperty("MinLimit", "2");     // the cache size is 2 at least 
    prop.setProperty("MaxLimit", "10");
    prop.setProperty("InitialLimit", "2"); // create 2 connections at startup
    prop.setProperty("InactivityTimeout", "1800");    //  seconds
    prop.setProperty("AbandonedConnectionTimeout", "900");  //  seconds
    prop.setProperty("MaxStatementsLimit", "10");
    prop.setProperty("PropertyCheckInterval", "60"); // seconds
 
    System.out.println("Creating OraclePool");
    OraclePool op = new OraclePool(szJdbcURL, szUser, szPasswd, prop, 
               "OracleSemConnPool");
    System.out.println("Done creating OraclePool");
 
    // grab an Oracle and do something with it
    System.out.println("Getting an Oracle from OraclePool");
    Oracle oracle = op.getOracle();
    System.out.println("Done");
    System.out.println("Is logical connection:" +
        oracle.getConnection().isLogicalConnection());
    GraphOracleSem g = new GraphOracleSem(oracle, szModelName);
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                        Node.createURI("u:Mary")));
    g.close();
    // return the Oracle back to the pool
    oracle.dispose();
    
    // grab another Oracle and do something else 
    System.out.println("Getting an Oracle from OraclePool");
    oracle = op.getOracle();
    System.out.println("Done");
    System.out.println("Is logical connection:" +
        oracle.getConnection().isLogicalConnection());
    g = new GraphOracleSem(oracle, szModelName);
    g.add(Triple.create(Node.createURI("u:John"), Node.createURI("u:parentOf"), 
                        Node.createURI("u:Jack")));
    g.close();
    
    OracleUtils.dropSemanticModel(oracle, szModelName); 
    
    // return the Oracle back to the pool
    oracle.dispose();
  }
}

次のコマンドは、javaコマンドの想定される出力に加え、例7-32をコンパイルして実行します。

javac -classpath ../jar/'*' Test20.java
java -classpath ./:../jar/'*'  Test20 jdbc:oracle:thin:@localhost:1521:orcl scott <password-for-scott> M1
Creating OraclePool
Done creating OraclePool
Getting an Oracle from OraclePool
Done
Is logical connection:true
Getting an Oracle from OraclePool
Done
Is logical connection:true

7.17 SPARQL Gatewayとセマンティク・データ

SPARQL Gatewayは、support for Apache Jenaに含まれるJ2EE Webアプリケーションです。Oracle Business Intelligence Enterprise Edition (OBIEE) 11gなどの、リレーショナル・データおよびXMLデータで稼働するアプリケーションで、セマンティク・データ(RDF/OWL/SKOS)を容易に利用可能にすることを目的としています。

7.17.1 SPARQL Gatewayの機能および利点の概要

SPARQL Gatewayでは、セマンティク・データを非セマンティク・アプリケーションに公開する際の次のような課題に対処します。

  • RDF構文、SPARQL問合せ構文およびSPARQLプロトコルについて理解しておく必要があること。

  • SPARQL問合せのレスポンス構文を理解しておく必要があること。

  • 変換で、SPARQL問合せのレスポンスを、アプリケーションが使用できる形に変更する必要があること。

このような課題に対処するため、SPARQL Gatewayは、SPARQL問合せとXSLT操作を管理し、標準準拠の任意のSPARQLエンドポイントに対してSPARQL問合せを実行して、必要なXSL変換を実行してから、アプリケーションへレスポンスを渡します。こうすると、アプリケーションは既存のデータ・ソースからの場合と同じようにセマンティク・データを使用できるようになります。

異なるトリプル・ストアまたはクワッド・ストアには、異なる機能があります。たとえば、Oracle DatabaseによってサポートされるSPARQLエンドポイントは、指定された標準準拠のSPARQL問合せを解析して応答するというコア機能に加え、RDF Semantic Graph support for Apache Jenaを使用して、パラレル実行、問合せタイムアウト、動的なサンプリング、結果キャッシュおよびその他の機能を実行できます。一方、これらの機能は、指定された別のセマンティク・データ・ストアからは使用できない場合があります。

RDF Semantic Graph SPARQL Gatewayでは、たとえば長時間実行中の問合せにタイムアウトを設定する機能や、複雑な問合せから指定された時間内に結果の一部を取得する機能のような、強く求められる特定の機能を利用することができます。アプリケーションに応答時間の制約があるように、問合せの終了を無期限に待機することは、エンド・ユーザーにとって困難です。SPARQL Gatewayは、SPARQLエンドポイントの上位に、タイムアウトとベスト・エフォート型問合せ関数の両方を提供します。このような効率性によって、SPARQL問合せの実行中のセマンティク・データの使用に関して、不確実性がある程度は効果的に排除されます。(「タイムアウト値の指定」「ベスト・エフォート型の問合せ実行の指定」を参照してください。)

7.17.2 SPARQL Gatewayのインストールおよび構成

SPARQL Gatewayをインストールして構成するには、次の主要なステップを実行します(それぞれの項を参照してください)。

  1. RDF Semantic Graph support for Apache Jena .zipファイルをダウンロードする(まだダウンロードしていない場合)

  2. WebLogic ServerでのSPARQL Gatewayのデプロイ

  3. 必要に応じてプロキシ設定を変更する

  4. 必要に応じてOracleSGDSデータソースを構成する

  5. 必要に応じてSparqlGatewayAdminGroupグループを追加し、構成する

7.17.2.1 RDF Semantic Graph support for Apache Jena .zipファイルをダウンロードする(まだダウンロードしていない場合)

RDF Semantic Graph support for Apache Jenaファイルをまだダウンロードしていない場合は、「ソフトウェア環境の設定」の説明に従ってRDFセマンティク・グラフ・ページからファイルをダウンロードし、一時ディレクトリに解凍します。

なお、SPARQL GatewayのJavaクラスの実装は、sdordfclient.jarに含まれています(「SPARQL GatewayのJava APIの使用」を参照)。

7.17.2.2 WebLogic ServerでのSPARQL Gatewayのデプロイ

Oracle WebLogic Serverで、次のようにSPARQL Gatewayをデプロイします。

  1. 次のとおり、WebLogic Serverのautodeployディレクトリに移動し、ビルトインsparqlgateway.warファイルをコピーします。(開発ドメインでのアプリケーションに対する自動デプロイの詳細は、http://docs.oracle.com/cd/E11035_01/wls100/deployment/autodeploy.htmlを参照してください。)

    cp -rf  /tmp/jena_adapter/sparqlgateway_web_app/sparqlgateway.war  <domain_name>/autodeploy/sparqgateway.war
      

    この例では、<domain_name>はWebLogic Serverドメインの名前です。

    次の方法で、ビルトイン・アプリケーションをカスタマイズできます。

    • 必要に応じて、sparqlgateway_web_app/sparqlgateway.warに埋め込まれているWEB-INF/web.xmlファイルを変更します。sparql_gateway_repository_filedirパラメータとsparql_gateway_repository_urlパラメータに対して、適切な値を指定してください。

    • 必要に応じて、XSLTファイルまたはSPARQL問合せファイルを、sparqlgateway_web_app/sparqlgateway.warのトップレベル・ディレクトリに追加します。

      ディレクトリには、Oracle提供のファイルdefault.xsltnoop.xsltおよびqb1.sparqlがあります。default.xsltファイルは、SPARQL問合せレスポンス(XML)を、主にOracleで利用可能な形式に変換するためのものです。

      (これらのファイルについては「SPARQL問合せとXSL変換の格納」を、SPARQL GatewayでのOBIEEの使用については「SPARQL GatewayのOBIEEへのXMLデータソースとしての使用」を参照してください。)

  2. Webブラウザを使用して、次の書式のURLに接続し、デプロイメントを確認します(Webアプリケーションはポート7001でデプロイされているとします)。

    http://<hostname>:7001/sparqlgateway
7.17.2.3 プロキシ設定の変更(必要な場合)

SPARQL Gatewayがファイアウォールの内側にあり、SPARQL Gatewayでファイアウォール内部のみでなくインターネット上のSPARQLエンドポイントとも通信する必要がある場合は、次のJVM設定を使用する必要があります。

-Dhttp.proxyHost=<your_proxy_host>
-Dhttp.proxyPort=<your_proxy_port>
-Dhttp.nonProxyHosts=127.0.0.1|<hostname_1_for_sparql_endpoint_inside_firewall>|<hostname_2_for_sparql_endpoint_inside_firewall>|...|<hostname_n_for_sparql_endpoint_inside_firewall>

これらの設定は、startWebLogic.shスクリプトに指定することができます。

7.17.2.4 OracleSGDSデータ・ソースの構成(必要な場合)

SPARQL GatewayのSPARQL問合せとXSL変換を格納してアクセスするためにOracle Databaseを使用する場合は、OracleSGDSという名前のデータソースが使用可能である必要があります。

OracleSGDSデータソースが構成されていて使用可能な場合、SPARQL Gatewayサーブレットは、すべての必要な表と索引を初期化時に自動作成します。

7.17.2.5 SparqlGatewayAdminGroupグループの追加と構成(必要な場合)

SPARQL Gatewayの次のJSPファイルは、Oracle Databaseに格納されているSPARQL問合せとXSL変換を表示、編集および更新するために役立ちます。

http://<host>:7001/sparqlgateway/admin/sparql.jsp
http://<host>:7001/sparqlgateway/admin/xslt.jsp

これらのファイルは、HTTP Basic認証によって保護されています。WEB-INF/weblogic.xmlに、SparqlGatewayAdminGroupという名前のプリンシパルが定義されます。

これらのJSPページのどちらにでもログインできるように、WebLogic Serverを使用してSparqlGatewayAdminGroupという名前のグループを追加し、新しいユーザーを作成するか、または既存ユーザーをこのグループに割り当てる必要があります。

7.17.3 SPARQL Gatewayでのセマンティク・データの使用

SPARQL Gatewayと対話するアプリケーションの主要なインタフェースには、次の形式のURLを使用します。

http://host:port/sparqlgateway/sg?<SPARQL_ENDPOINT>&<SPARQL_QUERY>&<XSLT>

前述の形式の説明:

  • <SPARQL_ENDPOINT>では、eeパラメータを指定します。SPARQLエンドポイントのURLエンコード形式を含みます。

    たとえば、ee=http%3A%2F%2Fsparql.org%2FbooksはSPARQLエンドポイントhttp://sparql.org/booksのURLエンコード文字列です。これは、SPARQL問合せがエンドポイントhttp://sparql.org/booksに対して実行されることを意味します。

  • <SPARQL_QUERY>では、SPARQL問合せ、またはSPARQL問合せの場所を指定します。

    アプリケーションで非常に長いURLを使用できる場合は、SPARQL問合せ全体をエンコードし、URLにeq=<encoded_SPARQL_query>を設定することができます。あまり長いURLをアプリケーションで使用できない場合は、「SPARQL問合せとXSL変換の格納」で説明されている方法の1つを使用して、SPARQL問合せを格納し、これらをSPARQL Gatewayで使用可能にすることができます。

  • <XSLT>では、XSL変換またはXSL変換の場所を指定します。

    アプリケーションで非常に長いURLを使用できる場合は、XSL変換全体をエンコードし、URLにex=<encoded_XSLT>を設定することができます。あまり長いURLをアプリケーションで使用できない場合は、「SPARQL問合せとXSL変換の格納」で説明されている方法の1つを使用して、XSL変換を格納し、これらをSPARQL Gatewayで使用可能にすることができます。

7.17.3.1 SPARQL問合せとXSL変換の格納

あまり長いURLをアプリケーションで使用できない場合は、SPARQL問合せとXSL変換の場所を、「SPARQL Gatewayでのセマンティク・データの使用」の説明にあるURL形式の<SPARQL_QUERY><XSLT>の部分に指定できます。次のいずれかの方法を使用します。

  • SPARQL問合せとXSL変換をSPARQL Gateway Webアプリケーション自体に格納します。

    これを行うには、sparqlgateway.warファイルを解凍し、SPARQL問合せとXSL変換を最上位ディレクトリに格納した後、sparqlgateway.warファイルを圧縮して、再デプロイします。

    sparqlgateway.warファイルには、サンプル・ファイルqb1.sparql (SPARQL問合せ)およびdefault.xslt (XSL変換)が含まれています。

    ヒント:

    SPARQL問合せファイルには、ファイル拡張子.sparqlを使用し、XSL変換ファイルには、ファイル拡張子.xsltを使用します。

    (提供されているサンプル・ファイルの名前を使用して)これらのファイルを指定する構文は、SPARQL問合せファイルについてはwq=qb1.sparql、XSL変換ファイルについてはwx=default.xsltです。

    デフォルトのXSL変換をカスタマイズする必要がある場合は、「デフォルトのXSLTファイルのカスタマイズ」の例を参照してください。

    wx=noop.xsltを指定すると、XSL変換は実行されず、SPARQLレスポンスはそのままでクライアントに戻されます。

  • ファイル・システム・ディレクトリにSPARQL問合せとXSL変換を格納し、そのディレクトリがデプロイ済のSPARQL Gateway Webアプリケーションにアクセス可能なことを確認します。

    デフォルトでは、次の<init-param>設定に示すとおり、そのディレクトリは/tmpに設定されています。

    <init-param>
       <param-name>sparql_gateway_repository_filedir</param-name>
       <param-value>/tmp/</param-value>
    </init-param>
    

    SPARQL Gatewayをデプロイする前に、このディレクトリをカスタマイズすることをお薦めします。ディレクトリ設定を変更するには、<param-value>タグと</param-value>タグの間のテキストを編集します。

    次の例では、sparql_gateway_repository_filedir<init-param>要素で指定されたディレクトリのSPARQL問合せファイルとXSL変換ファイルを指定します。

    fq=qb1.sparql
    fx=myxslt1.xslt
    
  • SPARQL問合せとXSL変換をWebサイトからアクセス可能にします。

    デフォルトでは、次の<init-param>設定に示すとおり、そのWebサイト・ディレクトリはhttp://127.0.0.1/queries/に設定されています。

    <init-param>
       <param-name>sparql_gateway_repository_url</param-name>
       <param-value>http://127.0.0.1/queries/</param-value>
    </init-param>
    

    このディレクトリは、SPARQL Gatewayをデプロイする前にカスタマイズします。Webサイト設定を変更するには、<param-value>タグと</param-value>タグの間のテキストを編集します。

    次の例では、sparql_gateway_repository_url<init-param>要素で指定されたURLのSPARQL問合せファイルとXSL変換ファイルを指定します。

    uq=qb1.sparql
    ux=myxslt1.xslt
    

    SPARQL Gatewayの内部では、適切で完全なURLを計算し、内容をフェッチして、問合せ実行を開始し、問合せのレスポンスXMLにXSL変換を適用します。

  • SPARQL問合せとXSL変換をOracle Databaseに格納します。

    この方法では、J2EEデータソースOracleSGDSを定義しておく必要があります。SPARQL GatewayがOracleSGDSデータソースからデータベース接続を取得すると、データベース表ORACLE_ORARDF_SG_QUERYから指定された整数IDを使用してSPARQL問合せが読み取られます。

    Oracle DatabaseからSPARQL問合せをフェッチするための構文はdq=<integer-id>、Oracle DatabaseからXSL変換をフェッチするための構文はdx=<integer-id>です。

    サーブレットの初期化時に、次の表がまだ存在しない場合は、自動的に作成されます(手動で作成する必要はありません)。

    • 主キーがQID (整数型)のORACLE_ORARDF_SG_QUERY

    • 主キーがXID (整数型)のORACLE_ORARDF_SG_XSLT

7.17.3.2 タイムアウト値の指定

実行時間が長くなる可能性のある問合せを「セマンティク・データを使用したSPARQL Gatewayの使用」で説明されているURL形式を使用して送信する場合、タイムアウト値をミリ秒で指定して、実行時間を制限することができます。たとえば、SPARQL Gatewayから開始したSPARQL問合せの実行が、1000ミリ秒(1秒)後に終了されるというURL形式とタイムアウトの指定を次に示します。

http://host:port/sparqlgateway/sg?<SPARQL_ENDPOINT>&<SPARQL_QUERY>&<XSLT>&t=1000

タイムアウトが発生したときに問合せが終了していない場合、空のSPARQLレスポンスがSPARQL Gatewayによって構築されます。

SPARQL GatewayがHTTP接続レベルで問合せ実行をタイムアウトしても、サーバー側では問合せの実行が続く場合もあることに注意してください。実際の動作はベンダーによって異なります。

7.17.3.3 ベスト・エフォート型問合せ実行の指定

ノート:

ベスト・エフォート型問合せ実行は、タイムアウト値も指定する場合にのみ指定できます(「タイムアウト値の指定」を参照)。

実行時間が長くなる可能性のある問合せを「セマンティク・データを使用したSPARQL Gatewayの使用」で説明されているURL形式を使用して送信する場合、タイムアウト値を指定することで、その問合せにベスト・エフォート型制限を指定することもできます。たとえば次に示すURL形式には、1000ミリ秒(1秒)のタイムアウト指定と、ベスト・エフォート型指定(& b=t)があわせて示されています。

http://host:port/sparqlgateway/sg?<SPARQL_ENDPOINT>&<SPARQL_QUERY>&<XSLT>&t=1000&b=t

web.xmlファイルには、ベスト・エフォート型オプションの動作に影響する2つのパラメータ設定(sparql_gateway_besteffort_maxroundsおよびsparql_gateway_besteffort_maxthreads)が含まれます。デフォルトの定義を次に示します。

<init-param>
  <param-name>sparql_gateway_besteffort_maxrounds</param-name>
  <param-value>10</param-value>
</init-param>
 
<init-param>
  <param-name>sparql_gateway_besteffort_maxthreads</param-name>
  <param-value>3</param-value>
</init-param>

SPARQLのSELECT問合せがベスト・エフォート型方式で実行されるとき、一連の問合せはSPARQL問合せ本体のLIMIT値を増加させた設定で実行されます。(中心的な考え方は、LIMIT設定をより小さくすることで、SPARQL問合せはより高速に実行されるという見解に基づいています。)SPARQL Gatewayは、LIMIT 1設定で問合せ実行を開始します。タイムアウトの前に、この問合せを完了できることが理想です。この場合、次の問合せでLIMIT制限が増加し、後続の問合せではより高い制限があるとします。問合せ実行の最大数は、sparql_gateway_besteffort_maxroundsパラメータによって制御されます。

一連の問合せをパラレルで実行可能な場合、sparql_gateway_besteffort_maxthreadsパラメータで並列度を制御します。

7.17.3.4 text/xml以外のコンテンツ・タイプの指定

デフォルトでは、SPARQL GatewayはXSL変換がXMLを生成することを前提としているため、HTTPレスポンスに設定されているデフォルトのコンテンツ・タイプはtext/xmlです。ただし、アプリケーションでXML以外のレスポンス形式が必要である場合は、次の書式でその形式を追加のURLパラメータ(構文&rt=を使用)に指定することができます。

http://host:port/sparqlgateway/sg?<SPARQL_ENDPOINT>&<SPARQL_QUERY>&<XSLT>&rt=<content_type>

<content_type>はURLエンコードされている必要があることに注意します。

7.17.4 デフォルトのXSLTファイルのカスタマイズ

デフォルトのXSL変換ファイル(wx=default.xsltを使用して参照される)をカスタマイズすることができます。この項では、いくつかのカスタマイズ例を示します。

次の例では、ある変数のバインドがhttp://purl.org/goodrelations/v1#で始まるURIを戻したときは、その部分をgr:に置換し、http://www.w3.org/2000/01/rdf-schema#で始まるURIを戻したときは、その部分をrdfs:に置換する、というネームスペース接頭辞置換ロジックを実装します。

<xsl:when test="starts-with(text(),'http://purl.org/goodrelations/v1#')">
   <xsl:value-of select="concat('gr:',substring-after(text(),'http://purl.org/goodrelations/v1#'))"/>
</xsl:when>
...
<xsl:when test="starts-with(text(),'http://www.w3.org/2000/01/rdf-schema#')">
   <xsl:value-of select="concat('rdfs:',substring-after(text(),'http://www.w3.org/2000/01/rdf-schema#'))"/>
</xsl:when>

次の例では、先頭のhttp://localhost/または先頭のhttp://127.0.0.1/を切り捨てるロジックを実装します。

<xsl:when test="starts-with(text(),'http://localhost/')">
  <xsl:value-of select="substring-after(text(),'http://localhost/')"/>
</xsl:when>
<xsl:when test="starts-with(text(),'http://127.0.0.1/')">
  <xsl:value-of select="substring-after(text(),'http://127.0.0.1/')"/>
</xsl:when>

7.17.5 SPARQL GatewayのJava APIの使用

Webインタフェースに加え、SPARQL Gateway管理サービスには、SPARQL問合せと関連するXSL変換を管理するための便利なJavaアプリケーション・プログラミング・インタフェース(API)があります。Java APIは、RDF Semantic Graph support for Apache Jenaライブラリsdordfclient.jarに含まれます。

Java APIリファレンス情報は、SPARQL Gatewayの.zipファイルに含まれるjavadoc_sparqlgateway.zipファイルで入手できます(未入手の場合、RDF Semantic Graph Support for Apache Jena .zipファイルのダウンロードを参照してください)。

このAPIのメイン・エントリ・ポイントはoracle.spatial.rdf.client.jena.SGDBHandlerクラス(SPARQL Gatewayデータベース・ハンドラ)で、問合せと変換を管理するために次のような静的メソッドを提供します。

  • deleteSparqlQuery(Connection, int)

  • deleteXslt(Connection, int)

  • insertSparqlQuery(Connection, int, String, String, boolean)

  • insertXslt(Connection, int, String, String, boolean)

  • getSparqlQuery(Connection, int, StringBuilder, StringBuilder)

  • getXslt(Connection, int, StringBuilder, StringBuilder)

これらのメソッドは、Oracle Databaseインスタンスに格納されているSPARQL Gateway関連の表のエントリを操作し、取得します。これらのメソッドを使用するには、必要な関連表がすでに存在している必要があります。表が存在しない場合は、SPARQL GatewayをWebサーバーにデプロイし、次の形式でURLにアクセスします。

http://<host>:<port>/sparqlgateway/sg?

<host>はWebサーバーのホスト名で、<port>はWebサーバーのリスニング・ポートです。表がまだ存在しない場合、このURLにアクセスすると、必要な表が自動的に作成されます。

Java APIを介して行われた変更は、管理Webインタフェースを介して行われた変更と同様に、SPARQL GatewayのWebサービスに影響を及ぼします。これにより、最も都合のよいインタフェースを使用して柔軟に問合せと変換を管理できます。

Java APIにより提供される挿入メソッドでは、表に格納されている既存の問合せや変換を置き換えることができない点に注意してください。既存の問合せまたは変換を置換しようとすると失敗します。問合せまたは変換を置き換えるには、削除メソッドの1つを使用して表にある既存のエントリを削除した後に、挿入メソッドの1つを使用して新しい問合せまたは変換を挿入する必要があります。

次の例に、Java APIを使用して一般的な管理タスクを実行する方法を示します。この例では、SPARQL Gatewayの基盤である、基礎となるOracle Databaseインスタンスへの接続がすでに確立されていることを前提としています。

例7-33 SPARQL問合せとXSL変換の格納

例7-33では、SPARQL Gatewayを使用したデータベースに、問合せとXSL変換を追加します。問合せと変換が追加された後、他のプログラムでは、リクエストURLで適切な問合せID (qid)とXSL変換ID (xid)を指定することで、ゲートウェイを介して問合せと変換を使用することができます。

なお、例7-33では、問合せと変換の両方が挿入されていますが、問合せと変換には必ずしも関連はなく、SPARQL Gatewayにアクセスするときに同時に使用される必要もありません。SPARQL Gatewayにリクエストを送信するときは、データベースの任意の問合せをデータベースの任意の変換とともに使用することができます。

String query = "PREFIX ... SELECT ..."; // full SPARQL query text
String xslt  = "<?xml ...> ...";        // full XSLT transformation text
 
String queryDesc = "Conference attendee information"; // description of SPARQL query
String xsltDesc = "BIEE table widget transformation"; // description of XSLT transformation
 
int queryId = queryIdCounter++; // assign a unique ID to this query
int xsltId  = xsltIdCounter++;  // assign a unique ID to this transformation
 
// Inserting a query or transformation will fail if the table already contains
// an entry with the same ID.  Setting this boolean to true will ignore these
// exceptions (but the table will remain unchanged). Here we specify that we
// want an exception thrown if we encounter a duplicate ID.
boolean ignoreDupException = false;
 
// add the query
try {
  // Delete query if one already exists with this ID (this will not throw an
  // error if no such entry exists)
  SGDBHandler.deleteSparqlQuery( connection, queryId );
  SGDBHandler.insertSparqlQuery( connection, queryId, query, queryDesc, ignoreDupException );
} catch( SQLException sqle ) {
  // Handle exception
} catch( QueryException qe ) {
  // Handle query syntax exception
}
 
// add the XSLT
try {
  // Delete xslt if one already exists with this ID (this will not throw an
  // error if no such entry exists)
  SGDBHandler.deleteXslt( connection, xsltId );
  SGDBHandler.insertXslt( connection, xsltId, xslt, xsltDesc, ignoreDupException );
}  catch( SQLException sqle ) {
  // Handle database exception
} catch( TransformerConfigurationException tce ) {
  // Handle XSLT syntax exception
}

例7-34 問合せの変更

例7-34では、データベースから既存の問合せを取得し、それを変更した後、更新された問合せを再度データベースに格納します。これらのステップでは、問合せの編集と変更の保存をシミュレートします。(問合せが存在しない場合は、例外がスローされます。)

StringBuilder query;
StringBuilder description;
 
// Populate these with the query text and description from the database
query = new StringBuilder( );
description = new StringBuilder( );
 
// Get the query from the database
try {
  SGDBHandler.getSparqlQuery( connection, queryId, query, description );
} catch( SQLException sqle ) {
  // Handle exception
  // NOTE: exception is thrown if query with specified ID does not exist
}
 
// The query and description should be populated now
 
// Modify the query 
String updatedQuery = query.toString( ).replaceAll("invite", "attendee");
 
// Insert the query back into the database
boolean ignoreDup = false;
try {
  // First must delete the old query
  SGDBHandler.deleteSparqlQuery( connection, queryId );
  // Now we can add
  SGDBHandler.insertSparqlQuery( connection, queryId, updatedQuery, description.toString( ), ignoreDup );
} catch( SQLException sqle ) {
  // Handle exception
} catch( QueryException qe ) {
  // Handle query syntax exception
}

例7-35 XSL変換の取得および出力

例7-35では、既存のXSL変換を取得し、標準出力に出力します。(変換が存在しない場合は、例外がスローされます。)

StringBuilder xslt;
StringBuilder description;
 
// Populate these with the XSLT text and description from the database
xslt = new StringBuilder( );
description = new StringBuilder( );
 
try {
  SGDBHandler.getXslt( connection, xsltId, xslt, description );
} catch( SQLException sqle ) {
  // Handle exception
  // NOTE: exception is thrown if transformation with specified ID does not exist
}
 
// Print it to standard output
System.out.printf( "XSLT description: %s\n", description.toString( ) );
System.out.printf( "XSLT body:\n%s\n", xslt.toString( ) );

7.17.6 SPARQL GatewayのグラフィカルなWebインタフェースの使用

SPARQL Gatewayでは、問合せのテスト、セマンティク・データのナビゲート、SPQARQL問合せとXSLTファイルの管理に役立つブラウザベースのインタフェースがいくつか用意されています。

7.17.6.1 メイン・ページ(index.html)

http://<host>:<port>/sparqlgateway/index.htmlは、SPARQL問合せを実行し、さらにdefault.xsltの変換を応答に対して適用するための、単純なインタフェースです。図7-2に、このインタフェースでの問合せの実行を示します。

図7-2 グラフィカル・インタフェースのメイン・ぺージ(index.html)

図7-2の説明が続きます
「図7-2 グラフィカル・インタフェースのメイン・ページ(index.html)」の説明

「SPARQL Endpoint」を入力または選択し、「SPARQL SELECT Query Body」を指定して「Submit Query」を押します。

たとえばSPARQLエンドポイントにhttp://dbpedia.org/sparqlを指定し、図7-2のSPARQL問合せ本体を使用すると、そのレスポンスは図7-3と同様です。この図のXML出力には、デフォルトの変換(default.xslt)が適用されています。

図7-3 SPARQL問合せメイン・ぺージのレスポンス

図7-3の説明が続きます
「図7-3 SPARQL問合せメイン・ページのレスポンス」の説明
7.17.6.2 ナビゲーションとブラウジングのページ(browse.jsp)

http://<host>:<port>/sparqlgateway/browse.jspでは、セマンティク・データ用のナビゲーションとブラウジング機能が提供されています。標準に準拠したすべてのSPARQLエンドポイントに対して使用できます。図7-4に、このインタフェースでの問合せの実行を示します。

図7-4 グラフィカル・インタフェースによるナビゲーションとブラウジングのページ(browse.jsp)

図7-4の説明が続きます
「図7-4 グラフィカル・インタフェースによるナビゲーションとブラウジングのページ(browse.jsp)」の説明

「SPARQL Endpoint」を入力または選択し、「SPARQL SELECT Query Body」を指定し、オプションで「Timeout (ms)」値(ミリ秒)と「Best Effort」オプションを指定して、「Submit Query」を押します。

図7-5に示すとおり、SPARQLレスポンスが解析され、表形式で示されます。

図7-5 ブラウジングとナビゲーションのページ: レスポンス

図7-5の説明が続きます
「図7-5 ブラウジングとナビゲーションのページ: レスポンス」の説明

図7-5で、URIはナビゲーションできるようにクリック可能であり、ユーザーがURI上でカーソルを移動すると、読みやすいように短縮されていたURIのツールチップ(図のhttp://purl.org.dc/elements/1.1/titledc:titleのツールチップが表示されています)が表示されます。

図7-5に示されている出力のURIhttp://example.org/book/book5をクリックすると、新しいSPARQL問合せが自動的に生成され、実行されます。図7-6に示すとおり、この生成されたSPARQL問合せには、この特定のURIを、主語、述語および目的語として使用する、3つの問合せパターンがあります。このような問合せによって、このURIがどのように使用されるか、およびデータセットの他のリソースとどのように関連するかを理解することができます。

図7-6 URIリンクのクリックによる問合せとレスポンス

図7-6の説明が続きます
「図7-6 URIリンクのクリックによる問合せとレスポンス」の説明

問合せに多数の一致があると、その結果がページに分類され、どのページでもクリックすることができます。デフォルトでは50個の結果が1ページに表示されます。ブラウジングとナビゲーションのページ(browse.jsp)でのレスポンスを、ページ当たり50行よりも多く(あるいは少なく)表示するには、URLに&resultsPerPageパラメータを指定します。たとえばページ当たり100行を表示できるようにするには、URLに次を含めます。

&resultsPerPage=100
7.17.6.3 XSLTの管理ページ(xslt.jsp)

http://<host>:<port>/sparqlgateway/admin/xslt.jspは、単純なXSLT管理インタフェースです。XSLT ID(整数)を入力して「XSLTの取得」をクリックすると、説明とXSLT本体を取得できます。XSLT本体のテキストを変更した後に「XSLTの保存」をクリックすることで、その変更を保存できます。使用可能なXSLT定義のナビゲートに役立つプレビューアがあります。

図7-7は、XSLTの管理ページを示しています。

図7-7 XSLTの管理ページ

図7-7の説明が続きます
「図7-7 XSLT管理ページ」の説明
7.17.6.4 SPARQLの管理ページ(sparql.jsp)

http://<host>:<port>/sparqlgateway/admin/xslt.jspは、単純なSPARQL管理インタフェースです。SPARQL ID(整数)を入力して「SPARQLの取得」をクリックすると、説明とSPARQL本体を取得できます。SPARQL本体のテキストを変更した後に「SPARQLの保存」をクリックすることで、その変更を保存できます。使用可能なSPARQL問合せのナビゲートに役立つプレビューアがあります。

図7-8は、SPARQLの管理ページを示しています。

図7-8 SPARQLの管理ページ

図7-8の説明が続きます
「図7-8 SPARQL管理ページ」の説明

7.17.7 OBIEEへのXMLデータソースとしてのSPARQL Gatewayの使用

この項では、SPARQL Gatewayをブリッジとして使用してOBIEEとRDFを統合することによって、Oracle Business Intelligence Enterprise Edition (OBIEE)で使用するXMLデータ・ソースを作成する方法について説明します。(具体的なステップおよび図は、Oracle BI管理ツールのバージョン11.1.1.3.0.100806.0408.000によるものです。)

  1. Oracle BI管理ツールを起動します。
  2. 「ファイル」「メタデータのインポート」を順次クリックします。図7-9に示すとおり、メタデータのインポート・ウィザードの最初のページが表示されます。

    図7-9 「メタデータのインポート」 - 「データ・ソースの選択」

    図7-9の説明が続きます
    「図7-9 メタデータのインポート - データ・ソースの選択」の説明

    接続タイプ: XMLを選択します。

    URL: SPARQL Gateway(「セマンティク・データを使用したSPARQL Gatewayの使用」を参照)と対話するアプリケーションのURL。タイムアウト・オプションおよびベスト・エフォート型オプションを含めることもできます。

    「ユーザー名」フィールドと「パスワード」フィールドは無視します。

  3. 「次」をクリックします。図7-10に示すとおり、メタデータのインポート・ウィザードの2番目のページが表示されます。

    図7-10 「メタデータのインポート」 - 「メタデータ型の選択」

    図7-10の説明が続きます
    「図7-10 メタデータのインポート - メタデータ型の選択」の説明

    インポートする必要なメタデータ型を選択します。選択したタイプに「表」が含まれていることを確認します。

  4. 「次」をクリックします。図7-11に示すとおり、メタデータのインポート・ウィザードの3番目のページが表示されます。

    図7-11 「メタデータのインポート」 - 「メタデータ・オブジェクトの選択」

    図7-11の説明が続きます
    「図7-11 メタデータのインポート - メタデータ・オブジェクトの選択」の説明

    「データソース・ビュー」で、表アイコンを持つノードを展開し、(SPARQL SELECT構文に定義した投影変数からマップされた)列名を選択して右矢印(>)ボタンをクリックし、選択した列を「リポジトリ・ビュー」に移動します。

  5. 「終了」をクリックします。
  6. (SPARQL GatewayまたはRDFデータに固有ではない)通常のBIビジネス・モデルの作業およびマッピングと表現定義の作業について、残りのステップを実行します。

7.18 Apache TomcatにおけるFusekiのデプロイ

Apache TomcatにFusekiをデプロイするには、Tomcatの管理Webページを使用するか、Fusekiの.warファイルをTomcatのwebappsフォルダにコピーして自動的にデプロイします。

このトピックでは自動デプロイ・ステップについて説明します。$FUSEKI_BASEの設定が完了し、構成ファイルが存在する(デフォルトでは、Fusekiの構成ファイルを格納するディレクトリとして/etc/fusekiが使用されます)。

  1. 最新バージョンのApache Tomcatをダウンロードしてインストールします。

    これらの手順では、Apache Tomcatインストール・ディレクトリのルートを、$CATALINA_HOMEと呼びます。

  2. fuseki.warをTomcatのwebappsフォルダにコピーします。たとえば、次のようにします。

    cd $CATALINA_HOME/webapps
    cp /tmp/jena_adapter/fuseki_web_app/fuseki.war  .
    
  3. Tomcatを起動します。

    $CATALINA_HOME/bin/startup.sh
    

    このファイルに実行権限がない場合は、次のコマンドを入力してから再度Tomcatを起動します。

    chmod u+x $CATALINA_HOME/bin/startup.sh
    
  4. ブラウザで、http://hostname:8080/fusekiに移動します。

7.19 RDFデータをバルク・ロードするためのORARDFLDRユーティリティ

この項では、ORARDFLDRユーティリティ・プログラムを使用したRDFデータのバルク・ロードについて説明します。

このユーティリティ・プログラムは、ディレクトリ内のすべてのファイルをOracleデータベースのセマンティク・モデルにロードします。RDF/XML、Turtle、N-Triple、N-Quads、Trigなど、複数のRDFシリアライズをサポートしています。gzipで圧縮されたファイルは、gzipファイルを解凍せずに直接ロードできます。また、Unicode文字エスケープおよびロング・リテラル(CLOB)は自動的に処理されます。

ORARDFLDRユーティリティ・プログラムの実行

ORARDFLDRを実行するコマンドを次に示します。

前提条件: 環境変数${ORACLE_JENA_HOME}がOTNキットが格納されているディレクトリを指していることを確認します。

使用方法:
java -cp ${ORACLE_JENA_HOME}/jar/'*' oracle.spatial.rdf.client.jena.utilities.RDFLoader <command_line_arguments>
ヘルプの詳細:
java -cp ${ORACLE_JENA_HOME}/jar/'*' oracle.spatial.rdf.client.jena.utilities.RDFLoader --help

便宜上、binディレクトリ内のシェル・スクリプトも実行できます。次に、このスクリプトを使用するコマンドについて説明します。

前提条件: ${ORACLE_JENA_HOME}を設定し、${ORACLE_JENA_HOME}/binがUnix PATH環境変数に含まれていることを確認します。

使用方法:
orardfldr <command_line_arguments>
ヘルプの詳細:
orardfldr --help

7.19.1 Oracle Autonomous DatabaseでのORARDFLDRの使用

この項では、ORARDFLDRユーティリティをOracle Autonomous Databaseで使用する方法について説明します。

Support for Apache Jenaに含まれるORARDFLDRユーティリティを使用して、クライアント・コンピュータからOracle Autonomous DatabaseにRDFファイルをバルク・ロードできます。データベースとの接続はクラウド・ウォレットに基づきます。

JDBCを使用してOracle Autonomous Databaseに接続する一般的な手順は、ATPへのJava接続を参照してください。

次の例では、JKSファイルを使用したプレーンJDBCの手順に従って、Oracle Autonomous DatabaseへのJDBC接続を確立する方法について説明します。

例7-36 Oracle Autonomous DatabaseへのJDBC接続

前提条件: Oracle jarファイルojdbc8.jarucp.jaroraclepki.jarosdt_core.jarおよびosdt_cert.jarがあることを確認します。

  1. wallet_<dbname>.zipファイルを解凍します。ファイルを解凍すると、次のリストのようなもの表示されます。
    [oracle@localhost Wallet_Info]$ ls
    cwallet.sso  keystore.jks      README      tnsnames.ora
    ewallet.p12  ojdbc.properties  sqlnet.ora  truststore.jks
  2. JKS関連の接続プロパティを追加するには、ojdbc.propertiesを変更します。ojdbc.propertiesファイルの最終バージョンは次のようになります。

    # Connection property while using Oracle wallets.
    #oracle.net.wallet_location=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=${TNS_ADMIN})))
    # FOLLOW THESE STEPS FOR USING JKS
    # (1) Uncomment the following properties to use JKS.
    # (2) Comment out the oracle.net.wallet_location property above
    # (3) Set the correct password for both trustStorePassword and keyStorePassword.
    # It's the password you specified when downloading the wallet from OCI Console or the Service Console.
    javax.net.ssl.trustStore=${TNS_ADMIN}/truststore.jks
    javax.net.ssl.trustStorePassword=password
    javax.net.ssl.keyStore=${TNS_ADMIN}/keystore.jks
    javax.net.ssl.keyStorePassword=password

次のJDBC URLを使用します。

jdbc:oracle:thin:@dbname_alias?TNS_ADMIN=<path_to_wallet_directory>

次の例では、rdfdbという名前のデータベースに対してORAFLDRユーティリティを使用し、ウォレット・ディレクトリを/home/oracle/RDF/Wallet_Info/にしてRDFファイルをロードします。

例7-37 ORAFLDRユーティリティを使用したRDFデータ・ファイルのロード

前提条件: 例7-36にリストされている前提条件jarが$ORACLE_JENA_HOME/jar/にコピーされていることを確認します。

ORARDFLDRを起動して、クライアント・コンピュータからAutonomous DatabaseにRDFファイルをロードします。

orardfldr --modelName=M1 --fileDir=./data --lang=N-TRIPLE 
    --jdbcUrl=jdbc:oracle:thin:@rdfdb_medium?TNS_ADMIN=/home/oracle/RDF/Wallet_Info/ 
    --user="RDFUSER" --password=password --networkOwner="RDFUSER" --networkName=NET1

RDFデータをN-Triple形式でRDFUSERが所有するNET1という名前のネットワーク内のM1という名前のモデルにロードします。RDFUSERは、データベース接続にも使用されます。