2 Oracle Database環境でのプロパティ・グラフの使用

この章では、Oracle Database環境でのプロパティ・グラフ・データの作成、格納、操作に関する概念および使用方法の情報を提供します。

トピック:

2.1 プロパティ・グラフについて

プロパティ・グラフでは、プロパティ(キー値ペア)をグラフの頂点およびエッジに容易に関連付けることができ、大量のデータ・セット間の関係に基づく分析操作が可能になります。

トピック:

2.1.1 プロパティ・グラフとは

プロパティ・グラフは、オブジェクトまたは頂点のセットと、これらのオブジェクトをつなぐ矢印またはエッジのセットで構成されます。頂点およびエッジには複数のプロパティを含めることができ、それらはキー値ペアとして表されます。

各頂点には一意の識別子があり、次のものを含めることができます。

  • 出力エッジのセット

  • 入力エッジのセット

  • プロパティの集まり

各エッジには一意の識別子があり、次のものを含めることができます。

  • 出力頂点

  • 入力頂点

  • 2つの頂点間の関係を示すテキスト・ラベル

  • プロパティの集まり

次の図に、2つの頂点と1つのエッジを持つ非常に単純なプロパティ・グラフを示します。2つの頂点には識別子1および2があります。両方の頂点には、プロパティnameおよびageがあります。エッジは、出力頂点1から入力頂点2に向かっています。このエッジは、テキスト・ラベルknowsと、頂点1と2の関係のタイプを識別するプロパティtypeで表されます。

図2-1 単純なプロパティ・グラフの例

図2-1の説明が続きます
「図2-1 単純なプロパティ・グラフの例」の説明

Big Data Spatial and Graphプロパティ・グラフ・データ・モデルは標準に基づいていませんが、W3C標準ベースのResource Description Framework (RDF)グラフ・データ・モデルに類似しています。プロパティ・グラフ・データ・モデルはRDFよりも単純であり、精密ではありません。このような相違点により、これは次のようなユースケースでよい候補となります。

  • ソーシャル・ネットワークでの影響の特定

  • 傾向と顧客行動の予測

  • パターン一致に基づく関係の発見

  • キャンペーンをカスタマイズするためのクラスタの特定

注意:

データベース側でOracleがサポートするプロパティ・グラフ・データ・モデルでは、頂点にラベル付けできません。しかし、指定した頂点プロパティの値を、1つ以上のラベルとして扱うことができます。

関連トピック

2.1.2 Oracle Databaseのプロパティ・グラフのサポートとは

プロパティ・グラフは、Hadoopのビッグ・データに対してサポートされているのに加えて、Oracle Databaseでサポートされます。このサポートは、PL/SQLパッケージ、データ・アクセス・レイヤーおよび分析レイヤーから構成されます。

次の図に、Oracleプロパティ・グラフ・アーキテクチャの概要を示します。

図2-2 Oracleプロパティ・グラフ・アーキテクチャ

図2-2の説明が続きます
「図2-2 Oracleプロパティ・グラフ・アーキテクチャ」の説明

トピック:

2.1.2.1 インメモリー・アナリスト

インメモリー・アナリスト・レイヤーにより、並列のインメモリー実行を使用して、プロパティ・グラフを分析できます。パス計算、ランキング、コミュニティ検出、リコメンデーションなど、35以上の分析機能を提供します。

2.1.2.2 データ・アクセス・レイヤー

データ・アクセス・レイヤーは、プロパティ・グラフの作成および削除、頂点およびエッジの追加および削除、キー値ペアを使用した頂点およびエッジの検索、テキスト索引の作成、およびその他の操作の実行に使用できる、Java APIのセットを提供します。Java APIには、プロパティ・グラフ・データ・モデル用のTinkerPop Blueprintsグラフ・インタフェースの実装が含まれています。また、このAPIは、広く普及しているオープンソースのテキスト索引付けおよび検索エンジンであるApache LuceneおよびApache SolrCloudと統合されています。

2.1.2.3 ストレージ管理

プロパティ・グラフはOracle Databaseに格納されます。プロパティ・グラフの頂点およびエッジをモデル化するために表が内部的に使用されます。

2.2 プロパティ・グラフのデータ形式について

次のグラフ形式がプロパティ・グラフのデータでサポートされます。

トピック:

2.2.1 GraphMLデータ形式

GraphMLファイル形式では、XMLを使用してグラフを記述します。このトピックの例は、「プロパティ・グラフとは」に示されているプロパティ・グラフのGraphMLの記述を示しています。

例2-1 単純なプロパティ・グラフのGraphMLの記述

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns">
    <key id="name" for="node" attr.name="name" attr.type="string"/>
    <key id="age" for="node" attr.name="age" attr.type="int"/>
    <key id="type" for="edge" attr.name="type" attr.type="string"/>
    <graph id="PG" edgedefault="directed">
        <node id="1">
            <data key="name">Alice</data>
            <data key="age">31</data>
        </node>
        <node id="2">
            <data key="name">Bob</data>
            <data key="age">27</data>
        </node>
        <edge id="3" source="1" target="2" label="knows">
            <data key="type">friends</data>
        </edge>
    </graph>
</graphml>

関連トピック

2.2.2 GraphSONデータ形式

GraphSONファイル形式では、JavaScript Object Notation (JSON)に基づいてグラフを記述します。このトピックの例は、「プロパティ・グラフとは」に示されているプロパティ・グラフのGraphSONの記述を示しています。

例2-2 単純なプロパティ・グラフのGraphSONの記述

{
    "graph": {
        "mode":"NORMAL",
        "vertices": [
            {
                "name": "Alice",
                "age": 31,
                "_id": "1",
                "_type": "vertex"
            },
            {
                "name": "Bob",
                "age": 27,
                "_id": "2",
                "_type": "vertex"
            }       
        ],
        "edges": [
            {
                "type": "friends",
                "_id": "3",
                "_type": "edge",
                "_outV": "1",
                "_inV": "2",
                "_label": "knows"
            }
        ]
    }
}

2.2.3 GMLデータ形式

Graph Modeling Language (GML)ファイル形式では、ASCIIを使用してグラフを記述します。このトピックの例は、「プロパティ・グラフとは」に示されているプロパティ・グラフのGMLの記述を示しています。

例2-3 単純なプロパティ・グラフのGMLの記述

graph [
   comment "Simple property graph"
   directed 1
   IsPlanar 1
   node [
      id 1
      label "1"
      name "Alice"
      age 31
        ]
   node [
      id 2
      label "2"
      name "Bob"
      age 27
        ]
   edge [
      source 1
      target 2
      label "knows"
      type "friends"
        ]
      ]

2.2.4 Oracleフラット・ファイル形式

Oracleフラット・ファイル形式はプロパティ・グラフのみを記述します。これは他のファイル形式よりも正確で、より優れたデータ型サポートを提供します。Oracleフラット・ファイル形式は、グラフを記述するために、頂点用とエッジ用にそれぞれ1つずつ、2つのファイルを使用します。レコードのフィールドはカンマで区切られます。

例2-4 単純なプロパティ・グラフのOracleフラット・ファイルの記述

次に、「プロパティ・グラフとは」に示す単純なプロパティ・グラフの例を記述するOracleフラット・ファイルを示します。

頂点ファイル:

1,name,1,Alice,,
1,age,2,,31,
2,name,1,Bob,,
2,age,2,,27,

エッジ・ファイル:

1,1,2,knows,type,1,friends,, 

2.3 Oracle Databaseのプロパティ・グラフ・スキーマ・オブジェクト

プロパティ・グラフPL/SQLおよびJava APIは特別なOracleデータベース・スキーマ・オブジェクトを使用します。

Oracle Spatial and Graphにより、Oracle Databaseのプロパティ・グラフ・データを格納、問合せ、操作できます。たとえば、myGraphという名前のプロパティ・グラフを作成するには、Java API (oracle.pg.rdbms.OraclePropertyGraph)またはPL/SQL API (MDSYS.OPG_APISパッケージ)のいずれかを使用できます。

PL/SQL APIの使用:

BEGIN
     opg_apis.create_pg(
           'myGraph',  
           dop => 4,             -- degree of parallelism
           num_hash_ptns => 8,   -- number of hash partitions used to store the graph
           tbs => 'USERS',       -- tablespace
           options => 'COMPRESS=T'
           );
END;
/

Java APIの使用:

  cfg = GraphConfigBuilder
            .forPropertyGraphRdbms()
            .setJdbcUrl("jdbc:oracle:thin:@127.0.0.1:1521:orcl")  
            .setUsername("<your_user_name>")
            .setPassword("<your_password>")  
            .setName("myGraph") 
            .setMaxNumConnections(8) 
            .setLoadEdgeLabel(false) 
            .build();

  OraclePropertyGraph opg = OraclePropertyGraph.getInstance(cfg);

プロパティ・グラフmyGraphがデータベースに確立された後、いくつかの表がユーザーのスキーマに自動的に作成され、接頭辞としてグラフ名が、接尾辞としてVT$またはGE$が付きます。たとえば、myGraphという名前のグラフの場合、表myGraphVT$が作成されて頂点およびそのプロパティ(K/Vペア)が格納され、表myGraphGE$が作成されてエッジおよびそのプロパティが格納されます。

簡単にするために、1つのグラフ名だけを許可し、大文字と小文字は区別されません。

追加の内部表がSS$、IT$、およびGT$の接尾辞で作成され、それぞれグラフのスナップショット、テキスト索引メタデータおよびグラフ・スケルトン(トポロジ構造)が格納されます。

表myGraphVT$およびmyGraphGE$の定義は次のとおりです。これらはSQLベースの分析およびSQLべースのプロパティ・グラフの問合せに重要です。VT$およびGE$表の両方で、VTS、VTEおよびFEは予約済の列で、列SLはセキュリティ・ラベル用、列K、T、V、VNおよびVTはグラフ要素のプロパティ(K/Vペア)に関する情報をまとめて格納します。VT$表で、VIDは頂点IDを格納するための長整数です。GE$表で、EID、SVIDおよびDVIDは、エッジID、出力(from)頂点IDおよび入力(to)頂点IDを格納するための長整数列です。

SQL> describe myGraphVT$
 Name                       Null?    Type
 ----------------------------------------- -------- ----------------------------
 VID                       NOT NULL NUMBER
 K                                  NVARCHAR2(3100)
 T                                  NUMBER(38)
 V                                  NVARCHAR2(15000)
 VN                                 NUMBER
 VT                                 TIMESTAMP(6) WITH TIME ZONE
 SL                                 NUMBER
 VTS                                DATE
 VTE                                DATE
 FE                                 NVARCHAR2(4000)


SQL> describe myGraphGE$
 Name                       Null?    Type
 ----------------------------------------- -------- ----------------------------
 EID                        NOT NULL NUMBER
 SVID                       NOT NULL NUMBER
 DVID                       NOT NULL NUMBER
 EL                                  NVARCHAR2(3100)
 K                                   NVARCHAR2(3100)
 T                                   NUMBER(38)
 V                                   NVARCHAR2(15000)
 VN                                  NUMBER
 VT                                  TIMESTAMP(6) WITH TIME ZONE
 SL                                  NUMBER
 VTS                                 DATE
 VTE                                 DATE
 FE                                  NVARCHAR2(4000)

プロパティ・グラフのスキーマ設計で、プロパティ値は数値データ型(long、int、double、float)の場合、VN列に格納され、タイムスタンプの場合、VT列に格納され、文字列、booleanおよびその他のシリアライズ可能データ型の場合、V列に格納されます。よりよいOracleテキスト問合せサポートのために、プロパティ値のリテラル表現は、データ型が数値またはタイムスタンプであっても、V列に格納されます。すべてのサポートされるデータ型を区別するために、整数IDはT列に格納されます。

VT$およびGE$表の両方のK列にはプロパティ・キーが格納されます。各エッジにはString型のラベルが必要で、そのラベルはGE$表のEL列に格納されます。

パフォーマンスとスケーラビリティのために、VT$とGE$の両方の表はIDに基づきハッシュ・パーティション化されており、パーティションの数はカスタマイズできます。パーティションの数は2の累乗(2、4、8、16など)にする必要があります。パーティションは"p1"から始まり連続的に名前が付けられるので、8つのパーティションで作成されたプロパティ・グラフは、"p1"、"p2"、...、"p8"というパーティションのセットになります。

国際文字をサポートするために、NVARCHAR列がVT$およびGE$表で使用されます。デフォルトのデータベース・キャラクタ・セットとして、UTF8をお薦めします。さらに、V列のサイズは15000で、32K VARCHAR (MAX_STRING_SIZE = EXTENDED)の有効化が必要です

トピック:

2.3.1 頂点(VT$)およびエッジ(GE$)表のデフォルトの索引

問合せのパフォーマンスのために、プロパティ・グラフの表にいくつかの索引がデフォルトで作成されます。索引名は、グラフ名を接頭辞に使うなど、表名と同じ表記規則に従います。たとえば、プロパティ・グラフmyGraphの場合、次のローカル索引が作成されます。

  • 一意の索引myGraphXQV$myGraphVT$(VID, K)

  • 一意の索引myGraphXQE$myGraphGE$(EID, K)

  • 索引myGraphXSE$myGraphGE$(SVID, DVID, EID, VN)

  • 索引myGraphXDE$myGraphGE$(DVID, SVID, EID, VN)

2.3.2 プロパティ・グラフ・スキーマの柔軟性

プロパティ・グラフ・スキーマの設計では、あらゆる種類のカタログや集中管理されたリポジトリは使用しません。各プロパティ・グラフは個別に格納され、ユーザーが選択したスキーマにより管理されます。ユーザーのスキーマは1つ以上のプロパティ・グラフを含むことができます。

この設計によりユーザーに大幅な柔軟性がもたらされます。次に例を示します。

  • 必要に応じて索引を追加できます。

  • 異なるプロパティ・グラフは基本表に対し異なる索引のセットまたは圧縮オプションを持つことができます。

  • 異なるプロパティ・グラフは異なる数のハッシュ・パーティションを持つことができます。

  • プロパティ・グラフのXSE$またはXDE$索引を削除することはできますが、整合性のために、一意の制約は保持する必要があります。

2.4 プロパティ・グラフの開始

次の手順に従い、プロパティ・グラフを開始します。

  1. プロパティ・グラフを初めて使用する際、ソフトウェアがインストールされ、稼働可能であることを確認します。
  2. Java APIで提供されるクラスを使用して、Javaプログラムを作成します。

2.5 プロパティ・グラフ・データ用のJava APIの使用

プロパティ・グラフの作成には、プロパティ・グラフおよびその中のオブジェクトを作成するためのJava APIの使用が含まれます。

トピック:

2.5.1 Java APIの概要

プロパティ・グラフで使用できるJava APIには、次のものが含まれます。

トピック:

2.5.1.1 Oracle Spatial and Graphプロパティ・グラフJava API

Oracle Spatial and Graphプロパティ・グラフ・サポートは、Oracle Database用のデータベース固有のAPIを提供します。データ・アクセス・レイヤーAPI (oracle.pg.*)は、TinkerPop Blueprints API、テキスト検索、およびOracle Databaseに格納されているプロパティ・グラフの索引付けを実装します。

Oracle Spatial and Graph APIを使用するには、次のクラスをJavaプログラムにインポートします。

import oracle.pg.common.*;
import oracle.pg.text.*;
import oracle.pg.rdbms.*;
import oracle.pgx.config.*;
import oracle.pgx.common.types.*;

TinkerPop Blueprints Java APIも含めます。

2.5.1.2 TinkerPop Blueprints Java API

TinkerPop Blueprintsはプロパティ・グラフ・データ・モデルをサポートします。このAPIは、主にSpatial and Graphプロパティ・グラフのデータ・アクセス・レイヤーJava APIを介して使用する、グラフ操作のためのユーティリティを提供します。

Blueprints APIを使用するには、次のクラスをJavaプログラムにインポートします。

import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.Edge;
2.5.1.3 Oracle Databaseプロパティ・グラフJava API

Oracle Databaseプロパティ・グラフJava APIにより、Oracle Databaseに格納されたプロパティ・グラフを作成および移入できます。

これらのJava APIを使用するには、クラスをJavaプログラムにインポートします。次に例を示します。

import oracle.pg.rdbms.*; 
import java.sql.*;

2.5.2 グラフ・データのパラレル・ロード

グラフ・データのパラレル・ロードを実行するために、Java APIが提供されています。

Oracle Spatial and Graphはグラフ・データのOracle Databaseへのロードをサポートします。グラフ・データは次の方法を使用してプロパティ・グラフにロードされます。

  • 頂点およびエッジは、graph.addVertex(Object id)/graph.addEdge(Object id) APIを使用して、増分を追加できます。

  • グラフ・データは、OraclePropertyGraphDataLoader APIを使用して、Oracleフラット・ファイル形式のファイルからパラレルにロードできます。

  • GraphML、GMLまたはGraphSONのプロパティ・グラフは、GMLReaderGraphMLReaderおよびGraphSONReaderをそれぞれ使用してロードできます。

このトピックでは、Oracle定義のフラット・ファイル形式でのプロパティ・グラフのパラレル・ロードに焦点を当てています。

パラレル・データ・ロードは、頂点(またはエッジ)入力ストリームが複数のチャンクに分割され、Oracle Databaseにパラレルにロードされる場合の、データのロードへの最適化されたソリューションを提供します。この操作には、2つの主なオーバーラップ・フェーズが含まれます。

  • 分割。頂点およびエッジ入力ストリームが複数のチャンクに分割され、一時入力ストリームに保存されます。チャックの数は指定した並列度によって決定されます

  • グラフのロード。各チャンクに対し、頂点(またはエッジ)に関する情報を処理し、データをプロパティ・グラフ表にロードするために、ローダー・スレッドが作成されます。

OraclePropertyGraphDataLoaderはいくつかの異なるオプションを使用してパラレル・データ・ロードをサポートします。

トピック:

2.5.2.1 JDBCベースのデータのロード

JDBCベースのデータのロードでは、Java Database Connectivity (JDBC) APIを使用して、グラフ・データをOracle Databaseにロードします。このオプションでは、指定した入力ストリームの頂点(またはエッジ)は、スプリッタ・スレッドにより複数のチャンクの間に広がります。各チャンクは、JDBCバッチを使用して、チャンク内のすべての要素を一時作業表に挿入する、異なるローダー・スレッドにより処理されます。使用されるスプリッタおよびローダー・スレッドの数は、ユーザーが指定した並列度(DOP)によって決定されます。

すべてのグラフ・データが一時作業表にロードされた後、一時作業表に格納されたすべてのデータはプロパティ・グラフVT$およびGE$表にロードされます。

次の例は、並列度48で、JDBCベースのパラレル・データ・ロードを使用し、グラフ・データをOracle定義のフラット・ファイル形式の頂点およびエッジ・ファイルからロードします。

    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    opgdl = OraclePropertyGraphDataLoader.getInstance(); 
    opgdl.loadData(opg, szOPVFile, szOPEFile, 48 /* DOP */, 1000 /* batch size */, true /* rebuild index flag */, "pddl=t,pdml=t" /* options */); 
);

データのロード操作のパフォーマンスを最適化するために、JDBCベースのデータのロードを呼び出すときに、フラグやヒントのセットを指定できます。これらのヒントとしては次のものがあります。

  • DOP: データのロード時に使用する並列度。このパラメータは、データをプロパティ・グラフVT$およびGE$表にロードするときに使用するローダー・スレッドの数と同様に、ファイルの分割時に生成するチャンクの数を決定します。

  • バッチ・サイズ:バッチ・モードでOracleのupdate文で使用するバッチ・サイズを指定する整数。JDBCベースのデータのロードに使用されるデフォルトのバッチ・サイズは1000です。

  • 索引の再作成: このフラグがtrueに設定されている場合、データ・ローダーは、データがロードされるプロパティ・グラフで定義されたすべての索引および制約を無効にします。すべてのデータがプロパティ・グラフにロードされた後、すべての索引および制約は再作成されます。

  • ロード・オプション: データのロード操作を最適化するための1つのオプション(またはカンマで区切った複数のオプション)。これらのオプションとしては次のものがあります。

    • NO_DUP=T: 入力データに無効な重複がないことを前提にしています。有効なプロパティ・グラフでは、各頂点(エッジ)は指定したプロパティ・キーに対し最大で1つの値を持つことができます。無効なプロパティ・グラフでは、各頂点(エッジ)は特定のキーに対し2つ以上の値を持つことがあります。例として、頂点vには2つのキー/値ペア、name/"John"およびname/"Johnny"があり、同じキーを共有しています。

    • PDML=T: データ・ローダーで使用されるデータベース・セッションでのDML操作のパラレル実行を有効にします。このヒントは、長時間実行するバッチ・ジョブのパフォーマンスを向上するのに使用されます。

    • PDDL=T: データ・ローダーで使用されるデータベース・セッションでのDDL操作のパラレル実行を有効にします。このヒントは、長時間実行するバッチ・ジョブのパフォーマンスを向上するのに使用されます。

    • KEEP_WORK_TABS=T: データのロードが完了した後の作業表のクリーニングおよび削除をスキップします。これはデバッグでの使用専用です。

    • KEEP_TMP_FILES=T: データのロードが完了した後の一時スプリッタ・ファイルの削除をスキップします。これはデバッグ専用です。

  • スプリッタ・フラグ: 分割フェーズで使用されるファイルまたはストリームのタイプを定義し、グラフのロード・フェーズで使用されるデータ・チャンクを生成する整数値です。一時ファイルは通常のファイル(0)、名前付きパイプ(1)、またはパイプされたストリーム(2)として作成できます。デフォルトでは、JDBCベースのデータのロードでは、次のものが使用されます

    中間データ・チャンクを処理するためのパイプされたストリーム。パイプされたストリームはJDBCベースのローダー専用です。これは完全にインメモリーで効率的であり、オペレーティング・システムにファイルを作成する必要がありません。

    名前付きパイプはローカルのオペレーティング・システムで空のファイルのように見えるのに対し、通常のファイルはローカルのオペレーティング・システムの領域を消費します。すべてのオペレーティング・システムが名前付きパイプをサポートするわけではないことに注意してください。

  • 分割ファイルの接頭辞: 分割フェーズがグラフのロード用のデータ・チャンクを生成しているときに、作成される一時ファイルまたはパイプに使用される接頭辞です。デフォルトで、接頭辞“OPG_Chunk”は通常のファイルに使用され、“OPG_Pipe”は名前付きパイプに使用されます。

  • 表領域: すべての一時作業表が作成される表領域の名前です。

サブトピック:

  • 複数のファイルを伴うJDBCベースのデータのロード

  • パーティションを伴うJDBCベースのデータのロード

  • ファインチューニングを使用したJDBCベースのパラレル・データ・ロード

複数のファイルを伴うJDBCベースのデータのロード

JDBCベースのデータのロードは、複数ファイルからの頂点およびエッジ、またはデータベースへの入力ストリームのロードもサポートします。次のコード・フラグメントは、パラレル・データ・ロードAPIを使用し、複数の頂点およびエッジ・ファイルをロードします。この例では、2つの文字列配列szOPVFilesおよびszOPEFilesが入力ファイルの保持に使用されます。

    String[] szOPVFiles = new String[] {"../../data/connections-p1.opv", 
                                        "../../data/connections-p2.opv"}; 
    String[] szOPEFiles = new String[] {"../../data/connections-p1.ope",                          
                                        "../../data/connections-p2.ope"}; 
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    opgdl = OraclePropertyGraphDataLoader.getInstance(); 
    opgdl.loadData(opg, szOPVFiles, szOPEFiles, 48 /* DOP */,
                   1000 /* batch size */, 
                   true /* rebuild index flag */, 
                   "pddl=t,pdml=t" /* options */); 

パーティションを伴うJDBCベースのデータのロード

数千から数十万の要素からのグラフ・データの処理中に、JDBCベースのデータのロードAPIにより、論理パーティション化を使用して、Oracleフラット・ファイル形式のグラフ・データをOracle Databaseにロードできます。

各パーティションは、グラフ・データ・ファイルの頂点(またはエッジ)のサブセットを表します。ファイルのサイズはほぼ、パーティションの数で分割されたファイル内の異なる要素IDの数です。各パーティションは[0, Number of partitions – 1]の範囲の、整数IDで識別されます。

パーティションでパラレル・データ・ロードを使用するには、使用する論理パーティションの合計数と、パーティション・オフセット(開始ID)を、loadData APIで使用される基本パラメータに加えて、指定する必要があります。グラフ・データ・ファイルまたは入力ストリームをデータベースに完全にロードするには、定義したパーティションの数だけ、データ・ロード操作を実行する必要があります。たとえば、2つのパーティションを使用してファイルからグラフ・データをロードするには、オフセット0および1を使用した2つのデータ・ロードAPIコールが必要です。データ・ローダーへの各コールは複数のスレッド、または1つのシステムまたは複数のシステムでの別個のJavaクライアントを使用して処理できます。

この方法は、1つの頂点ファイル(または入力ストリーム)および1つのエッジ・ファイル(または入力ストリーム)で使用されることを意図したものであることに注意してください。さらに、このオプションでは頂点およびエッジでの索引および制約を無効にすることが必要です。これらの索引および制約は、すべてのパーティションがロードされた後で再作成される必要があります。

次の例は、2つのパーティションを使用してグラフ・データをロードします。各パーティションは1つのJavaプロセスDataLoaderWorkerによってロードされます。複数のワーカーを調整するために、DataLoaderCoordinatorという名前のコーディネータ・プロセスが使用されます。この例では、次の操作を行います

  1. すべての索引および制約を無効にし、

  2. 一時作業表loaderProgressを作成します。これはデータのロード・プロセス(つまり、何人のワーカーが作業を終了したか)を記録するものです。作業表が作成されると、すべてのDataLoaderWorkerプロセスがデータのロードを開始します。

  3. 1ずつプロセスを増分します。

  4. すべてのDataLoaderWorkerプロセスが実行されるまで、プロセスのポーリングを継続します(DataLoaderCoordinatorプロセスを使用して)。

  5. すべての索引および制約を再作成します。

注意: DataLoaderWorkerでは、フラグSKIP_INDEXtrueに、フラグrebuildIndxfalseに設定される必要があります。

// start DataLoaderCoordinator, set dop = 8 and number of partitions = 2
java DataLoaderCoordinator  jdbcUrl  user password pg 8 2
// start the first DataLoaderWorker, set dop = 8, number of partitions = 2, partition offset = 0
java DataLoaderWorker jdbcUrl user password pg  8 2 0
// start the first DataLoaderWorker, set dop = 8, number of partitions = 2, partition offset = 1
java DataLoaderWorker jdbcUrl user password pg  8 2 1

DataLoaderCoordinatorが最初にすべての索引および制約を無効にします。次に、loaderProgressという名前の表を作成し、列progress = 0の1行を挿入します。

public class DataLoaderCoordinator {
        public static void main(String[] szArgs) {
          String jdbcUrl = szArgs[0];
          String user = szArgs[1];
          String password = szArgs[2];
          String graphName = szArgs[3];
          int dop = Integer.parseInt(szArgs[4]);
          int numLoaders = Integer.parseInt(szArgs[5]);

          Oracle oracle = null;
          OraclePropertyGraph opg = null;
          try {
            oracle = new Oracle(jdbcUrl, user, password);
            OraclePropertyGraphUtils.dropPropertyGraph(oracle, graphName);
            opg = OraclePropertyGraph.getInstance(oracle, graphName);

            List<String> vIndices = opg.disableVertexTableIndices();
            List<String> vConstraints = opg.disableVertexTableConstraints();
            List<String> eIndices = opg.disableEdgeTableIndices();
            List<String> eConstraints = opg.disableEdgeTableConstraints();

            String szStmt = null;
            try {
              szStmt = "drop table loaderProgress";
              opg.getOracle().executeUpdate(szStmt);
            }
            catch (SQLException ex) {
              if (ex.getErrorCode() == 942) {
                // table does not exist. ignore
              }
              else {
                throw new OraclePropertyGraphException(ex);
              }
            }

            szStmt = "create table loaderProgress (progress integer)";
            opg.getOracle().executeUpdate(szStmt);
            szStmt = "insert into loaderProgress (progress) values (0)";
            opg.getOracle().executeUpdate(szStmt);
            opg.getOracle().getConnection().commit();
            while (true) {
              if (checkLoaderProgress(oracle) == numLoaders) {
                break;
              } else {
                Thread.sleep(1000);
              }
            }

            opg.rebuildVertexTableIndices(vIndices, dop, null);
            opg.rebuildVertexTableConstraints(vConstraints, dop, null);
            opg.rebuildEdgeTableIndices(eIndices, dop, null);
            opg.rebuildEdgeTableConstraints(eConstraints, dop, null);
          }
          catch (IOException ex) {
            throw new OraclePropertyGraphException(ex);
          }
          catch (SQLException ex) {
            throw new OraclePropertyGraphException(ex);
          }
          catch (InterruptedException ex) {
            throw new OraclePropertyGraphException(ex);
          }
          catch (Exception ex) {
            throw new OraclePropertyGraphException(ex);
          }
          finally {
            try {
              if (opg != null) {
                opg.shutdown();
              }
              if (oracle != null) {
                oracle.dispose();
              }
            }
            catch (Throwable t) {
              System.out.println(t);
            }
          }

        }

        private static int checkLoaderProgress(Oracle oracle) {
          int result = 0;
          ResultSet rs = null;

          try {
            String szStmt = "select progress from loaderProgress";
            rs = oracle.executeQuery(szStmt);
            if (rs.next()) {
              result =  rs.getInt(1);
            }

          }
          catch (Exception ex) {
            throw new OraclePropertyGraphException(ex);
          }
          finally {
            try {
              if (rs != null) {
                rs.close();
              }
	    }
            catch (Throwable t) {
              System.out.println(t);
            }
          }
          return result;
        }
}
     
public class DataLoaderWorker {

        public static void main(String[] szArgs) {
          String jdbcUrl = szArgs[0];
          String user = szArgs[1];
          String password = szArgs[2];
          String graphName = szArgs[3];
          int dop = Integer.parseInt(szArgs[4]);
          int numLoaders = Integer.parseInt(szArgs[5]);
          int offset = Integer.parseInt(szArgs[6]);

          Oracle oracle = null;
          OraclePropertyGraph opg = null;

          try {
            oracle = new Oracle(jdbcUrl, user, password);
            opg = OraclePropertyGraph.getInstance(oracle, graphName, 8, dop, null/*tbs*/, ",SKIP_INDEX=T,");
            OraclePropertyGraphDataLoader opgdal = OraclePropertyGraphDataLoader.getInstance();

            while (true) {
              if (checkLoaderProgress(oracle) == 1) {
                break;
              } else {
                Thread.sleep(1000);
              }
            }

            String opvFile = "../../../data/connections.opv";
            String opeFile = "../../../data/connections.ope";
            opgdal.loadData(opg, opvFile, opeFile, dop, numLoaders, offset, 1000, false, null, "pddl=t,pdml=t");

            updateLoaderProgress(oracle);
          }
          catch (SQLException ex) {
            throw new OraclePropertyGraphException(ex);
          }
          catch (InterruptedException ex) {
            throw new OraclePropertyGraphException(ex);
          }
          finally {
            try {
              if (opg != null) {
                opg.shutdown();
              }
              if (oracle != null) {
                oracle.dispose();
              }
            }
            catch (Throwable t) {
              System.out.println(t);
            }
          }
        }

        private static int checkLoaderProgress(Oracle oracle) {
          int result = 0;
          ResultSet rs = null;

          try {
            String szStmt = "select count(*) from loaderProgress";
            rs = oracle.executeQuery(szStmt);
            if (rs.next()) {
              result = rs.getInt(1);
            }
          }
          catch (SQLException ex) {
            if (ex.getErrorCode() == 942) {
              // table does not exist. ignore
            } else {
              throw new OraclePropertyGraphException(ex);
            }
          }
          finally {
            try {
              if (rs != null) {
                rs.close();
              }
            }
            catch (Throwable t) {
              System.out.println(t);
            }
          }
          return result;
        }

        private static void updateLoaderProgress(Oracle oracle) {
          ResultSet rs = null;

          try {
            String szStmt = "update loaderProgress set progress = progress + 1";
            oracle.executeUpdate(szStmt);
            oracle.getConnection().commit();
          }
          catch (Exception ex) {
            throw new OraclePropertyGraphException(ex);
          }
          finally {
            try {
              if (rs != null) {
                rs.close();
              }
            }
            catch (Throwable t) {
              System.out.println(t);
            }
          }
        }
}

ファインチューニングを使用したJDBCベースのパラレル・データ・ロード

JDBCベースのデータ・ロードは、要素をプロパティ・グラフ・インスタンスへロードするときに使用されるIDオフセットと同様に、ロードされる行からのデータのサブセットのファインチューニングをサポートします。ファイルから読み取る行の最大数と、頂点およびエッジの両方のオフセット行番号(開始位置)を指定して、ファイルからロードするデータのサブセットを指定できます。この方法では、行の最大数が読み取られるまで、データはオフセット行番号からロードされます。最大行数が-1の場合、ロード・プロセスはファイルの終わりまでデータをスキャンします。

複数のグラフ・データ・ファイルにはいくつかのID衝突または重複が含まれることがあるので、JDBCベースのデータ・ロードでは、頂点およびエッジのIDオフセットを定義できます。この方法では、ロードされた各頂点のIDは、元の頂点IDおよび指定した頂点IDのオフセットの合計になります。同様に、ロードされた各エッジのIDは、元のエッジIDおよび指定したエッジIDのオフセットの合計から生成されます。頂点およびエッジ・ファイルは相関関係がある必要があります。ロードされたエッジに対する入出力頂点IDは指定した頂点IDオフセットに対して変更されるからです。この操作は、1つの論理パーティションを使用したデータのロードでのみサポートされます。

次のコード・フラグメントは、指定したグラフ・データ・ファイルから最初の100個の頂点およびエッジ行をロードします。この例では、IDオフセット0が使用され、これはID調整が行われないことを示します。

    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 
    // Run the data loading using fine tuning 
    long lVertexOffsetlines = 0; 
    long lEdgeOffsetlines = 0; 
    long lVertexMaxlines = 100; 
    long lEdgeMaxlines = 100;
    long lVIDOffset = 0;
    long lEIDOffset = 0;
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
    
    opgdl.loadData(opg, szOPVFile, szOPEFile, 
                   lVertexOffsetlines /* offset of lines to start loading from 
              partition, default 0 */, 
                   lEdgeOffsetlines /* offset of lines to start loading from 
                   partition, default 0 */, 
      lVertexMaxlines /* maximum number of lines to start loading from 
                        partition, default -1 (all lines in partition) */, 
      lEdgeMaxlines /* maximum number of lines to start loading from 
                       partition, default -1 (all lines in partition) */, 
      lVIDOffset /* vertex ID offset: the vertex ID will be original 
                    vertex ID + offset, default 0 */, 
      lEIDOffset /* edge ID offset: the edge ID will be original edge ID 
                    + offset, default 0 */, 
      4 /* DOP */, 
      1 /* Total number of partitions, default 1 */, 
      0 /* Partition to load: from 0 to totalPartitions - 1, default 0 */, 
      OraclePropertyGraphDataLoader.PIPEDSTREAM /* splitter flag */, 
      "chunkPrefix" /* prefix: the prefix used to generate split chunks 
                       for regular files or named pipes */, 
      1000 /* batch size: batch size of Oracle update in batching mode. 
              Default value is 1000 */, 
      true /* rebuild index */, 
      null /* table space name*/,
      "pddl=t,pdml=t" /* options: enable parallel DDL and DML */);
2.5.2.2 外部表ベースのデータのロード

外部表ベースのデータのロードでは、グラフ・データをOracle Databaseにロードするのに、外部表を使用します。外部表のロードにより、データベース内の通常のリレーショナル表にあるかのように、ユーザーは外部ソースのデータにアクセスできます。この場合、指定した入力ストリームの頂点(またはエッジ)は、スプリッタ・スレッドにより複数のチャンクの間に広がります。各チャンクは、チャンク内のすべての要素をOracle Databaseに渡す役割を持つ、異なるローダー・スレッドにより処理されます。使用されるスプリッタおよびローダー・スレッドの数は、ユーザーが指定した並列度(DOP)によって決定されます。

外部表がデータ・ロード・ロジックにより自動的に作成された後、ローダーは外部表から読み取り、すべてのデータをプロパティ・グラフ・スキーマ表(VT$およびGE$)表にロードします。

外部表ベースのデータのロードには、外部表により読み出されるファイルが格納されるディレクトリ・オブジェクトが必要です。このディレクトリは、SQL*Plus環境で次のスクリプトを実行して作成できます。

create or replace directory tmp_dir as '/tmppath/';
grant read, write on directory tmp_dir to public;

次のコード・フラグメントは、並列度が48で、外部表ベースのパラレル・データ・ロードを使用し、グラフ・データをOracleフラット・ファイル形式の頂点およびエッジ・ファイルからロードします。

    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 
    String szExtDir = "tmp_dir";
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    opgdl = OraclePropertyGraphDataLoader.getInstance(); 
    opgdl.loadDataWithExtTab(opg, szOPVFile, szOPEFile, 48 /*DOP*/, 
                             true /*named pipe flag: setting the flag to true will use 
                                   named pipe based splitting; otherwise, regular file 
                                   based splitting would be used*/, 
                             szExtDir /* database directory object */, 
                             true /*rebuild index */, 
                             "pddl=t,pdml=t,NO_DUP=T" /*options */);

データのロード操作のパフォーマンスを最適化するために、外部表ベースのデータのロードを呼び出すときに、フラグやヒントのセットを指定できます。これらのヒントとしては次のものがあります。

  • DOP: データのロード時に使用する並列度。このパラメータは、データをプロパティ・グラフVT$およびGE$表にロードするときに使用するローダー・スレッドの数と同様に、ファイルの分割時に生成するチャンクの数を決定します。

  • 索引の再作成: このフラグがtrueに設定されている場合、データ・ローダーは、データがロードされるプロパティ・グラフで定義されたすべての索引および制約を無効にします。すべてのデータがプロパティ・グラフにロードされた後、すべての索引および制約は再作成されます。

  • ロード・オプション: データのロード操作を最適化するための1つのオプション(またはカンマで区切った複数のオプション)。これらのオプションとしては次のものがあります。

    • NO_DUP=T: 重複キー/値ペアに対する検証が実行されないため、データをプロパティ・グラフ表にロードするのにより速い方法を選択します。

    • PDML=T: データ・ローダーで使用されるデータベース・セッションでのDML操作のパラレル実行を有効にします。このヒントは、長時間実行するバッチ・ジョブのパフォーマンスを向上するのに使用されます。

    • PDDL=T: データ・ローダーで使用されるデータベース・セッションでのDDL操作のパラレル実行を有効にします。このヒントは、長時間実行するバッチ・ジョブのパフォーマンスを向上するのに使用されます。

    • KEEP_WORK_TABS=T: データのロードが完了した後の作業表のクリーニングおよび削除をスキップします。これはデバッグでの使用専用です。

    • KEEP_TMP_FILES=T: データのロードが完了した後の一時スプリッタ・ファイルの削除をスキップします。これはデバッグでの使用専用です。

  • スプリッタ・フラグ: 分割フェーズで使用されるファイルまたはストリームのタイプを定義し、グラフのロード・フェーズで使用されるデータ・チャンクを生成する整数値です。一時ファイルは通常のファイル(0)または名前付きパイプ(1)として作成できます。

    デフォルトで、外部表ベースのデータのロードでは、通常のファイルを使用して、データ・チャンク用の一時ファイルを処理します。名前付きパイプはそれをサポートするオペレーティング・システムでのみ使用できます。一般的に、通常のファイルをDBFSと一緒に使用するのはよい方法です。

  • 分割ファイルの接頭辞: 分割フェーズがグラフのロード用のデータ・チャンクを生成しているときに、作成される一時ファイルまたはパイプに使用される接頭辞です。デフォルトで、接頭辞“Chunk”は通常のファイルに使用され、“Pipe”は名前付きパイプに使用されます。

  • 表領域: すべての一時作業表が作成される表領域の名前です。

JDBCベースのデータのロードと同様に、外部表ベースのデータのロードは、1つのファイル、複数のファイル、パーティション、およびファインチューニングを使用して、パラレル・データ・ロードをサポートします。

サブトピック:

  • 複数のファイルを使用した外部表ベースのデータのロード

  • パーティションを使用した外部表ベースのデータのロード

  • ファインチューニングを使用した外部表ベースのパラレル・データ・ロード

複数のファイルを使用した外部表ベースのデータのロード

外部表ベースのデータのロードは、複数ファイルからの頂点およびエッジ、またはデータベースへの入力ストリームのロードもサポートします。次のコード・フラグメントは、パラレル・データ・ロードAPIを使用し、複数の頂点およびエッジ・ファイルをロードします。この例では、2つの文字列配列szOPVFilesおよびszOPEFilesが入力ファイルの保持に使用されます。

    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 
    String szExtDir = "tmp_dir";
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    opgdl = OraclePropertyGraphDataLoader.getInstance(); 
    opgdl.loadDataWithExtTab(opg, szOPVFile, szOPEFile, 48 /* DOP */, 
                             true /* named pipe flag */, 
                             szExtDir /* database directory object */, 
                             true /* rebuild index flag */, 
                             "pddl=t,pdml=t" /* options */);

パーティションを使用した外部表ベースのデータのロード

非常に大きいプロパティ・グラフの処理中に、外部表ベースのデータのロードAPIにより、論理パーティション化を使用して、Oracleフラット・ファイル形式のグラフ・データをOracle Databaseにロードできます。各パーティションは、グラフ・データ・ファイルの頂点(またはエッジ)のサブセットを表します。ファイルのサイズはほぼ、パーティションの数で分割されたファイル内の異なる要素IDの数です。各パーティションは[0, Number of partitions – 1]の範囲の、整数IDで識別されます。

パーティションでパラレル・データ・ロードを使用するには、使用するパーティションの合計数と、パーティション・オフセットを、loadDataWithExtTab APIで使用される基本パラメータに加えて、指定する必要があります。グラフ・データ・ファイルまたは入力ストリームをデータベースに完全にロードするには、定義したパーティションの数だけ、データ・ロード操作を実行する必要があります。たとえば、2つのパーティションを使用してファイルからグラフ・データをロードするには、オフセット0および1を使用した2つのデータ・ロードAPIコールが必要です。データ・ローダーへの各コールは複数のスレッド、または1つのシステムまたは複数のシステムでの別個のJavaクライアントを使用して処理できます。

この方法は、1つの頂点ファイル(または入力ストリーム)および1つのエッジ・ファイル(または入力ストリーム)で使用されることを意図したものであることに注意してください。さらに、このオプションでは頂点およびエッジでの索引および制約を無効にすることが必要です。これらの索引および制約は、すべてのパーティションがロードされた後で再作成される必要があります。

パーティションを使用したJDBCベースのデータのロードの例は、パーティションを使用した外部表ベースのロードとして動作するように、容易に移行できます。必要な変更は、API loadData()loadDataWithExtTab()で置き換え、データベース・ディレクトリ・オブジェクトなどの追加の入力パラメータを指定するだけです。

ファインチューニングを使用した外部表ベースのパラレル・データ・ロード

外部表ベースのデータのロードも、要素をプロパティ・グラフ・インスタンスへロードするときに使用されるIDオフセットと同様に、ロードされる行からのデータのサブセットのファインチューニングをサポートします。ファイルから読み取る行の最大数と、頂点およびエッジの両方のオフセット行番号を指定して、ファイルからロードするデータのサブセットを指定できます。この方法では、行の最大数が読み取られるまで、データはオフセット行番号からロードされます。最大行数が-1の場合、ロード・プロセスはファイルの終わりまでデータをスキャンします。

グラフ・データ・ファイルにはいくつかのID衝突が含まれることがあるので、外部表ベースのデータのロードでは、頂点およびエッジのIDオフセットを定義できます。この方法では、ロードされた各頂点のIDは、元の頂点IDおよび指定した頂点IDのオフセットの合計から取得されます。同様に、ロードされた各エッジのIDは、元のエッジIDおよび指定したエッジIDのオフセットの合計から生成されます。頂点およびエッジ・ファイルは相関関係がある必要があります。ロードされたエッジに対する入出力頂点IDは指定した頂点IDオフセットに対して変更されるからです。この操作は、1つのパーティションを使用したデータのロードでのみサポートされます。

次のコード・フラグメントは、指定したグラフ・データ・ファイルから最初の100個の頂点およびエッジをロードします。この例では、IDオフセットは指定しません。

    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 

    // Run the data loading using fine tuning 
    long lVertexOffsetlines = 0; 
    long lEdgeOffsetlines = 0; 
    long lVertexMaxlines = 100; 
    long lEdgeMaxlines = 100;
    long lVIDOffset = 0;
    long lEIDOffset = 0;
    String szExtDir = "tmp_dir";

    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
    
    opgdl.loadDataWithExtTab(opg, szOPVFile, szOPEFile, 
                             lVertexOffsetlines /* offset of lines to start loading 
                                                   from partition, default 0 */, 
                             lEdgeOffsetlines /* offset of lines to start loading from 
                                                 partition, default 0 */, 
                             lVertexMaxlines /* maximum number of lines to start 
                                                loading from partition, default -1 
                                               (all lines in partition) */, 
                             lEdgeMaxlines /* maximum number of lines to start loading 
                                              from partition, default -1 (all lines in 
                                              partition) */, 
                             lVIDOffset /* vertex ID offset: the vertex ID will be 
                                          original vertex ID + offset, default 0 */, 
                             lEIDOffset /* edge ID offset: the edge ID will be 
                                          original edge ID + offset, default 0 */, 
                             4 /* DOP */, 
                             1 /* Total number of partitions, default 1 */, 
                             0 /* Partition to load (from 0 to totalPartitions - 1, 
                                  default 0) */, 
                             OraclePropertyGraphDataLoader.NAMEDPIPE 
                             /* splitter flag */, 
                             "chunkPrefix" /* prefix */, 
                             szExtDir /* database directory object */, 
                             true /* rebuild index flag */, 
                             "pddl=t,pdml=t" /* options */);
2.5.2.3 SQL*Loaderベースのデータのロード

SQL*Loaderベースのデータのロードでは、グラフ・データをOracle Databaseにロードするのに、Oracle SQL*Loaderを使用します。SQL*Loaderにより、外部ファイルからOracle Databaseの表にデータがロードされます。この場合、指定した入力ストリームの頂点(またはエッジ)は、スプリッタ・スレッドにより複数のチャンクの間に広がります。各チャンクは、SQL*Loaderを使用して、チャンク内のすべての要素を一時作業表に挿入する、異なるローダー・スレッドにより処理されます。使用されるスプリッタおよびローダー・スレッドの数は、ユーザーが指定した並列度(DOP)によって決定されます。

すべてのグラフ・データが一時作業表にロードされた後、グラフ・ローダーは一時作業表に格納されたすべてのデータをプロパティ・グラフVT$およびGE$表にロードします。

次のコード・フラグメントは、並列度48でSQLベースのパラレル・データ・ロードを使用し、Oracleフラット・ファイル形式の頂点およびエッジ・ファイルからグラフ・データをロードします。APIを使用するには、SQL*Loaderへのパスを指定する必要があります。

    String szUser = "username";
    String szPassword = "password";
    String szDbId = "db12c"; /*service name of the database*/
    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 
    String szSQLLoaderPath = "<YOUR_ORACLE_HOME>/bin/sqlldr";
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 

    opgdl = OraclePropertyGraphDataLoader.getInstance(); 
    opgdl.loadDataWithSqlLdr(opg, szUser, szPassword, szDbId, 
                             szOPVFile, szOPEFile, 
                             48 /* DOP */, 
                             true /*named pipe flag */, 
                             szSQLLoaderPath /* SQL*Loader path: the path to 
                                                bin/sqlldr*/, 
                             true /*rebuild index */, 
                             "pddl=t,pdml=t" /* options */);

JDBCベースのデータのロードと同様に、SQL*Loaderベースのデータのロードは、1つのファイル、複数のファイル、パーティション、およびファインチューニングを使用して、パラレル・データ・ロードをサポートします。

サブトピック:

  • 複数のファイルを使用したSQL*Loaderベースのデータのロード

  • パーティションを使用したSQL*Loaderベースのデータのロード

  • ファインチューニングを使用したSQL*Loaderベースのパラレル・データ・ロード

複数のファイルを使用したSQL*Loaderベースのデータのロード

SQL*Loaderベースのデータのロードは、複数ファイルからの頂点およびエッジ、またはデータベースへの入力ストリームのロードをサポートします。次のコード・フラグメントは、パラレル・データ・ロードAPIを使用し、複数の頂点およびエッジ・ファイルをロードします。この例では、2つの文字列配列szOPVFilesおよびszOPEFilesが入力ファイルの保持に使用されます。

    String szUser = "username";
    String szPassword = "password";
    String szDbId = "db12c"; /*service name of the database*/
    String[] szOPVFiles = new String[] {"../../data/connections-p1.opv", 
                                        "../../data/connections-p2.opv"}; 
    String[] szOPEFiles = new String[] {"../../data/connections-p1.ope", 
                                        "../../data/connections-p2.ope"}; 
    String szSQLLoaderPath = "../../../dbhome_1/bin/sqlldr";
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 

    opgdl = OraclePropertyGraphDataLoader.getInstance(); 
    opgdl. loadDataWithSqlLdr (opg, szUser, szPassword, szDbId, 
                               szOPVFiles, szOPEFiles, 
                               48 /* DOP */, 
                               true /* named pipe flag */, 
                               szSQLLoaderPath /* SQL*Loader path */, 
                               true /* rebuild index flag */, 
                               "pddl=t,pdml=t" /* options */);

パーティションを使用したSQL*Loaderベースのデータのロード

大きいプロパティ・グラフの処理中に、SQL*LoaderベースのデータのロードAPIにより、論理パーティション化を使用して、Oracleフラット・ファイル形式のグラフ・データをOracle Databaseにロードできます。各パーティションは、グラフ・データ・ファイルの頂点(またはエッジ)のサブセットを表します。ファイルのサイズはほぼ、パーティションの数で分割されたファイル内の異なる要素IDの数です。各パーティションは[0, Number of partitions – 1]の範囲の、整数IDで識別されます。

パーティションでパラレル・データ・ロードを使用するには、使用するパーティションの合計数と、パーティション・オフセットを、loadDataWithSqlLdr APIで使用される基本パラメータに加えて、指定する必要があります。グラフ・データ・ファイルまたは入力ストリームをデータベースに完全にロードするには、定義したパーティションの数だけ、データ・ロード操作を実行する必要があります。たとえば、2つのパーティションを使用してファイルからグラフ・データをロードするには、オフセット0および1を使用した2つのデータ・ロードAPIコールが必要です。データ・ローダーへの各コールは複数のスレッド、または1つのシステムまたは複数のシステムでの別個のJavaクライアントを使用して処理できます。

この方法は、1つの頂点ファイル(または入力ストリーム)および1つのエッジ・ファイル(または入力ストリーム)で使用されることを意図したものであることに注意してください。さらに、このオプションでは頂点およびエッジでの索引および制約を無効にすることが必要です。これらの索引および制約は、すべてのパーティションがロードされた後で再作成される必要があります。

パーティションを使用したJDBCベースのデータのロードの例は、パーティションを使用したSQL*Loaderベースのロードとして動作するように、容易に移行できます。必要な変更は、API loadData()loadDataWithSqlLdr()で置き換え、SQL*Loaderの場所などの追加の入力パラメータを指定するだけです。

ファインチューニングを使用したSQL*Loaderベースのパラレル・データ・ロード

SQL Loaderベースのデータ・ロードは、要素をプロパティ・グラフ・インスタンスへロードするときに使用されるIDオフセットと同様に、ロードされる行からのデータのサブセットの微調整をサポートします。ファイルから読み取る行の最大数と、頂点およびエッジの両方のオフセット行番号を指定して、ファイルからロードするデータのサブセットを指定できます。この方法では、行の最大数が読み取られるまで、データはオフセット行番号からロードされます。最大行数が-1の場合、ロード・プロセスはファイルの終わりまでデータをスキャンします。

グラフ・データ・ファイルにはいくつかのID衝突が含まれることがあるので、SQL Loaderベースのデータのロードでは、頂点およびエッジのIDオフセットを定義できます。この方法では、ロードされた各頂点のIDは、元の頂点IDおよび指定した頂点IDのオフセットの合計から取得されます。同様に、ロードされた各エッジのIDは、元のエッジIDおよび指定したエッジIDのオフセットの合計から生成されます。頂点およびエッジ・ファイルは相関関係がある必要があります。ロードされたエッジに対する入出力頂点IDは指定した頂点IDオフセットに対して変更されるからです。この操作は、1つのパーティションを使用したデータのロードでのみサポートされます。

次のコード・フラグメントは、指定したグラフ・データ・ファイルから最初の100個の頂点およびエッジをロードします。この例では、IDオフセットは指定しません。

    String szUser = "username";
    String szPassword = "password";
    String szDbId = "db12c"; /* service name of the database */
    String szOPVFile = "../../data/connections.opv"; 
    String szOPEFile = "../../data/connections.ope"; 
    String szSQLLoaderPath = "../../../dbhome_1/bin/sqlldr";
    
    // Run the data loading using fine tuning 
    long lVertexOffsetlines = 0; 
    long lEdgeOffsetlines = 0; 
    long lVertexMaxlines = 100; 
    long lEdgeMaxlines = 100;
    long lVIDOffset = 0;
    long lEIDOffset = 0;
    OraclePropertyGraph opg = OraclePropertyGraph.getInstance( args, szGraphName); 
    OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
    
    opgdl.loadDataWithSqlLdr(opg, szUser, szPassword, szDbId, 
                             szOPVFile, szOPEFile, 
                             lVertexOffsetlines /* offset of lines to start loading 
                                                   from partition, default 0*/, 
                             lEdgeOffsetlines /* offset of lines to start loading from 
                                                 partition, default 0*/, 
                             lVertexMaxlines /* maximum number of lines to start 
                                                loading from partition, default -1 
                                                (all lines in partition)*/, 
                             lEdgeMaxlines /* maximum number of lines to start loading 
                                              from partition, default -1 (all lines in 
                                              partition) */, 
                             lVIDOffset /* vertex ID offset: the vertex ID will be 
                                           original vertex ID + offset, default 0 */, 
                             lEIDOffset /* edge ID offset: the edge ID will be 
                                           original edge ID + offset, default 0 */, 
                             48 /* DOP */, 
                             1 /* Total number of partitions, default 1 */, 
                             0 /* Partition to load (from 0 to totalPartitions - 1, 
                                  default 0) */, 
                             OraclePropertyGraphDataLoader.NAMEDPIPE 
                             /* splitter flag */, 
                             "chunkPrefix" /* prefix */, 
                             szSQLLoaderPath /* SQL*Loader path: the path to 
                                                bin/sqlldr*/, 
                             true /* rebuild index */,
                             "pddl=t,pdml=t" /* options */);

2.5.3 グラフ・データのパラレル取得

プロパティ・グラフのパラレル問合せは、頂点(またはエッジ)のパラレル・スキャンを実行するための単純なJava APIを提供します。パラレル取得は、表パーティション全体へのデータの配分を利用する最適化されたソリューションであり、パーティションは別個のデータベース接続を使用して問合せられます。

パラレル取得では、各要素が特定のパーティション(分割)からのすべての頂点(またはエッジ)を保持する配列が生成されます。問合せされたシャードのサブセットは、指定した開始分割IDと、指定した接続配列のサイズにより分離されます。この方法では、サブセットは[開始, 開始 - 1 + 接続の配列のサイズ]の範囲内の分割を考慮します。N分割を含む頂点表のすべての分割に、([0, N - 1]の範囲の)整数IDが割り当てられることに注意してください。

次のコードは、プロパティ・グラフをロードし、接続の配列をオープンし、オープンした接続を使用してパラレル問合せを実行してすべての頂点およびエッジを取得します。getVerticesPartitioned (getEdgesPartitioned)メソッドの呼出し数は、分割の合計数と使用される接続の数によって制御されます。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(args, szGraphName);

// Clear existing vertices/edges in the property graph 
opg.clearRepository(); 

String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";

// This object will handle parallel data loading
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
opgdl.loadData(opg, szOPVFile, szOPEFile, dop); 

// Create connections used in parallel query
Oracle[] oracleConns = new Oracle[dop];
Connection[]   conns = new Connection[dop];
for (int i = 0; i < dop; i++) { 
  oracleConns[i] = opg.getOracle().clone();
  conns[i] = oracleConns[i].getConnection();
}

long lCountV = 0;
// Iterate over all the vertices’ partitionIDs to count all the vertices
for (int partitionID = 0; partitionID < opg.getVertexPartitionsNumber(); 
     partitionID += dop) { 
  Iterable<Vertex>[] iterables 
        = opg.getVerticesPartitioned(conns /* Connection array */, 
                                     true /* skip store to cache */, 
                                     partitionID /* starting partition */); 
  lCountV += consumeIterables(iterables); /* consume iterables using 
                                             threads */
}

// Count all vertices
System.out.println("Vertices found using parallel query: " + lCountV);

long lCountE = 0;
// Iterate over all the edges’ partitionIDs to count all the edges
for (int partitionID = 0; partitionID < opg.getEdgeTablePartitionIDs(); 
     partitionID += dop) { 
  Iterable<Edge>[] iterables 
          = opg.getEdgesPartitioned(conns /* Connection array */, 
                                    true /* skip store to cache */, 
                                    partitionID /* starting partitionID */); 
  lCountE += consumeIterables(iterables); /* consume iterables using 
                                             threads */
}

// Count all edges
System.out.println("Edges found using parallel query: " + lCountE);

// Close the connections to the database after completed
for (int idx = 0; idx < conns.length; idx++) { 
   conns[idx].close();
}

2.5.4 サブグラフ抽出のための要素フィルタ・コールバックの使用

Oracle Spatial and Graphは、ユーザー定義の要素フィルタ・コールバックを使用した、容易なサブグラフ抽出のサポートを提供します。要素フィルタ・コールバックは、サブグラフに頂点(またはエッジ)を保持するために、頂点(またはエッジ)が満たす必要のある条件のセットを定義します。ユーザーはVertexFilterCallbackおよびEdgeFilterCallback APIインタフェースを実装することによって、独自の要素フィルタを定義できます。

次のコード・フラグメントは、頂点が政治的役割を持たず、その出生地が米国であるかどうかを検証するVertexFilterCallbackを実装します。

/**
* VertexFilterCallback to retrieve a vertex from the United States 
* that does not have a political role 
*/
private static class NonPoliticianFilterCallback 
implements VertexFilterCallback
{
@Override
public boolean keepVertex(OracleVertexBase vertex) 
{
String country = vertex.getProperty("country");
String role = vertex.getProperty("role");

if (country != null && country.equals("United States")) {
if (role == null || !role.toLowerCase().contains("political")) {
return true;
}
}

return false;
}

public static NonPoliticianFilterCallback getInstance()
{
return new NonPoliticianFilterCallback();
}
}

次のコード・フラグメントは、VertexFilterCallbackを使用して特定の入力頂点に接続しているエッジのうち、その接続が政治家でなく米国出身であるエッジのみを保持する、EdgeFilterCallbackを実装します。

/**
 * EdgeFilterCallback to retrieve all edges connected to an input 
 * vertex with "collaborates" label, and whose vertex is from the 
 * United States with a role different than political
*/
private static class CollaboratorsFilterCallback 
implements EdgeFilterCallback
{
private VertexFilterCallback m_vfc;
private Vertex m_startV;

public CollaboratorsFilterCallback(VertexFilterCallback vfc, 
 Vertex v) 
{
m_vfc = vfc;
m_startV = v; 
}

@Override
public boolean keepEdge(OracleEdgeBase edge) 
{
if ("collaborates".equals(edge.getLabel())) {
if (edge.getVertex(Direction.IN).equals(m_startV) && 
m_vfc.keepVertex((OracleVertex) 
edge.getVertex(Direction.OUT))) {
return true;
}
else if (edge.getVertex(Direction.OUT).equals(m_startV) && 
 m_vfc.keepVertex((OracleVertex) 
edge.getVertex(Direction.IN))) {
return true;
}
}

return false;
}

public static CollaboratorsFilterCallback
getInstance(VertexFilterCallback vfc, Vertex v)
{
return new CollaboratorsFilterCallback(vfc, v);
}

}

前に定義したフィルタ・コールバックを使用して、次のコード・フラグメントは、プロパティ・グラフをロードし、フィルタ・コールバックのインスタンスを作成し、その後に政治家ではなく米国出身であるたバラク・オバマのすべての協力者を取得します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 args, szGraphName);

// Clear existing vertices/edges in the property graph 
opg.clearRepository(); 

String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";

// This object will handle parallel data loading
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
opgdl.loadData(opg, szOPVFile, szOPEFile, dop); 

// VertexFilterCallback to retrieve all people from the United States // who are not politicians
NonPoliticianFilterCallback npvfc = NonPoliticianFilterCallback.getInstance();

// Initial vertex: Barack Obama
Vertex v = opg.getVertices("name", "Barack Obama").iterator().next();

// EdgeFilterCallback to retrieve all collaborators of Barack Obama 
// from the United States who are not politicians
CollaboratorsFilterCallback cefc = CollaboratorsFilterCallback.getInstance(npvfc, v);

Iterable<<Edge> obamaCollabs = opg.getEdges((String[])null /* Match any 
of the properties */,
cefc /* Match the 
EdgeFilterCallback */
);
Iterator<<Edge> iter = obamaCollabs.iterator();

System.out.println("\n\n--------Collaborators of Barack Obama from " +
 " the US and non-politician\n\n");
long countV = 0;
while (iter.hasNext()) {
Edge edge = iter.next(); // get the edge
// check if obama is the IN vertex
if (edge.getVertex(Direction.IN).equals(v)) {
 System.out.println(edge.getVertex(Direction.OUT) + "(Edge ID: " + 
 edge.getId() + ")"); // get out vertex
}
else {
System.out.println(edge.getVertex(Direction.IN)+ "(Edge ID: " + 
 edge.getId() + ")"); // get in vertex
}

countV++;
}

デフォルトでは、すべての頂点の取得、すべてのエッジの取得(および並列アプローチ)などのすべての読取り操作は、メソッドopg.setVertexFilterCallback(vfc)およびopg.setEdgeFilterCallback(efc)を使用して、プロパティ・グラフに関連付けられたフィルタ・コールバックを使用します。フィルタ・コールバック・セットがない場合は、すべての頂点(またはエッジ)およびエッジが取得されます。

次のコード・フラグメントはプロパティ・グラフでデフォルトのエッジ・フィルタ・コールバック・セットを使用して、エッジを取得します。

// VertexFilterCallback to retrieve all people from the United States // who are not politicians
NonPoliticianFilterCallback npvfc = NonPoliticianFilterCallback.getInstance();

// Initial vertex: Barack Obama
Vertex v = opg.getVertices("name", "Barack Obama").iterator().next();

// EdgeFilterCallback to retrieve all collaborators of Barack Obama 
// from the United States who are not politicians
CollaboratorsFilterCallback cefc = CollaboratorsFilterCallback.getInstance(npvfc, v);

opg.setEdgeFilterCallback(cefc);

Iterable<Edge> obamaCollabs = opg.getEdges();
Iterator<Edge> iter = obamaCollabs.iterator();

System.out.println("\n\n--------Collaborators of Barack Obama from " +
 " the US and non-politician\n\n");
long countV = 0;
while (iter.hasNext()) {
Edge edge = iter.next(); // get the edge
// check if obama is the IN vertex
if (edge.getVertex(Direction.IN).equals(v)) {
 System.out.println(edge.getVertex(Direction.OUT) + "(Edge ID: " + 
 edge.getId() + ")"); // get out vertex
}
else {
System.out.println(edge.getVertex(Direction.IN)+ "(Edge ID: " + 
 edge.getId() + ")"); // get in vertex
}

countV++;
}

2.5.5 プロパティ・グラフ・データの読取りでの最適化フラグの使用

Oracle Spatial and Graphは、グラフの反復パフォーマンスを向上させるために、最適化フラグのサポートを提供します。最適化フラグにより、情報がない、またはID、ラベルおよび入力/出力頂点などの最小限の情報を持つオブジェクトとして、頂点(またはエッジ)を処理できます。これにより、反復中の各頂点(またはエッジ)の処理にかかる時間が削減されます。

次の表に、プロパティ・グラフで頂点(またはエッジ)を処理するときに使用できる最適化フラグを示します。

最適化フラグ 説明
DO_NOT_CREATE_OBJECT 頂点またはエッジを処理するときに、事前定義済の定数オブジェクトを使用します。
JUST_EDGE_ID エッジの処理時に、IDのみを持つエッジ・オブジェクトを作成します。
JUST_LABEL_EDGE_ID エッジの処理時に、IDとラベルのみを持つエッジ・オブジェクトを作成します。
JUST_LABEL_VERTEX_EDGE_ID エッジの処理時に、ID、ラベル、および入力/出力頂点IDのみを持つエッジ・オブジェクトを作成します。
JUST_VERTEX_EDGE_ID エッジの処理時に、IDおよび入力/出力頂点IDのみを持つエッジ・オブジェクトを作成します。
JUST_VERTEX_ID 頂点の処理時に、IDのみを持つ頂点オブジェクトを作成します。

次のコード・フラグメントは、最適化フラグのセットを使用して、プロパティ・グラフの頂点およびエッジからすべてのIDのみを取得します。すべての頂点およびエッジの読取りによって取得されたオブジェクトには、IDのみが含まれ、キー/値プロパティや追加情報は含まれません。

import oracle.pg.common.OraclePropertyGraphBase.OptimizationFlag;
OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 args, szGraphName);

// Clear existing vertices/edges in the property graph 
opg.clearRepository(); 

String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";

// This object will handle parallel data loading
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
opgdl.loadData(opg, szOPVFile, szOPEFile, dop); 


// Optimization flag to retrieve only vertices IDs
OptimizationFlag optFlagVertex = OptimizationFlag.JUST_VERTEX_ID;

// Optimization flag to retrieve only edges IDs
OptimizationFlag optFlagEdge = OptimizationFlag.JUST_EDGE_ID;

// Print all vertices
Iterator<Vertex> vertices = 
opg.getVertices((String[])null /* Match any of the 
properties */,
null /* Match the VertexFilterCallback */, 
optFlagVertex /* optimization flag */ 
).iterator();

System.out.println("----- Vertices IDs----");
long vCount = 0;
while (vertices.hasNext()) {
OracleVertex v = vertices.next();
System.out.println((Long) v.getId());
vCount++;
}
System.out.println("Vertices found: " + vCount);


// Print all edges
Iterator<Edge> edges =
opg.getEdges((String[])null /* Match any of the properties */,
null /* Match the EdgeFilterCallback */, 
optFlagEdge /* optimization flag */ 
).iterator();

System.out.println("----- Edges ----");
long eCount = 0;
while (edges.hasNext()) {
Edge e = edges.next();
System.out.println((Long) e.getId());
eCount++;
}
System.out.println("Edges found: " + eCount);

デフォルトでは、すべての頂点の取得、すべてのエッジの取得(および並列アプローチ)などのすべての読取り操作は、メソッドopg.setDefaultVertexOptFlag(optFlagVertex)およびopg.setDefaultEdgeOptFlag(optFlagEdge)を使用してプロパティ・グラフと関連付けられた最適化フラグを使用します。頂点およびエッジの処理に対する最適化フラグが定義されていない場合、頂点およびエッジに関するすべての情報が取得されます。

次のコード・フラグメントはプロパティ・グラフでデフォルト最適化フラグのセットを使用して、その頂点およびエッジからすべてのIDのみを取得します。

import oracle.pg.common.OraclePropertyGraphBase.OptimizationFlag;

// Optimization flag to retrieve only vertices IDs
OptimizationFlag optFlagVertex = OptimizationFlag.JUST_VERTEX_ID;

// Optimization flag to retrieve only edges IDs
OptimizationFlag optFlagEdge = OptimizationFlag.JUST_EDGE_ID;

opg.setDefaultVertexOptFlag(optFlagVertex);
opg.setDefaultEdgeOptFlag(optFlagEdge);

Iterator<Vertex> vertices = opg.getVertices().iterator();
System.out.println("----- Vertices IDs----");
long vCount = 0;
while (vertices.hasNext()) {
OracleVertex v = vertices.next();
System.out.println((Long) v.getId());
vCount++;
}
System.out.println("Vertices found: " + vCount);


// Print all edges
Iterator<Edge> edges = opg.getEdges().iterator();
System.out.println("----- Edges ----");
long eCount = 0;
while (edges.hasNext()) {
Edge e = edges.next();
System.out.println((Long) e.getId());
eCount++;
}
System.out.println("Edges found: " + eCount);

2.5.6 プロパティ・グラフのサブグラフの属性の追加および削除

Oracle Spatial and Graphは、ユーザーがカスタマイズした操作コールバックを使用した、頂点またはエッジ(あるいはその両方)のサブグラフへの属性(キー/値ペア)の更新をサポートします。操作コールバックは、(特定の属性および値を追加または削除することによって)頂点(またはエッジ)を更新するために、頂点(またはエッジ)が満たす必要のある条件セットを定義します。

ユーザーはVertexOpCallbackおよびEdgeOpCallback APIインタフェースを実装することにより、独自の属性操作を定義できます。更新操作に含まれる頂点(またはエッジ)によって満たされる必要のある条件を定義するneedOpメソッド、および要素の更新時に使用されるキー名とキー値をそれぞれ返すgetAttributeKeyNameおよびgetAttributeKeyValueメソッドをオーバーライドする必要があります。

次のコード・フラグメントは、バラク・オバマの協力者のみに関連付けられたobamaCollaborator属性を操作するVertexOpCallbackを実装します。このプロパティの値は、協力者の役職に基づいて指定されます。

private static class CollaboratorsVertexOpCallback 
implements VertexOpCallback
{
private OracleVertexBase m_obama;
private List<Vertex> m_obamaCollaborators;

public CollaboratorsVertexOpCallback(OraclePropertyGraph opg)
{
// Get a list of Barack Obama'sCollaborators
m_obama = (OracleVertexBase) opg.getVertices("name", 
 "Barack Obama")
.iterator().next();

Iterable<Vertex> iter = m_obama.getVertices(Direction.BOTH, 
"collaborates");
m_obamaCollaborators = OraclePropertyGraphUtils.listify(iter);
}

public static CollaboratorsVertexOpCallback 
getInstance(OraclePropertyGraph opg)
{
return new CollaboratorsVertexOpCallback(opg);
}

/**
 * Add attribute if and only if the vertex is a collaborator of Barack 
 * Obama
*/
@Override
public boolean needOp(OracleVertexBase v)
{
return m_obamaCollaborators != null && 
 m_obamaCollaborators.contains(v);
}

@Override
public String getAttributeKeyName(OracleVertexBase v)
{
return "obamaCollaborator";
}

/**
 * Define the property's value based on the vertex role
 */
@Override
public Object getAttributeKeyValue(OracleVertexBase v)
{
String role = v.getProperty("role");
role = role.toLowerCase();
if (role.contains("political")) {
return "political";
}
else if (role.contains("actor") || role.contains("singer") ||
 role.contains("actress") || role.contains("writer") ||
 role.contains("producer") || role.contains("director")) {
return "arts";
}
else if (role.contains("player")) {
return "sports";
}
else if (role.contains("journalist")) {
return "journalism";
}
else if (role.contains("business") || role.contains("economist")) {
return "business";
}
else if (role.contains("philanthropist")) {
return "philanthropy";
}
return " ";
}
}

次のコード・フラグメントは、バラク・オバマの敵対者のみに関連付けられたobamaFeud属性を操作するEdgeOpCallbackを実装します。このプロパティの値は、協力者の役職に基づいて指定されます。

private static class FeudsEdgeOpCallback 
implements EdgeOpCallback
{
private OracleVertexBase m_obama;
private List<Edge> m_obamaFeuds;

public FeudsEdgeOpCallback(OraclePropertyGraph opg)
{
// Get a list of Barack Obama's feuds
m_obama = (OracleVertexBase) opg.getVertices("name", 
 "Barack Obama")
.iterator().next();

Iterable<Vertex> iter = m_obama.getVertices(Direction.BOTH, 
"feuds");
m_obamaFeuds = OraclePropertyGraphUtils.listify(iter);
}

public static FeudsEdgeOpCallback getInstance(OraclePropertyGraph opg)
{
return new FeudsEdgeOpCallback(opg);
}

/**
 * Add attribute if and only if the edge is in the list of Barack Obama's 
 * feuds
*/
@Override
public boolean needOp(OracleEdgeBase e)
{
return m_obamaFeuds != null && m_obamaFeuds.contains(e);
}

@Override
public String getAttributeKeyName(OracleEdgeBase e)
{
return "obamaFeud";
}

/**
 * Define the property's value based on the in/out vertex role
 */
@Override
public Object getAttributeKeyValue(OracleEdgeBase e)
{
OracleVertexBase v = (OracleVertexBase) e.getVertex(Direction.IN);
if (m_obama.equals(v)) {
v = (OracleVertexBase) e.getVertex(Direction.OUT);
}
String role = v.getProperty("role");
role = role.toLowerCase();

if (role.contains("political")) {
return "political";
}
else if (role.contains("actor") || role.contains("singer") ||
 role.contains("actress") || role.contains("writer") ||
 role.contains("producer") || role.contains("director")) {
return "arts";
}
else if (role.contains("journalist")) {
return "journalism";
}
else if (role.contains("player")) {
return "sports";
}
else if (role.contains("business") || role.contains("economist")) {
return "business";
}
else if (role.contains("philanthropist")) {
return "philanthropy";
}
return " ";
}
}

前に定義した操作コールバックを使用して、次のコード・フラグメントは、プロパティ・グラフをロードし、操作コールバックのインスタンスを作成し、その後にOraclePropertyGraphaddAttributeToAllVerticesおよびaddAttributeToAllEdgesメソッドを使用して属性を適切な頂点およびエッジに追加します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 args, szGraphName);

// Clear existing vertices/edges in the property graph 
opg.clearRepository(); 

String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";

// This object will handle parallel data loading
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
opgdl.loadData(opg, szOPVFile, szOPEFile, dop); 

// Create the vertex operation callback
CollaboratorsVertexOpCallback cvoc = CollaboratorsVertexOpCallback.getInstance(opg);

// Add attribute to all people collaborating with Obama based on their role
opg.addAttributeToAllVertices(cvoc, true /** Skip store to Cache */, dop);

// Look up for all collaborators of Obama
Iterable<Vertex> collaborators = opg.getVertices("obamaCollaborator", "political");
System.out.println("Political collaborators of Barack Obama " + getVerticesAsString(collaborators));

collaborators = opg.getVertices("obamaCollaborator", "business");
System.out.println("Business collaborators of Barack Obama " + 
getVerticesAsString(collaborators));

// Add an attribute to all people having a feud with Barack Obama to set
// the type of relation they have
FeudsEdgeOpCallback feoc = FeudsEdgeOpCallback.getInstance(opg);
opg.addAttributeToAllEdges(feoc, true /** Skip store to Cache */, dop);

// Look up for all feuds of Obama
Iterable<Edge> feuds = opg.getEdges("obamaFeud", "political");
System.out.println("\n\nPolitical feuds of Barack Obama " + getEdgesAsString(feuds));

feuds = opg.getEdges("obamaFeud", "business");
System.out.println("Business feuds of Barack Obama " + 
getEdgesAsString(feuds));

次のコード・フラグメントは、属性obamaCollaboratorにphilanthropyという値を持つ頂点を削除した後でAPI removeAttributeFromAllVerticesをコールするために使用できるVertexOpCallbackの実装を定義します。また、属性obamaFeudにbusinessという値を持つエッジを削除した後でAPI removeAttributeFromAllEdgesをコールするために使用できるEdgeOpCallbackの実装も定義します。

System.out.println("\n\nRemove 'obamaCollaborator' property from all the" + 
 "philanthropy collaborators");
PhilanthropyCollaboratorsVertexOpCallback pvoc = PhilanthropyCollaboratorsVertexOpCallback.getInstance();

opg.removeAttributeFromAllVertices(pvoc);

System.out.println("\n\nRemove 'obamaFeud' property from all the" + "business feuds");
BusinessFeudsEdgeOpCallback beoc = BusinessFeudsEdgeOpCallback.getInstance();

opg.removeAttributeFromAllEdges(beoc);

/**
 * Implementation of a EdgeOpCallback to remove the "obamaCollaborators" 
 * property from all people collaborating with Barack Obama that have a 
 * philanthropy role
 */
private static class PhilanthropyCollaboratorsVertexOpCallback implements VertexOpCallback
{
  public static PhilanthropyCollaboratorsVertexOpCallback getInstance()
  {
     return new PhilanthropyCollaboratorsVertexOpCallback();
  }
  
  /**
   * Remove attribute if and only if the property value for   
   * obamaCollaborator is Philanthropy
   */
  @Override
  public boolean needOp(OracleVertexBase v)
  {
    String type = v.getProperty("obamaCollaborator");
    return type != null && type.equals("philanthropy");
  }

  @Override
  public String getAttributeKeyName(OracleVertexBase v)
  {
    return "obamaCollaborator";
  }

  /**
   * Define the property's value. In this case can be empty
   */
  @Override
  public Object getAttributeKeyValue(OracleVertexBase v)
  {
    return " ";
  }
}

/**
 * Implementation of a EdgeOpCallback to remove the "obamaFeud" property
 * from all connections in a feud with Barack Obama that have a business role
 */
private static class BusinessFeudsEdgeOpCallback implements EdgeOpCallback
{
  public static BusinessFeudsEdgeOpCallback getInstance()
  {
    return new BusinessFeudsEdgeOpCallback();
  }

  /**
   * Remove attribute if and only if the property value for obamaFeud is       
   * business
   */
  @Override
  public boolean needOp(OracleEdgeBase e)
  {
    String type = e.getProperty("obamaFeud");
    return type != null && type.equals("business");
  }

 @Override
 public String getAttributeKeyName(OracleEdgeBase e)
 {
   return "obamaFeud";
 }

 /**
  * Define the property's value. In this case can be empty
  */
  @Override
  public Object getAttributeKeyValue(OracleEdgeBase e)
  {
    return " ";
  }
}

2.5.7 プロパティ・グラフ・メタデータの取得

データベース内のすべてのグラフ名など、グラフのメタデータと統計を取得できます。各グラフについて、最小/最大頂点ID、最小/最大エッジID、頂点プロパティ名、エッジ・プロパティ名、グラフ頂点内の分割の数、およびパラレル表スキャンをサポートするエッジ表などを取得します。

次のコード・フラグメントは、Oracle databaseに格納されている既存のプロパティ・グラフのメタデータおよび統計を取得します。

// Get all graph names in the database
List<String> graphNames = OraclePropertyGraphUtils.getGraphNames(dbArgs);

for (String graphName : graphNames) {
OraclePropertyGraph opg = OraclePropertyGraph.getInstance(args, 
graphName);

System.err.println("\n Graph name: " + graphName);
System.err.println(" Total vertices: " + 
 opg.countVertices(dop));
 
System.err.println(" Minimum Vertex ID: " + 
 opg.getMinVertexID(dop));
System.err.println(" Maximum Vertex ID: " + 
 opg.getMaxVertexID(dop));

Set<String> propertyNamesV = new HashSet<String>();
opg.getVertexPropertyNames(dop, 0 /* timeout,0 no timeout */,
 propertyNamesV);

System.err.println(" Vertices property names: " + 
getPropertyNamesAsString(propertyNamesV));

System.err.println("\n\n Total edges: " + opg.countEdges(dop));
System.err.println(" Minimum Edge ID: " + opg.getMinEdgeID(dop));
System.err.println(" Maximum Edge ID: " + opg.getMaxEdgeID(dop));

Set<String> propertyNamesE = new HashSet<String>();
opg.getEdgePropertyNames(dop, 0 /* timeout,0 no timeout */, 
 propertyNamesE);

System.err.println(" Edge property names: " +
getPropertyNamesAsString(propertyNamesE));

System.err.println("\n\n Table Information: ");
System.err.println("Vertex table number of splits: " + 
 (opg.getVertexPartitionsNumber()));
System.err.println("Edge table number of splits: " + 
 (opg.getEdgePartitionsNumber()));
}

2.5.8 新しいデータの既存のプロパティ・グラフへのマージ

グラフ・データのOracle Databaseの空のプロパティ・グラフへのロードに加えて、新しいグラフ・データを既存の(空のまたは空でない)グラフにマージできます。データのロードと同様に、データのマージも、入力頂点およびエッジを複数のチャンクに分割し、それらをデータベースの既存のグラフにパラレルにマージします。

マージを実行中のフローは、新しいグラフ・データと既存のグラフ・データの間に重複があるかどうかによって異なります。ここでの重複とは、グラフ要素の同じキーが、新しいグラフ・データと既存のグラフ・データとで異なる値を持つ可能性があるという意味です。たとえば、ID 1の頂点のキーweightは、新しいグラフ・データでは値0.8を、既存のグラフ・データでは値0.5を持つ可能性があります。この場合、新しい値または既存の値をキーに使用する必要があるかを指定する必要があります。

次のオプションがグラフ・データのマージに使用できます。JDBベース、外部表ベース、SQL loaderベースのマージ。

  • JDBCベースのグラフ・データのマージ

  • 外部表ベースのデータのマージ

  • SQL*Loaderベースのデータのマージ

JDBCベースのグラフ・データのマージ

JDBCベースのデータのマージでは、Java Database Connectivity (JDBC) APIを使用して、新しいグラフ・データをOracle Databaseにロードしてから、新しいグラフ・データを既存のグラフにマージします。

次の例は、DOP(並列度)が48、バッチ・サイズが1000で、データのマージ・オプションを指定した、JDBCベースのデータ・マージを使用し、Oracle定義のフラット・ファイル形式の頂点およびエッジ・ファイルszOPVFileおよびszOPEFileからの新しいグラフ・データを、opgという名前の既存のグラフにマージします。

String szOPVFile = "../../data/connectionsNew.opv"; 
String szOPEFile = "../../data/connectionsNew.ope"; 
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance(); 
opgdl.mergeData(opg, szOPVFile, szOPEFile, 
     48 /*DOP*/, 
     1000 /*Batch Size*/, 
     true /*Rebuild index*/,  
     "pdml=t, pddl=t, no_dup=t, use_new_val_for_dup_key=t" /*Merge options*/);

データのマージ操作のパフォーマンスを最適化するために、JDBCベースのデータのマージを呼び出すときに、フラグやヒントのセットをマージ・オプション・パラメータに指定できます。これらのヒントとしては次のものがあります。

  • DOP: データのマージ時に使用する並列度。このパラメータは、データをプロパティ・グラフVT$およびGE$表にマージするときに使用するローダー・スレッドの数と同様に、ファイルの分割時に生成するチャンクの数を決定します。

  • バッチ・サイズ:バッチ・モードでOracleのJDBC文で使用するバッチ・サイズを指定する整数。

  • 索引の再作成: trueに設定されている場合、データ・ローダーは、データがロードされるプロパティ・グラフで定義されたすべての索引および制約を無効にします。すべてのデータがプロパティ・グラフにマージされた後、すべての元の索引および制約は再作成され、有効化されます。

  • マージ・オプション: データのマージ操作を最適化するための1つのオプション(またはカンマで区切った複数のオプション)。これらのオプションとしては次のものがあります。

    • PDML=T: データ・ローダーで使用されるデータベース・セッションでのDML操作のパラレル実行を有効にします。このヒントは、長時間実行するバッチ・ジョブのパフォーマンスを向上するのに使用されます。

    • PDDL=T: データ・ローダーで使用されるデータベース・セッションでのDDL操作のパラレル実行を有効にします。このヒントは、長時間実行するバッチ・ジョブのパフォーマンスを向上するのに使用されます。

    • NO_DUP=T: 新しい入力グラフ・データに無効な重複がないことを前提にしています。有効なプロパティ・グラフでは、各頂点(またはエッジ)は指定したプロパティ・キーに対し最大で1つの値を持つことができます。無効なプロパティ・グラフでは、各頂点(またはエッジ)は特定のキーに対し2つ以上の値を持つことがあります。例として、頂点vには2つのキー/値ペア、name/"John"およびname/"Johnny"があり、同じキーを共有しています。

    • OVERLAP=F: 新しいグラフ・データと既存のグラフ・データの間に重複がないことを前提とします。つまり、新しいグラフ・データと既存のグラフ・データに複数の異なる値を持つキーはありません。

    • USE_NEW_VAL_FOR_DUP_KEY=T: 新しいグラフ・データと既存のグラフ・データの間に重複がある場合、新しいグラフ・データの値を使用します。そうでなければ、既存のグラフ・データの値を使用します。

外部表ベースのデータのマージ

外部表ベースのデータのマージでは、外部表を使用して、新しいグラフ・データをOracle Databaseにロードしてから、新しいグラフ・データを既存のグラフにマージします。

外部表ベースのデータのマージには、外部表により読み出されるファイルが格納されるディレクトリ・オブジェクトが必要です。このディレクトリは、次のSQL*Plus文を使用して作成できます。

create or replace directory tmp_dir as '/tmppath/';
grant read, write on directory tmp_dir to public;

次の例は、DOP(並列度)が48で、マージ・オプションを指定した、外部表ベースのデータ・マージを使用し、Oracleフラット・ファイル形式の頂点およびエッジ・ファイルszOPVFileおよびszOPEFileからの新しいグラフ・データを、既存のグラフopgにマージします。

String szOPVFile = "../../data/connectionsNew.opv"; 
String szOPEFile = "../../data/connectionsNew.ope"; 
String szExtDir = "tmp_dir";
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance(); 
opgdl.mergeDataWithExtTab(opg, szOPVFile, szOPEFile, 
     48 /*DOP*/, 
     true /*Use Named Pipe for splitting*/, 
     szExtDir /*database directory object*/, 
     true /*Rebuild index*/,  
     "pdml=t, pddl=t, no_dup=t, use_new_val_for_dup_key=t" /*Merge options*/);

SQL*Loaderベースのデータのマージ

SQLローダー・ベースのデータのマージでは、Oracle SQL*Loaderを使用して、新しいグラフ・データをOracle Databaseにロードしてから、新しいグラフ・データを既存のグラフにマージします。

次の例は、DOP(並列度)が48で、マージ・オプションを指定した、SQLローダー・ベースのデータ・マージを使用し、Oracleフラット・ファイル形式の頂点およびエッジ・ファイルszOPVFileおよびszOPEFileからの新しいグラフ・データを、既存のグラフopgにマージします。APIを使用するには、SQL*Loaderへのパスを指定する必要があります。

String szUser = "username";
String szPassword = "password";
String szDbId = "db12c"; /*service name of the database*/ 
String szOPVFile = "../../data/connectionsNew.opv"; 0
String szOPEFile = "../../data/connectionsNew.ope"; 
String szSQLLoaderPath = "<YOUR_ORACLE_HOME>/bin/sqlldr";    
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance(); 
opgdl.mergeDataWithSqlLdr(opg, szUser, szPassword, szDbId, szOPVFile, szOPEFile, 
     48 /*DOP*/, 
     true /*Use Named Pipe for splitting*/, 
     szSQLLoaderPath /* SQL*Loader path: the path to bin/sqlldr */, 
     true /*Rebuild index*/,  
     "pdml=t, pddl=t, no_dup=t, use_new_val_for_dup_key=t" /*Merge options*/);

2.5.9 プロパティ・グラフ・インスタンスのオープンとクローズ

プロパティ・グラフを記述する際、次のOracleプロパティ・グラフ・クラスを使用して、プロパティ・グラフ・インスタンスを適切にオープンおよびクローズします。

  • OraclePropertyGraph.getInstance: Oracleプロパティ・グラフのインスタンスをオープンします。このメソッドには、接続情報とグラフ名の2つのパラメータがあります。接続情報の形式は、バックエンド・データベースとして、HBaseとOracle NoSQL Databaseのどちらを使用しているかによって異なります。

  • OraclePropertyGraph.clearRepository: プロパティ・グラフ・インスタンスからすべての頂点およびエッジを削除します。

  • OraclePropertyGraph.shutdown: グラフ・インスタンスをクローズします。

Oracle Databaseの場合、OraclePropertyGraph.getInstanceメソッドはOracleインスタンスを使用してデータベース接続を管理します。OraclePropertyGraphには、グラフ名、ハッシュ・パーティションの数、並列度、表領域、ストレージのオプション(圧縮など)を設定できる、コンストラクタのセットがあります。次に例を示します。

import oracle.pg.rdbms.*;
Oracle oracle = new Oracle(jdbcURL, username, password);

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(oracle, graphName);
opg.clearRepository(); 
//     .
//     .  Graph description
//     .
// Close the graph instance
opg.shutdown();

インメモリー・アナリスト機能がアプリケーションに必要な場合、GraphConfigBuilderを使用してOracle Database用のグラフを作成し、引数としてグラフ名を指定してOraclePropertyGraphをインスタンス化する必要があります。たとえば、次のコード・スニペットでは、グラフconfigを作成し、OraclePropertyGraphインスタンスを取得し、データをそのグラフにロードし、インメモリー・アナリストを取得します。

import oracle.pgx.config.*;
import oracle.pgx.api.*;
import oracle.pgx.common.types.*;

...
 
PgNosqlGraphConfig cfg = GraphConfigBuilder. forPropertyGraphRdbms ()
       .setJdbcUrl("jdbc:oracle:thin:@<hostname>:1521:<sid>")
       .setUsername("<username>").setPassword("<password>")
       .setName(szGraphName)
       .setMaxNumConnections(8)
       .addEdgeProperty("lbl", PropertyType.STRING, "lbl")
       .addEdgeProperty("weight", PropertyType.DOUBLE, "1000000")
       .build();
 
  OraclePropertyGraph opg = OraclePropertyGraph.getInstance(cfg);  
 
  String szOPVFile = "../../data/connections.opv";
  String szOPEFile = "../../data/connections.ope";
 
  // perform a parallel data load
  OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
  opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, "PDML=T,PDDL=T,NO_DUP=T,"); 
 
  ...
  PgxSession session = Pgx.createSession("session-id-1");
  PgxGraph g = session.readGraphWithProperties(cfg);

  Analyst analyst = session.createAnalyst();
  ...

2.5.10 頂点の作成

頂点を作成するには、次のOracleプロパティ・グラフ・メソッドを使用します。

  • OraclePropertyGraph.addVertex: グラフに頂点インスタンスを追加します。

  • OracleVertex.setProperty: 頂点にキー値プロパティを割り当てます。

  • OraclePropertyGraph.commit: すべての変更をプロパティ・グラフ・インスタンスに保存します。

次のコード・フラグメントは、opgプロパティ・グラフ・インスタンスに年齢、名前、体重、身長、性別のプロパティを指定して、V1およびV2という名前の2つの頂点を作成します。v1プロパティはデータ型を明示的に設定します。

// Create vertex v1 and assign it properties as key-value pairs
Vertex v1 = opg.addVertex(1l);
  v1.setProperty("age",  Integer.valueOf(31));
  v1.setProperty("name", "Alice");
  v1.setProperty("weight", Float.valueOf(135.0f));
  v1.setProperty("height", Double.valueOf(64.5d));
  v1.setProperty("female", Boolean.TRUE);
  
Vertex v2 = opg.addVertex(2l);
  v2.setProperty("age",  27);
  v2.setProperty("name", "Bob");
  v2.setProperty("weight", Float.valueOf(156.0f));
  v2.setProperty("height", Double.valueOf(69.5d));
  v2.setProperty("female", Boolean.FALSE); 

2.5.11 エッジの作成

エッジを作成するには、次のOracleプロパティ・グラフ・メソッドを使用します。

  • OraclePropertyGraph.addEdge: グラフにエッジ・インスタンスを追加します。

  • OracleEdge.setProperty: エッジにキー値プロパティを割り当てます。

次のコード・フラグメントは、2つの頂点(v1およびv2)と1つのエッジ(e1)を作成します。

// Add vertices v1 and v2
Vertex v1 = opg.addVertex(1l);
v1.setProperty("name", "Alice");
v1.setProperty("age", 31);

Vertex v2 = opg.addVertex(2l);  
v2.setProperty("name", "Bob");
v2.setProperty("age", 27);

// Add edge e1
Edge e1 = opg.addEdge(1l, v1, v2, "knows");
e1.setProperty("type", "friends");

2.5.12 頂点とエッジの削除

頂点およびエッジ・インスタンスを個別に、またはすべて同時に削除できます。次のメソッドを使用します。

  • OraclePropertyGraph.removeEdge: 指定されたエッジをグラフから削除します。

  • OraclePropertyGraph.removeVertex: 指定された頂点をグラフから削除します。

  • OraclePropertyGraph.clearRepository: プロパティ・グラフ・インスタンスからすべての頂点およびエッジを削除します。

次のコード・フラグメントは、グラフ・インスタンスからエッジe1および頂点v1を削除します。頂点を削除するときに、隣接するエッジもグラフから削除されます。これは、すべてのエッジは開始頂点と終了頂点を持っている必要があるからです。開始頂点または終了頂点を削除すると、そのエッジは有効なエッジではなくなります。

// Remove edge e1
opg.removeEdge(e1);

// Remove vertex v1
opg.removeVertex(v1);

OraclePropertyGraphインスタンスからすべてのコンテンツを削除するには、OraclePropertyGraph.clearRepositoryメソッドを使用します。ただし、この操作は元に戻すことができないので、慎重に使用してください。

2.5.13 データベースから組込みインメモリー・アナリストへのグラフの読取り

グラフをOracle Databaseから、同じクライアントのJavaアプリケーション(1つのJVM)に組み込まれた、インメモリー・アナリストへ読み取ることができます。次の例では、正しいjava.io.tmpdirの設定が必要です。

int dop = 8;                    // need customization
Map<PgxConfig.Field, Object> confPgx = new HashMap<PgxConfig.Field, Object>();
confPgx.put(PgxConfig.Field.ENABLE_GM_COMPILER, false);
confPgx.put(PgxConfig.Field.NUM_WORKERS_IO, dop);   // 
confPgx.put(PgxConfig.Field.NUM_WORKERS_ANALYSIS, dop); // <= # of physical cores
confPgx.put(PgxConfig.Field.NUM_WORKERS_FAST_TRACK_ANALYSIS, 2);
confPgx.put(PgxConfig.Field.SESSION_TASK_TIMEOUT_SECS, 0);  // no timeout set
confPgx.put(PgxConfig.Field.SESSION_IDLE_TIMEOUT_SECS, 0);  // no timeout set

PgRdbmsGraphConfig cfg = GraphConfigBuilder.forPropertyGraphRdbms().setJdbcUrl("jdbc:oracle:thin:@<your_db_host>:<db_port>:<db_sid>")
     .setUsername("<username>")
     .setPassword("<password>")
     .setName("<graph_name>")
     .setMaxNumConnections(8)
     .setLoadEdgeLabel(false)
     .build();
OraclePropertyGraph opg = OraclePropertyGraph.getInstance(cfg);
ServerInstance localInstance = Pgx.getInstance();
localInstance.startEngine(confPgx);
PgxSession session = localInstance.createSession("session-id-1"); // Put your session description here.

Analyst analyst = session.createAnalyst();

// The following call will trigger a read of graph data from the database
PgxGraph pgxGraph = session.readGraphWithProperties(opg.getConfig());

long triangles = analyst.countTriangles(pgxGraph, false);
System.out.println("triangles " + triangles);

// Remove edge e1
opg.removeEdge(e1);

// Remove vertex v1
opg.removeVertex(v1);

2.5.14 頂点のラベルの指定

データベースおよびデータ・アクセス・レイヤーは頂点にラベルを提供しません。しかし、指定した頂点プロパティの値を、1つ以上のラベルとして扱うことができます。このような変換は、インメモリー・アナリストにのみ関連します。

次の例では、プロパティ"country"はsetUseVertexPropertyValueAsLabel()へのコールに指定され、カンマ区切り","がsetPropertyValueDelimiter()へのコールに指定されます。これら2つはともに、country頂点プロパティの値は、カンマで区切られた頂点のラベルとして扱われることを示しています。たとえば、頂点Xのcountryプロパティが文字列値"US"を持つ場合、その頂点ラベルはUSになります。頂点Yが文字列値"UK,CN"を持つ場合、2つのラベルUKおよびCNを持つことになります。

GraphConfigBuilder.forPropertyGraph... 
   .setName("<your_graph_name>")
   ...
  .setUseVertexPropertyValueAsLabel("country")
  .setPropertyValueDelimiter(",")
  .build();

2.5.15 インメモリー・グラフの作成

グラフ・データのメモリーへの読取りに加え、インメモリー・グラフをプログラムで作成することができます。グラフのサイズが小さい場合、またはグラフのコンテンツが非常に動的な場合に、これにより開発を簡略化することができます。キーのJavaクラスはGraphBuilderです。これはaddVertexおよびaddEdge APIを使用して追加された頂点およびエッジのセットを累積できます。すべての変更が行われると、インメモリー・グラフ・インスタンス(PgxGraph)はGraphBuilderにより作成できます。

次のJavaコード・スニペットは、グラフの作成フローを示します。存在しない頂点は、隣接するエッジの作成に伴って動的に追加されるので、明示的なaddVertexへのコールはないことに注意してください。

import oracle.pgx.api.*;

PgxSession session = Pgx.createSession("example");
GraphBuilder<Integer> builder = session.newGraphBuilder();

builder.addEdge(0, 1, 2);
builder.addEdge(1, 2, 3);
builder.addEdge(2, 2, 4);
builder.addEdge(3, 3, 4);
builder.addEdge(4, 4, 2);

PgxGraph graph = builder.build();

頂点プロパティを使用してグラフを作成するには、作成する頂点オブジェクトに対するsetPropertyを使用できます。

PgxSession session = Pgx.createSession("example");
GraphBuilder<Integer> builder = session.newGraphBuilder();

builder.addVertex(1).setProperty("double-prop", 0.1);
builder.addVertex(2).setProperty("double-prop", 2.0);
builder.addVertex(3).setProperty("double-prop", 0.3);
builder.addVertex(4).setProperty("double-prop", 4.56789);

builder.addEdge(0, 1, 2);
builder.addEdge(1, 2, 3);
builder.addEdge(2, 2, 4);
builder.addEdge(3, 3, 4);
builder.addEdge(4, 4, 2);

PgxGraph graph = builder.build();

長整数を頂点およびエッジの識別子として使用するには、GraphBuilderの新しいインスタンスの取得時に、IdType.LONGを指定します。次に例を示します。

import oracle.pgx.common.types.IdType;
GraphBuilder<Long> builder = session.newGraphBuilder(IdType.LONG);

エッジの作成時に、addEdgeへのコールで以前作成した頂点オブジェクトを直接使用することができます。

v1 = builder.addVertex(1l).setProperty("double-prop", 0.5)
v2 = builder.addVertex(2l).setProperty("double-prop", 2.0)

builder.addEdge(0, v1, v2)

頂点と同様に、エッジもプロパティを持つことができます。次の例では、setLabelを使用して、エッジ・レベルを設定します。

builder.addEdge(4, v4, v2).setProperty("edge-prop", "edge_prop_4_2").setLabel("label")

2.5.16 プロパティ・グラフの削除

データベースからプロパティ・グラフを削除するには、OraclePropertyGraphUtils.dropPropertyGraphメソッドを使用します。このメソッドには、接続情報とグラフ名の2つのパラメータがあります。次に例を示します。

// Drop the graph
Oracle oracle = new Oracle(jdbcUrl, username, password);
OraclePropertyGraphUtils.dropPropertyGraph(oracle, graphName);

PL/SQL APIを使用してプロパティ・グラフを削除することもできます。次に例を示します。

EXECUTE opg_apis.drop_pg('my_graph_name');

2.6 プロパティ・グラフ・データのテキスト索引付けの管理

Oracle Spatial and Graphプロパティ・グラフの索引付けのサポートにより、特定のキー/値またはキー/テキスト・ペア別に要素を高速に取得することを可能にします。このような索引は、要素タイプ(頂点またはエッジ)、キーのセット(および値)、および索引タイプに基づき作成されます。

2種類の索引付け構造がサポートされています。

  • 自動テキスト索引は、プロパティ・キーのセット別に頂点またはエッジの自動索引付けを行います。この主な目的は、特定のキー/値のペアに基づく頂点およびエッジの問合せパフォーマンスを向上させることです。

  • 手動テキスト索引では、プロパティ・グラフの指定された頂点およびエッジのセットに対して複数の索引を定義できます。索引に含めるグラフ要素を指定する必要があります。

Oracle Spatial and Graphは、Oracle Databaseに格納されたプロパティ・グラフに対して手動および自動テキスト索引を作成するためのAPIを提供します。索引は、使用可能なオープンソースの検索エンジン、Apache LuceneおよびSolrCloud、独自の検索および分析エンジンのOracle Textを使用して管理されます。この節の続きでは、データ・アクセス・レイヤーのプロパティ・グラフ機能を使用してテキスト索引を作成する方法に焦点を当てます。

トピック:

2.6.1 プロパティ・グラフ・データのテキスト索引の作成

テキスト索引の構成はOracleIndexParametersオブジェクトを使用して定義されます。このオブジェクトには、検索エンジン、場所、ディレクトリ(またはシャード)の数、並列度などの索引に関する情報が含まれます。

デフォルトで、テキスト索引は、メソッドopg.setDefaultIndexParameters(indexParams)を使用して、プロパティ・グラフに関連付けられたOracleIndexParametersに基づいて構成されます。自動索引の最初の作成で、構成と、将来の索引付きキーに対するテキスト検索エンジンの範囲が決まります。

索引は、パラメータの異なるセットを指定することでも作成できます。次のコード・フラグメントは、Luceneエンジンと物理ディレクトリを使用して、既存のプロパティ・グラフでの自動テキスト索引を作成します。

// Create an OracleIndexParameters object to get Index configuration (search engine, etc).
OracleIndexParameters indexParams = OracleIndexParameters.buildFS(args)  
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex("name", Vertex.class, indexParams.getParameters());

テキスト索引の最初の構成を変更するには、まず既存のグラフを削除し、新しい構成を使用して索引を再作成する必要がある場合があります。

  • Lucene検索エンジンを使用したテキスト索引の構成

  • SolrCloud検索エンジンを使用したテキスト索引の構成

  • Oracle Textを使用したテキスト索引の構成

Lucene検索エンジンを使用したテキスト索引の構成

Apache Lucene検索エンジンを使用したテキスト索引はLuceneIndexParameters構成オブジェクトを使用します。Lucene検索エンジンを使用した索引の構成パラメータには次のものがあります。

  • ディレクトリの数: 自動索引のために使用するApache Luceneディレクトリの数を指定する整数。複数のディレクトリを使用することで、ストレージとパフォーマンスのスケーラビリティが提供されます。デフォルト値は1に設定されます。

  • バッチ・サイズ: Apache Luceneでドキュメントのバッチに使用するバッチ・サイズを指定する整数。使用されるデフォルトのバッチ・サイズは10000です。

  • コミット・バッチ・サイズ: コミット操作が実行される前に、Apache Lucene索引に追加されるドキュメントの数を指定する整数。使用されるデフォルトのコミット・バッチ・サイズは500000です。

  • データ型の処理フラグ: Apache Luceneデータ型の処理が有効かどうかを指定するブール値。データ型の処理を有効にすると、numericおよびdate timeデータ型での検索が固定されます。

  • ディレクトリ名: Apache Luceneディレクトリが作成されるベース・パスの場所を指定する文字列配列。

次のコード・フラグメントは、Apache Lucene検索エンジンと物理ディレクトリを使用して、テキスト索引の構成を作成します。

OracleIndexParameters indexParams = 
      OracleIndexParameters.buildFS(4, 4, 10000, 50000, true, 
                                   "/home/data/text-index");

Oracle Spatial and Graphは、DBFSディレクトリを使用してすべてのApache Luceneディレクトリとデータ・ファイルを格納することで、Apache Luceneの機能を拡張します。

SolrCloud検索エンジンを使用したテキスト索引の構成

SolrCloud検索エンジンを使用するテキスト索引は、バックグラウンドのSolrIndexParametersオブジェクトを使用して、SolrCloudのホスト名、シャードの数、索引の作成時に使用するレプリケーション・ファクタを特定します。SolrCloud検索エンジンを使用した索引の構成パラメータには次のものがあります。

  • 構成名: Oracleプロパティ・グラフ用のSolrCloud構成ファイルが保存されるZookeeperディレクトリの名前(opgconfigなど)。構成ファイルには、必要なフィールドのスキーマ(schema.xml)およびストレージ設定(solrconfig.xml)が含まれます。

  • サーバーURL: SolrCloudサービスに接続するのに使用されるSolrCloudサーバーのURL (http://localhost:2181/solrなど)。

  • SolrCloudノード・セット: コレクションのシャードが保存されるSolrCloudサービスのノードのホスト名(node01:8983_solr,node02:8983_solr,node03:8983_solrなど)。値がnullに設定されている場合、コレクションはサービスで使用可能なすべてのSolrCloudノードを使用して作成されます。

  • Zookeeperタイムアウト: Zookeeper接続を待機するのに使用するタイムアウトを表す正の整数(秒単位)。

  • シャードの数: テキスト索引コレクションのために作成するシャードの数。SolrCloud構成がHDFSディレクトリを使用している場合、シャードの数はSolrCloudノード・セットに指定したSolrCloudノードの数を超えることはできません。

  • レプリケーション・ファクタ: SolrCloudコレクションで使用されるレプリケーション・ファクタ。デフォルト値は1に設定されます。

  • ノード当たりの最大シャード数: 各SolrCloudノードで作成できるシャードの最大数。この値は、SolrCloud Nodeセットのシャード数/ノード数よりも小さくできないことに注意してください。

  • DOP: プロパティ・グラフから頂点(またはエッジ)を読み取るとき、およびK/Vペアを索引付けするときに使用する並列度。デフォルト値は1に設定されます。

  • バッチ・サイズ: Apache SolrCloudでドキュメントのバッチに使用するバッチ・サイズを指定する整数。使用されるデフォルトのバッチ・サイズは10000です。

  • コミット・バッチ・サイズ: コミット操作が実行される前に、Apache SolrCloud索引に追加されるドキュメントの数を指定する整数。使用されるデフォルトのコミット・バッチ・サイズは500000です。

  • 書込みタイムアウト: 索引操作が完了するのを待機するのに使用するタイムアウト(秒単位)。索引操作が通信エラーのために失敗した場合、タイムアウトに達するまで、または操作が完了するまで、操作が試みられます。

次のコード・フラグメントは、SolrCloudを使用して、テキスト索引の構成を作成します。

String configName = "opgconfig";
String solrServerUrl = "nodea:2181/solr"
String solrNodeSet = "nodea:8983_solr,nodeb:8983_solr," +  
                     "nodec:8983_solr,noded:8983_solr";
 
int zkTimeout = 15;
int numShards = 4;
int replicationFactor = 1;
int maxShardsPerNode = 1;
 
OracleIndexParameters indexParams = 
                 OracleIndexParameters.buildSolr(configName, 
                                       solrServerUrl, 
                                       solrNodeSet, 
                                       zkTimeout,
                                       numShards,
                                       replicationFactor,
                                       maxShardsPerNode,
                                       4,
                                       10000,
                                       500000,
                                       15);

SolrCloudを使用している場合、「ZookeeperへのコレクションのSolrCloud構成のアップロード」に説明されているように、最初にテキスト索引用のコレクションの構成をApache Zookeeperにロードする必要があります。

Oracle Textを使用したテキスト索引の構成

Oracle Spatial and GraphはOracle Textを使用した自動テキスト索引をサポートします。Oracle Textでは、頂点(またはエッジ)表のV列に格納されているテキスト値の索引付け、検索および分析のために標準SQLが使用されます。Oracle Textはプロパティ・グラフの頂点(またはエッジ)のすべての既存のK/Vペアを索引付けするので、このオプションは自動テキスト索引とともにのみ使用でき、索引の作成時にはワイルドカード("*")索引キー・パラメータを使用する必要があります。

プロパティ・グラフ機能はUnicodeをより適切にサポートするためにNVARCHARタイプの列を使用するので、データベースのキャラクタ・セットとしてUTF8 (AL32UTF8)を使用することを強くお薦めします。

頂点表(またはエッジ表)にOracle Text索引を作成するには、ALTER SESSION権限が必要です。次の例では、権限を付与します。

SQL> grant alter session to <YOUR_USER_SCHEMA_HERE>;

カスタマイズが必要な場合、次の例のように、CTX_DDLにEXECUTEを付与します。

SQL> grant execute on ctx_ddl to <YOUR_USER_SCHEMA_HERE>;

Oracle Textを使用したテキスト索引はOracleTextIndexParametersオブジェクトを使用します。Oracle Textを使用した索引の構成パラメータには次のものがあります。

  • プリファレンス所有者: プリファレンスの所有者。

  • データ・ストア: テキスト値の格納方法を指定するデータストア・プリファレンス。データストア・プリファレンスは次のようにctx_ddl.create_preference APIを使用して作成できます。

    SQL> -- The following requires access privilege to CTX_DDL
    SQL> exec ctx_ddl.create_preference('SCOTT.OPG_DATASTORE', 'DIRECT_DATASTORE');
    

    値がNULLに設定されている場合、索引はCTXSYS.DEFAULT_DATASOREを使用して作成されます。このプリファレンスは、DIRECT_DATASTORE型を使用します。

  • フィルタ: 索引付けのためのテキストのフィルタ処理方法を決定するフィルタ・プリファレンスです。フィルタ・プリファレンスは次のようにctx_ddl.create_preferenceを使用して作成できます。

    SQL> -- The following requires access privilege to CTX_DDL
    SQL> exec ctx_ddl.create_preference('SCOTT.OPG_FILTER', 'AUTO_FILTER');
    

    値がNULLに設定されている場合、索引はCTXSYS.NULL_FILTERを使用して作成されます。このプリファレンスは、NULL_FILTER型を使用します。

  • 記憶域: テキスト索引に関連付けられた表に対して表領域および作成パラメータを指定する記憶域プリファレンス。記憶域プリファレンスは次のようにctx_ddl.create_preferenceを使用して作成できます。

    SQL> -- The following requires access privilege to CTX_DDL
    SQL> exec ctx_ddl.create_preference('SCOTT.OPG_STORAGE', 'BASIC_STORAGE');
    

    値がNULLに設定されている場合、索引はCTXSYS.DEFAULT_STORAGEを使用して作成されます。このプリファレンスは、BASIC_STORAGE型を使用します。

  • ワード・リスト: 有効な問合せオプションを指定するワード・リスト・プリファレンス。これらの問合せオプションには、ステミング、ファジー・マッチ、サブストリング、接頭辞索引付けが含まれます。データ・ストア・プリファレンスは次のようにctx_ddl.create_preferenceを使用して作成できます。

    SQL> -- The following example enables stemming and fuzzy matching for English.
    SQL> exec ctx_ddl.create_preference('SCOTT.OPG_WORDLIST', 'BASIC_WORDLIST');
    

    値がNULLに設定されている場合、索引はCTXSYS.DEFAULT_WORDLISTを使用して作成されます。このプリファレンスは、使用するデータベース言語に対して言語ステマーを使用します。

  • ストップ・リスト: 索引付けが想定されないワードのリストを指定するストップ・リスト・プリファレンス。ストップ・リスト・プリファレンスは、ctx_ddl.create_stoplistを使用して作成できます。

    値がNULLに設定されている場合、索引はCTXSYS.DEFAULT_STOPLISTを使用して作成されます。このプリファレンスは、使用するデータベース言語のストップリストを使用します。

  • レクサー: 索引付けするテキストの言語を指定するレクサー・プリファレンス。レクサー・プリファレンスは次のようにctx_ddl.create_preferenceを使用して作成できます。

    SQL> -- The following requires access privilege to CTX_DDL
    SQL> exec ctx_ddl.create_preference('SCOTT.OPG_AUTO_LEXER', 'AUTO_LEXER');
    

    値がNULLに設定されている場合、索引はCTXSYS.DEFAULT_LEXERを使用して作成されます。このプリファレンスは、インストール時に使用された言語に基づき、追加のオプションとともに、BASIC_LEXER型を使用します。

次のコード・フラグメントは、Oracle TextをデフォルトのオプションとOPG_AUTO_LEXERと使用して、テキスト索引の構成を作成します。

String prefOwner = "scott";
String datastore = (String) null;
String filter = (String) null;
String storage = (String) null;
String wordlist = (String) null;
String stoplist = (String) null;
String lexer = "OPG_AUTO_LEXER";
String options = (String) null;

OracleIndexParameters params 
                  = OracleTextIndexParameters.buildOracleText(prefOwner,               
                                                              datastore, 
                                                              filter, 
                                                              storage, 
                                                              wordlist, 
                                                              stoplist, 
                                                              lexer, 
                                                              dop, 
                                                              options);

2.6.2 プロパティ・グラフ・データの自動索引の使用

自動テキスト索引は、プロパティ・キーのセットにより、頂点またはエッジの自動的な索引付けを提供します。この主な目的は、特定のキー/値のペアに基づき、頂点およびエッジの検索速度を上げることです。指定したキーの自動索引が有効な場合、キー/値ペア参照は、データベース参照ではなく、索引に対するテキスト検索として実行されます。

プロパティ・グラフで自動索引を指定するときに、次のメソッドを使用して、自動索引を作成、削除および操作します。

  • OraclePropertyGraph.createKeyIndex(String key, Class elementClass, Parameter[] parameters): 指定したプロパティ・キーにより、タイプelementClassのすべての要素に対する自動索引を作成します。索引は指定したパラメータに基づいて構成されます。

  • OraclePropertyGraph.createKeyIndex(String[] keys, Class elementClass, Parameter[] parameters): プロパティ・キーのセットにより、タイプelementClassのすべての要素に対する自動索引を作成します。索引は指定したパラメータに基づいて構成されます。

  • OraclePropertyGraph.dropKeyIndex(String key, Class elementClass): 指定したプロパティ・キーにより、タイプelementClassのすべての要素に対する自動索引を削除します。

  • OraclePropertyGraph.dropKeyIndex(String[] keys, Class elementClass): 指定したプロパティ・キーのセット用の、タイプelementClassのすべての要素に対する自動索引を作成します。

  • OraclePropertyGraph.getAutoIndex(Class elementClass): タイプelementClassの自動索引の索引インスタンスを取得します。

  • OraclePropertyGraph.getIndexedKeys(Class elementClass): タイプelementClassのすべての要素の自動索引で現在使用されている索引付けされたキーのセットを取得します。

提供されている例ExampleRDBMS6では、入力ファイルからプロパティ・グラフを作成し、頂点に自動テキスト索引を作成し、Apache Luceneを使用していくつかのテキスト検索問合せを実行します。

次のコード・フラグメントは、name、role、religion、countryのプロパティ・キーを指定して、既存のプロパティ・グラフの頂点に自動索引を作成します。自動テキスト索引は、/home/data/text-indexディレクトリの下の4つのサブディレクトリに格納されます。Apache Luceneデータ型処理が有効になります。この例では、再索引付けタスクのためにDOP (並列度) 4を使用しています。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(…);
 
String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";
 
// Do a parallel data loading
OraclePropertyGraphDataLoader opgdl = 
OraclePropertyGraphDataLoader.getInstance();
    opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, "PDML=T,PDDL=T,NO_DUP=T,"); 
    
// Create an automatic index using Apache Lucene engine. 
// Specify Index Directory parameters (number of directories, 
// number of connections to database, batch size, commit size, 
// enable datatypes, location)
OracleIndexParameters indexParams = 
     OracleIndexParameters.buildFS(4, 4, 10000, 50000, true, 
             "/home/data/text-index ");
opg.setDefaultIndexParameters(indexParams);
    
// specify indexed keys
String[] indexedKeys = new String[4];
indexedKeys[0] = "name";
indexedKeys[1] = "role";
indexedKeys[2] = "religion";
indexedKeys[3] = "country";
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex(indexedKeys, Vertex.class);

デフォルトでは、索引はメソッドopg.setDefaultIndexParameters(indexParams)を使用してプロパティ・グラフに関連付けられたOracleIndexParametersに基づいて構成されます。

索引は、パラメータの異なるセットを指定することでも作成できます。これは次のコード・スニペットに示されています。

// Create an OracleIndexParameters object to get Index configuration (search engine, etc).
OracleIndexParameters indexParams = OracleIndexParameters.buildFS(args)  
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex("name", Vertex.class, indexParams.getParameters());

次の例のコード・フラグメントは、すべての頂点に対して問合せを実行し、キー値ペアname:Barack Obamaを持つすべての一致する頂点を検索します。この操作は、テキスト索引の検索を実行します。

さらに、getVertices APIコールでパラメータuseWildCardsを指定することにより、ワイルドカード検索がサポートされます。ワイルドカード検索は、指定したプロパティ・キーに対して自動索引が有効な場合にのみサポートされます。Apache Luceneを使用したテキスト検索構文の詳細は、https://lucene.apache.org/core/2_9_4/queryparsersyntax.htmlを参照してください。

// Find all vertices with name Barack Obama. 
    Iterator<Vertices> vertices = opg.getVertices("name", "Barack Obama").iterator();
    System.out.println("----- Vertices with name Barack Obama -----");
    countV = 0;
    while (vertices.hasNext()) {
      System.out.println(vertices.next());
      countV++;
    }
    System.out.println("Vertices found: " + countV);
 
   // Find all vertices with name including keyword "Obama"
   // Wildcard searching is supported.
    boolean useWildcard = true;
    Iterator<Vertices> vertices = opg.getVertices("name", "*Obama*").iterator();
    System.out.println("----- Vertices with name *Obama* -----");
    countV = 0;
    while (vertices.hasNext()) {
      System.out.println(vertices.next());
      countV++;
    }
    System.out.println("Vertices found: " + countV);

このコード例によって生成される出力は、次のようになります。

----- Vertices with name Barack Obama-----
Vertex ID 1 {name:str:Barack Obama, role:str:political authority, occupation:str:44th president of United States of America, country:str:United States, political party:str:Democratic, religion:str:Christianity}
Vertices found: 1
 
----- Vertices with name *Obama* -----
Vertex ID 1 {name:str:Barack Obama, role:str:political authority, occupation:str:44th president of United States of America, country:str:United States, political party:str:Democratic, religion:str:Christianity}
Vertices found: 1

2.6.3 プロパティ・グラフ・データの手動索引の使用

手動索引は、プロパティ・グラフの頂点およびエッジで複数の索引の定義をサポートします。手動索引では、要素を手動で索引へ書込み、取得、および削除する必要があります。

プロパティ・グラフで手動索引を記述するときに、次のメソッドを使用して、手動索引を追加、削除および操作します。

  • OraclePropertyGraph.createIndex(String name, Class elementClass, Parameter[] parameters): タイプelementClassのすべての要素の指定した名前を使用して手動索引を作成します。

  • OraclePropertyGraph.dropIndex(String name): 指定した手動索引を削除します。

  • OraclePropertyGraph.getIndex(String name, Class elementClass): タイプelementClassの指定した手動索引の索引インスタンスを取得します。

  • OraclePropertyGraph.getIndices(): プロパティ・グラフに作成したすべての手動索引の索引インスタンスの配列を取得します。

このトピックのコード例では、エッジに手動テキスト索引を作成し、索引にデータを入力し、Apache SolrCloudを使用していくつかのテキスト検索問合せを実行します。

SolrCloudを使用している場合、「ZookeeperへのコレクションのSolrCloud構成のアップロード」に説明されているように、最初にテキスト索引用のコレクションの構成をApache Zookeeperにロードする必要があります。

次のコード・フラグメントは、4つのシャード(ノード当たり1つのシャード)とレプリケーション係数1を使用して既存のプロパティ・グラフに手動テキスト索引を作成します。シャードの数は、SolrCloudクラスタ内のノード数と一致します。

 String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";
 
// Do a parallel data loading
OraclePropertyGraphDataLoader opgdl = 
OraclePropertyGraphDataLoader.getInstance();
    opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, "PDML=T,PDDL=T,NO_DUP=T,"); 
    
// Create a manual text index using SolrCloud// Specify Index Directory parameters: configuration name, Solr Server URL, Solr Node set, 
// replication factor, zookeeper timeout (secs),
// maximum number of shards per node,  
   // number of connections to database, batch size, commit size, 
         // write timeout (in secs)
             String configName = "opgconfig";
             String solrServerUrl = "nodea:2181/solr"
             String solrNodeSet = "nodea:8983_solr,nodeb:8983_solr," +  
                                  "nodec:8983_solr,noded:8983_solr";
 
         int zkTimeout = 15;
         int numShards = 4;
         int replicationFactor = 1;
         int maxShardsPerNode = 1;
 
OracleIndexParameters indexParams = 
                 OracleIndexParameters.buildSolr(configName, 
                                       solrServerUrl, 
                                       solrNodeSet, 
                                       zkTimeout,
                                       numShards,
                                       replicationFactor,
                                       maxShardsPerNode,
                                       4,
                                       10000,
                                       500000,
                                       15);
opg.setDefaultIndexParameters(indexParams);
    
 
// Create manual indexing on above properties for all vertices
OracleIndex<Edge> index = ((OracleIndex<Edge>) opg.createIndex("myIdx", Edge.class));
 
Vertex v1 = opg.getVertices("name", "Barack Obama").iterator().next();
 
Iterator<Edge> edges
                = v1.getEdges(Direction.OUT, "collaborates").iterator();
 
          while (edges.hasNext()) {
             Edge edge = edges.next();
             Vertex vIn = edge.getVertex(Direction.IN);
             index.put("collaboratesWith", vIn.getProperty("name"), edge);
          }

次のコード・フラグメントは、手動索引に対して問合せを実行して、キー値ペアcollaboratesWith:Beyonceを持つすべてのエッジを取得します。さらに、get APIコールでパラメータuseWildCardsを指定することにより、ワイルドカード検索がサポートされます。

// Find all edges with collaboratesWith Beyonce. 
   // Wildcard searching is supported using true parameter.
    edges = index.get("collaboratesWith", "Beyonce").iterator();
    System.out.println("----- Edges with name Beyonce -----");
    countE = 0;
    while (edges.hasNext()) {
      System.out.println(edges.next());
      countE++;
    }
    System.out.println("Edges found: "+ countE);
 
// Find all vertices with name including Bey*. 
   // Wildcard searching is supported using true parameter.
    edges = index.get("collaboratesWith", "*Bey*", true).iterator();
    System.out.println("----- Edges with collaboratesWith Bey* -----");
    countE = 0;
    while (edges.hasNext()) {
      System.out.println(edges.next());
      countE++;
    }
    System.out.println("Edges found: " + countE);

このコード例によって生成される出力は、次のようになります。

----- Edges with name Beyonce -----
Edge ID 1000 from Vertex ID 1 {country:str:United States, name:str:Barack Obama, occupation:str:44th president of United States of America, political party:str:Democratic, religion:str:Christianity, role:str:political authority} =[collaborates]=> Vertex ID 2 {country:str:United States, music genre:str:pop soul , name:str:Beyonce, role:str:singer actress} edgeKV[{weight:flo:1.0}]
Edges found: 1
 
----- Edges with collaboratesWith Bey* -----
Edge ID 1000 from Vertex ID 1 {country:str:United States, name:str:Barack Obama, occupation:str:44th president of United States of America, political party:str:Democratic, religion:str:Christianity, role:str:political authority} =[collaborates]=> Vertex ID 2 {country:str:United States, music genre:str:pop soul , name:str:Beyonce, role:str:singer actress} edgeKV[{weight:flo:1.0}]
Edges found: 1

2.6.4 プロパティ・グラフのテキスト索引を介した検索問合せの実行

Oracle Spatial and Graphは、自動および手動テキスト索引を介してテキスト検索問合せを実行するユーティリティのセットを提供します。これらのユーティリティは、特定のキー値ペアに基づく問合せから、1つまたは複数のキーを介した(ワイルドカード、ファジー検索、範囲問合せなどの拡張問合せオプションを使用した)テキスト検索の実行まで、様々です。

  • Apache Luceneを使用したテキスト索引を介した検索問合せの実行

  • SolrCloudを使用したテキスト索引を介した検索問合せの実行

  • Oracle Textを使用したテキスト索引を介した検索問合せの実行

Apache Luceneを使用したテキスト索引を介した検索問合せの実行

次のコード・フラグメントは、Apache Luceneを使用して自動索引を作成し、特定のキー/値ペアを指定して、テキスト索引を介して問合せを実行します。

// Do a parallel data loading
OraclePropertyGraphDataLoader opgdl = 
               OraclePropertyGraphDataLoader.getInstance();

opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, 
               "PDML=T,PDDL=T,NO_DUP=T,"); 
    
// Create an automatic index using Apache Lucene engine. 
// Specify Index Directory parameters (number of directories, 
// number of connections to database, batch size, commit size, 
// enable datatypes, location)
OracleIndexParameters indexParams = 
            OracleIndexParameters.buildFS(4, 4, 10000, 50000, true, 
                                          "/home/data/text-index ");
opg.setDefaultIndexParameters(indexParams);

// Create manual indexing on above properties for all vertices
OracleIndex<Edge> index = ((OracleIndex<Edge>) opg.createIndex("myIdx", Edge.class));
 
Vertex v1 = opg.getVertices("name", "Barack Obama").iterator().next();
 
Iterator<Edge> edges
                = v1.getEdges(Direction.OUT, "collaborates").iterator();
 
while (edges.hasNext()) {
  Edge edge = edges.next();
  Vertex vIn = edge.getVertex(Direction.IN);
  index.put("collaboratesWith", vIn.getProperty("name"), edge);
  index.put("country", vIn.getProperty("country"), edge);
}
 
// Wildcard searching is supported using true parameter.
Iterator<Edge> edges = index.get("country", "United States").iterator();
System.out.println("----- Edges with query: " + queryExpr + " -----");
long countE = 0;
while (edges.hasNext()) {
  System.out.println(edges.next());
  countE++;
}
System.out.println("Edges found: "+ countE);

この場合、テキスト索引はキーおよび値オブジェクトから検索問合せを生成します。useWildcardsフラグが指定されていない、または無効な場合は、取得した結果には完全一致のみが含まれます。値オブジェクトが数値または日付値の場合、生成された問合せは、下限および上限が値で定義された範囲問合せを含んだものになります。数値または日付の一致のみが取得されます。

一方、値が文字列の場合は、データ型が何であってもすべての一致するキー値ペアが取得されます。このタイプの問合せの結果のテキスト問合せは、サポートされるデータ型ごとに1つの、オプションの検索条件のセットを含む、Boolean問合せです。データ型の処理の詳細は、「データ型の処理」に記載されています。

この方法で、前述のコードは、問合せ式country1:"United States" OR country9:"United States" OR … OR countryE:"United States" (Luceneのデータ型の処理が有効な場合)、またはcountry:"1United States" OR country:"2United States" OR … OR country:"EUnited States" (Luceneのデータ型の処理が無効な場合)を生成します。

ワイルドカードを有効にして文字列値オブジェクトを使用するには、Apache Lucene構文を使用して値を記述する必要があります。Apache Luceneを使用したテキスト検索の構文の詳細は、https://lucene.apache.org/core/2_9_4/queryparsersyntax.htmlhttps://lucene.apache.org/core/2_9_4/queryparsersyntax.htmlを参照してください。

問合せを実行するデータ型のクラスを指定して、一致するキー値ペアのデータ型をフィルタ処理できます。次のコード・フラグメントでは、文字列データ型のみの1つのキー値ペアを使用して、テキスト索引を介した問合せを実行します。次のコードは、問合せ式country1:"United States" (Luceneのデータ型の処理が有効な場合)、またはcountry:"1United States" (Luceneのデータ型の処理が無効な場合)を生成します。

// Wildcard searching is supported using true parameter.
Iterator<Edge> edges = index.get("country", "United States", true, String.class).iterator();

System.out.println("----- Edges with query: " + queryExpr + " -----");
long countE = 0;
while (edges.hasNext()) {
  System.out.println(edges.next());
  countE++;
}
System.out.println("Edges found: "+ countE);

Boolean演算子を使用する処理の場合、以降の各キー値ペアは、データ型の接頭辞/接尾辞を追加する必要があるので、問合せは適切な一致を検出できます。Oracle Spatial and Graphは、自動および手動テキスト索引が必要とするデータ型の識別子と問合せ構文を使用して、ユーザーが独自のLuceneテキスト検索問合せを記述するのに役立つ、ユーティリティのセットを提供します。

LuceneIndexのメソッドbuildSearchTerm(key, value, dtClass)は、データ型の識別子をキー(または値)に追加し、指定したデータ型およびApache Luceneのデータ型の処理構成に基づき、その値を必要な文字列表現に変換することで、field:query_expr形式の問合せ式を作成します。

次のコード・フラグメントは、buildSearchTermメソッドを使用して、前述の例で使用された、問合せ式country1:United* (Luceneのデータ型の処理が有効な場合)、またはcountry:1United* (Luceneのデータ型の処理が無効な場合)を生成します。

String szQueryStrCountry = index.buildSearchTerm("country", 
                                  "United*", String.class);

キーおよび値を個々のオブジェクトとして扱い、必要な構文を使用して、WildcardQueryのような異なるLucene問合せを作成するには、LuceneIndexのメソッドappendDatatypesSuffixToKey(key, dtClass)およびappendDatatypesSuffixToValue(value, dtClass)により、適切なデータ型識別子を追加し、値を指定したデータ型に基づき必要なLucene文字列表現に変換します。

次のコード・フラグメントは、appendDatatypesSuffixToKeyメソッドを使用し、Luceneテキスト問合せに必要なフィールド名を生成します。Luceneのデータ型の処理が有効な場合、返された文字列はキー(country1)の接尾辞として文字列データ型識別子を追加します。そうでない場合、取得した文字列は元のキー(country)となります。

String key = index.appendDatatypesSuffixToKey("country", String.class);

次のコード・フラグメントは、appendDatatypesSuffixToValueメソッドを使用し、Luceneテキスト問合せに必要な問合せ本体の式を生成します。Luceneのデータ型の処理が無効な場合、返された文字列はキー(1United*)の接頭辞として文字列データ型識別子を追加します。そうでない場合、返された文字列は値の文字列表現(United)となります。

String value = index.appendDatatypesSuffixToValue("United*", String.class);

LuceneIndexもメソッドbuildSearchTermObject(key, value, dtClass)を使用して条件オブジェクトの生成をサポートします。条件オブジェクトは通常様々なタイプのLucene問合せオブジェクトで使用され、取得するドキュメントのフィールドおよび値を限定します。次のコード・フラグメントは、buildSearchTermObjectメソッドを使用して、ワイルドカード問合せオブジェクトを作成する方法を示します。

Term term = index.buildSearchTermObject("country", "United*", String.class);  
Query query = new WildcardQuery(term);

SolrCloudを使用したテキスト索引を介した検索問合せの実行

次のコード・フラグメントは、SolrCloudを使用して自動索引を作成し、特定のキー値ペアを指定して、テキスト索引を介して問合せを実行します。

// Create a manual text index using SolrCloud// Specify Index Directory parameters: configuration name, Solr Server URL, Solr Node set, 
// replication factor, zookeeper timeout (secs),
// maximum number of shards per node,  
// number of connections to database, batch size, commit size, 
// write timeout (in secs)
String configName = "opgconfig";
String solrServerUrl = "nodea:2181/solr"
String solrNodeSet = "nodea:8983_solr,nodeb:8983_solr," +  
                     "nodec:8983_solr,noded:8983_solr";
 
int zkTimeout = 15;
int numShards = 4;
int replicationFactor = 1;
int maxShardsPerNode = 1;
 
OracleIndexParameters indexParams = 
                 OracleIndexParameters.buildSolr(configName, 
                                       solrServerUrl, 
                                       solrNodeSet, 
                                       zkTimeout,
                                       numShards,
                                       replicationFactor,
                                       maxShardsPerNode,
                                       4,
                                       10000,
                                       500000,
                                       15);
opg.setDefaultIndexParameters(indexParams);
    
// specify indexed keys
String[] indexedKeys = new String[4];
indexedKeys[0] = "name";
indexedKeys[1] = "role";
indexedKeys[2] = "religion";
indexedKeys[3] = "country";
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex(indexedKeys, Vertex.class); 

// Create manual indexing on above properties for all vertices
OracleIndex<Vertex> index = ((OracleIndex<Vertex>) opg.getAutoIndex(Vertex.class);
 
Iterator<Vertex> vertices = index.get("country", "United States").iterator();
System.out.println("----- Vertices with query: " + queryExpr + " -----");
countV = 0;
while (vertices.hasNext()) {
  System.out.println(vertices.next());
  countV++;
}
System.out.println("Vertices found: "+ countV);

この場合、テキスト索引は値オブジェクトから検索問合せを生成します。useWildcardsフラグが指定されていない、または無効な場合は、取得した結果には完全一致のみが含まれます。値オブジェクトが数値または日付値の場合、生成された問合せは、下限および上限が値で定義された範囲問合せを含んだものになります。数値または日付の一致のみが取得されます。

一方、値が文字列の場合は、データ型が何であってもすべての一致するキー値ペアが取得されます。このタイプの問合せの結果のテキスト問合せは、サポートされるデータ型ごとに1つの、オプションの検索条件のセットを含む、Boolean問合せです。データ型の処理の詳細は、「データ型の処理」に記載されています。

この方法で前述のコードは問合せ式country_str:"United States" OR country_ser:"United States" OR … OR country_json:"United States"を生成します。

ワイルドカードを有効にして文字列値オブジェクトを使用するには、Apache Lucene構文を使用して値を記述する必要があります。Apache Luceneを使用したテキスト検索の構文の詳細は、https://lucene.apache.org/core/2_9_4/queryparsersyntax.htmlhttps://lucene.apache.org/core/2_9_4/queryparsersyntax.htmlを参照してください。

問合せを実行するデータ型のクラスを指定して、一致するキー値ペアのデータ型をフィルタ処理できます。次のコード・フラグメントでは、文字列データ型のみの1つのキー値ペアを使用して、テキスト索引を介した問合せを実行します。このコードは問合せ式country_str:"United States"を生成します。

// Wildcard searching is supported using true parameter.
Iterator<Edge> edges = index.get("country", "United States", true, String.class).iterator();
    System.out.println("----- Edges with query: " + queryExpr + " -----");
    countE = 0;
    while (edges.hasNext()) {
      System.out.println(edges.next());
      countE++;
    }
    System.out.println("Edges found: "+ countE);

Boolean演算子を使用する処理の場合、以降の各キー値ペアは、データ型の接頭辞/接尾辞を追加する必要があるので、問合せは適切な一致を検出できます。Oracle Spatial and Graphは、自動および手動テキスト索引が必要とするデータ型の識別子と問合せ構文を使用して、ユーザーが独自のLuceneテキスト検索問合せを記述するのに役立つ、ユーティリティのセットを提供します。

SolrIndexのメソッドbuildSearchTerm(key, value, dtClass)は、データ型の識別子をキー(または値)に追加し、索引が必要とするデータ型形式を使用して、その値を必要な文字列表現に変換することで、field:query_expr形式の問合せ式を作成します。

次のコード・フラグメントは、buildSearchTermメソッドを使用して、前述の例で使用された、問合せ式country_str:United*を生成します。

String szQueryStrCountry = index.buildSearchTerm("country", 
                                  "United*", String.class);

キーおよび値を個々のオブジェクトとして扱い、必要な構文を使用して、WildcardQueryのような異なるSolrCloud問合せを作成するには、SolrIndexのメソッドappendDatatypesSuffixToKey(key, dtClass)およびappendDatatypesSuffixToValue(value, dtClass)により、適切なデータ型識別子を追加し、キーおよび値を指定したデータ型に基づく必要なSolrCloud文字列表現に変換します。

次のコード・フラグメントは、appendDatatypesSuffixToKeyメソッドを使用し、Luceneテキスト問合せに必要なフィールド名を生成します。Luceneのデータ型の処理が有効な場合、返された文字列はキー(country_str)の接尾辞として文字列データ型識別子を追加します。

String key = index.appendDatatypesSuffixToKey("country", String.class);

次のコード・フラグメントは、appendDatatypesSuffixToValueメソッドを使用し、Luceneテキスト問合せに必要な問合せ本体の式を生成します。Luceneのデータ型の処理が無効な場合、返された文字列はキー(1United*)の接頭辞として文字列データ型識別子を追加します。そうでない場合、返された文字列は値の文字列表現(United)となります。

String key = index.appendDatatypesSuffixToKey("country", String.class);

次のコード・フラグメントは、appendDatatypesSuffixToValueメソッドを使用し、SolrCloudテキスト問合せに必要な問合せ本体の式を生成します。返された文字列は値の文字列表現(United*)となります。

String value = index.appendDatatypesSuffixToValue("United*", String.class);

Oracle Textを使用したテキスト索引を介した検索問合せの実行

Oracle Textでのテキスト検索問合せは、スコアの範囲および順序、スコアIDを含む"contains"句を使用したSELECT SQL問合せに変換されます。Oracleのプロパティ・グラフには、OracleTextQueryObjectという名前のユーティリティが含まれ、これを使用して、Oracle Text索引を介したテキスト検索問合せを実行できます。

次のコード・フラグメントは、Oracle Textを使用して自動索引を作成し、特定のキー値ペアを指定して、テキスト索引を介して問合せを実行します。

String prefOwner = "scott";
String datastore = (String) null;
String filter = (String) null;
String storage = (String) null;
String wordlist = (String) null;
String stoplist = (String) null;
String lexer = "OPG_AUTO_LEXER";
String options = (String) null;

OracleIndexParameters params 
                  = OracleTextIndexParameters.buildOracleText(prefOwner,               
                                                              datastore, 
                                                              filter, 
                                                              storage, 
                                                              wordlist, 
                                                              stoplist, 
                                                              lexer, 
                                                              dop, 
                                                              options);

opg.setDefaultIndexParameters(indexParams);
    

// Create auto indexing on all existing properties, use wildcard for all
opg.createKeyIndex(("*", Vertex.class); 

// Get the auto index object
OracleIndex<Vertex> index = ((OracleIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Create the text query object for Oracle Text
OracleTextQueryObject otqo 
               = OracleTextQueryObject.getInstance("Obama" /* query body */, 
                                                   1 /* score */, 
                                                   ScoreRange.POSITIVE /* Score range */,   
                                                   Direction.ASC /* order by direction*/);
 
Iterator<Vertex> vertices = index.get("name", otqo).iterator();
System.out.println("----- Vertices with query: " + otqo.toString() + " -----");
countV = 0;
while (vertices.hasNext()) {
  System.out.println(vertices.next());
  countV++;
}
System.out.println("Vertices found: "+ countV);

問合せを実行するデータ型のクラスを指定して、一致するキー値ペアのデータ型をフィルタ処理できます。次のコード・フラグメントでは、テキスト索引を介して問合せを実行し、語句Obamaを含む文字列値を持つすべてのプロパティを取得します。

// Create the text query object for Oracle Text
OracleTextQueryObject otqo 
               = OracleTextQueryObject.getInstance("Obama" /* query body */, 
                                                   1 /* score */, 
                                                   ScoreRange.POSITIVE 
                                                   /* Score range */,   
                                                   Direction.ASC 
                                                   /* order by direction*/,
                                                   "name",
                                                   String.class);
 
Iterator<Vertex> vertices = index.get("name", otqo).iterator();
System.out.println("----- Vertices with query: " + otqo.toString() + " -----");
countV = 0;
while (vertices.hasNext()) {
  System.out.println(vertices.next());
  countV++;
}
System.out.println("Vertices found: "+ countV);

2.6.5 データ型の処理

Oracleのプロパティ・グラフ・サポートは、値のデータ型に基づいて要素のキー/値ペアを索引付けおよび格納します。データ型処理の主な目的は、数値や日付範囲の問合せなど、包括的な問合せのサポートを提供することです。

デフォルトでは、特定のキー/値ペアに対する検索は、その値のデータ型に基づく問合せ式まで行われます。たとえば、キー値ペアage:30を持つ頂点を検索する場合は、整数のデータ型が指定されたすべての年齢フィールドに対して問合せが実行されます。値が問合せ式である場合は、API get(String key, Object value, Class dtClass, Boolean useWildcards)をコールすることにより、検索する値のデータ型クラスも指定できます。データ型を指定しないと、問合せ式はすべての使用可能なデータ型と一致します。

Boolean演算子を使用する処理の場合、以降の各キー値ペアは、データ型の接頭辞/接尾辞を追加する必要があるので、問合せは適切な一致を検出できます。次のトピックでは、Apache LuceneおよびSolrCloud用のこの接頭辞/接尾辞の追加方法について説明します。

トピック:

2.6.5.1 Apache Luceneでのデータ型識別子の追加

Luceneのデータ型の処理が有効な場合、問合せ式のキーの接尾辞として適切なデータ型識別子を追加する必要があります。これは、キーに対してString.concat()の演算を実行することによって行うことができます。Luceneのデータ型の処理が無効な場合、データ型識別子を値Stringに接頭辞として挿入する必要があります。次の表に、Apache Luceneを使用したテキスト索引付けに使用できるデータ型識別子を示します(LuceneIndexのJavadocも参照)。

表2-1 Apache Luceneデータ型識別子

Luceneデータ型識別子 説明

TYPE_DT_STRING

String

TYPE_DT_BOOL

Boolean

TYPE_DT_DATE

Date

TYPE_DT_FLOAT

Float

TYPE_DT_DOUBLE

Double

TYPE_DT_LONG

Long

TYPE_DT_CHAR

Character

TYPE_DT_SHORT

Short

TYPE_DT_BYTE

Byte

TYPE_DT_SPATIAL

Spatial

TYPE_DT_INTEGER

Integer

TYPE_DT_SERIALIZABLE

Serializable

次のコード・フラグメントは、Luceneのデータ型処理を使用してエッジに手動索引を作成し、データを追加してから、手動索引に対して問合せを実行し、ワイルドカードを使用してキー/値ペアcollaboratesWith:Beyonce AND country1:United*を持つすべてのエッジを取得します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(…);
 
String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";
 
// Do a parallel data loading
OraclePropertyGraphDataLoader opgdl = 
OraclePropertyGraphDataLoader.getInstance();
    opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, "PDML=T,PDDL=T,NO_DUP=T,"); 
    
// Specify Index Directory parameters (number of directories, 
   // number of connections to database, batch size, commit size, 
         // enable datatypes, location)
OracleIndexParameters indexParams = 
     OracleIndexParameters.buildFS(4, 4, 10000, 50000, true, 
            "/home/data/text-index ");
opg.setDefaultIndexParameters(indexParams);
// Create manual indexing on above properties for all edges
OracleIndex<Edge> index = ((OracleIndex<Edge>) opg.createIndex("myIdx", Edge.class));
 
Vertex v1 = opg.getVertices("name", "Barack Obama").iterator().next();
 
Iterator<Edge> edges
                = v1.getEdges(Direction.OUT, "collaborates").iterator();
 
          while (edges.hasNext()) {
             Edge edge = edges.next();
             Vertex vIn = edge.getVertex(Direction.IN);
             index.put("collaboratesWith", vIn.getProperty("name"), edge);
             index.put("country", vIn.getProperty("country"), edge);
          }
 
// Wildcard searching is supported using true parameter.
    String key = "country";
    key = key.concat(String.valueOf(oracle.pg.text.lucene.LuceneIndex.TYPE_DT_STRING));
   
    String queryExpr = "Beyonce AND " + key + ":United*";
    edges = index.get("collaboratesWith", queryExpr, true /*UseWildcard*/).iterator();
    System.out.println("----- Edges with query: " + queryExpr + " -----");
    countE = 0;
    while (edges.hasNext()) {
      System.out.println(edges.next());
      countE++;
    }
    System.out.println("Edges found: "+ countE);

このコード例によって生成される出力は、次のようになります。

----- Edges with name Beyonce AND country1:United* -----
Edge ID 1000 from Vertex ID 1 {country:str:United States, name:str:Barack Obama, occupation:str:44th president of United States of America, political party:str:Democratic, religion:str:Christianity, role:str:political authority} =[collaborates]=> Vertex ID 2 {country:str:United States, music genre:str:pop soul , name:str:Beyonce, role:str:singer actress} edgeKV[{weight:flo:1.0}]
Edges found: 1

次のコード・フラグメントは、頂点に自動索引を作成し、Luceneのデータ型処理を無効化し、データを追加してから、前述の例からの手動索引に対して問合せを実行し、ワイルドカードを使用してキー/値ペアcountry:United* AND role:1*political*を持つすべての頂点を取得します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(…);
 
String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";
 
// Do a parallel data loading
OraclePropertyGraphDataLoader opgdl = 
OraclePropertyGraphDataLoader.getInstance();
    opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, "PDML=T,PDDL=T,NO_DUP=T,"); 
    
// Create an automatic index using Apache Lucene engine. 
// Specify Index Directory parameters (number of directories, 
   // number of connections to database, batch size, commit size, 
         // enable datatypes, location)
OracleIndexParameters indexParams = 
     OracleIndexParameters.buildFS(4, 4, 10000, 50000, false, "/ home/data/text-index ");
opg.setDefaultIndexParameters(indexParams);
    
// specify indexed keys
String[] indexedKeys = new String[4];
indexedKeys[0] = "name";
indexedKeys[1] = "role";
indexedKeys[2] = "religion";
indexedKeys[3] = "country";
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex(indexedKeys, Vertex.class);
 
// Wildcard searching is supported using true parameter.
    String value = "*political*";
    value = String.valueOf(LuceneIndex.TYPE_DT_STRING) + value;
String queryExpr = "United* AND role:" + value;
    
 
vertices = opg.getVertices("country", queryExpr,  true /*useWildcard*/).iterator();
    System.out.println("----- Vertices with query: " + queryExpr + " -----");
    countV = 0;
    while (vertices.hasNext()) {
      System.out.println(vertices.next());
      countV++;
    }
    System.out.println("Vertices found: " + countV);

このコード例によって生成される出力は、次のようになります。

----- Vertices with query: United* and role:1*political* -----
Vertex ID 30 {name:str:Jerry Brown, role:str:political authority, occupation:str:34th and 39th governor of California, country:str:United States, political party:str:Democratic, religion:str:roman catholicism}
Vertex ID 24 {name:str:Edward Snowden, role:str:political authority, occupation:str:system administrator, country:str:United States, religion:str:buddhism}
Vertex ID 22 {name:str:John Kerry, role:str:political authority, country:str:United States, political party:str:Democratic, occupation:str:68th United States Secretary of State, religion:str:Catholicism}
Vertex ID 21 {name:str:Hillary Clinton, role:str:political authority, country:str:United States, political party:str:Democratic, occupation:str:67th United States Secretary of State, religion:str:Methodism}
Vertex ID 19 {name:str:Kirsten Gillibrand, role:str:political authority, country:str:United States, political party:str:Democratic, occupation:str:junior United States Senator from New York, religion:str:Methodism}
Vertex ID 13 {name:str:Ertharin Cousin, role:str:political authority, country:str:United States, political party:str:Democratic}
Vertex ID 11 {name:str:Eric Holder, role:str:political authority, country:str:United States, political party:str:Democratic, occupation:str:United States Deputy Attorney General}
Vertex ID 1 {name:str:Barack Obama, role:str:political authority, occupation:str:44th president of United States of America, country:str:United States, political party:str:Democratic, religion:str:Christianity}
Vertices found: 8
2.6.5.2 SolrCloudでのデータ型識別子の追加

SolrCloudテキスト索引のブール演算では、問合せ式のキーの接尾辞として適切なデータ型識別子を追加する必要があります。これは、キーに対してString.concat()の演算を実行することによって行うことができます。次の表に、SolrCloudを使用したテキスト索引付けに使用できるデータ型識別子を示しています(SolrIndexのJavadocを参照)。

表2-2 SolrCloudデータ型識別子

Solrデータ型識別子 説明

TYPE_DT_STRING

String

TYPE_DT_BOOL

Boolean

TYPE_DT_DATE

Date

TYPE_DT_FLOAT

Float

TYPE_DT_DOUBLE

Double

TYPE_DT_INTEGER

Integer

TYPE_DT_LONG

Long

TYPE_DT_CHAR

Character

TYPE_DT_SHORT

Short

TYPE_DT_BYTE

Byte

TYPE_DT_SPATIAL

Spatial

TYPE_DT_SERIALIZABLE

Serializable

次のコード・フラグメントでは、SolrCloudを使用してエッジに手動索引を作成し、データを追加してから、手動索引に対して問合せを実行し、ワイルドカードを使用してキー/値ペアcollaboratesWith:Beyonce AND country1:United*を持つすべてのエッジを取得します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(args,       
                                                          szGraphName);
 
String szOPVFile = "../../data/connections.opv";
String szOPEFile = "../../data/connections.ope";
 
// Do a parallel data loading
OraclePropertyGraphDataLoader opgdl = 
OraclePropertyGraphDataLoader.getInstance();
    opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, "PDML=T,PDDL=T,NO_DUP=T,"); 
    
// Create a manual text index using SolrCloud// Specify Index Directory parameters: configuration name, Solr Server URL, Solr Node set, 
// replication factor, zookeeper timeout (secs),
// maximum number of shards per node,  
   // number of connections to database, batch size, commit size, 
         // write timeout (in secs)
             String configName = "opgconfig";
             String solrServerUrl = "nodea:2181/solr"
             String solrNodeSet = "nodea:8983_solr,nodeb:8983_solr," +  
                                  "nodec:8983_solr,noded:8983_solr";
 
         int zkTimeout = 15;
         int numShards = 4;
         int replicationFactor = 1;
         int maxShardsPerNode = 1;
 
OracleIndexParameters indexParams = 
                 OracleIndexParameters.buildSolr(configName, 
                                       solrServerUrl, 
                                       solrNodeSet, 
                                       zkTimeout,
                                       numShards,
                                       replicationFactor,
                                       maxShardsPerNode,
                                       4,
                                       10000,
                                       500000,
                                       15);
opg.setDefaultIndexParameters(indexParams);
    
// Create manual indexing on above properties for all vertices
OracleIndex<Edge> index = ((OracleIndex<Edge>) opg.createIndex("myIdx", Edge.class));
 
Vertex v1 = opg.getVertices("name", "Barack Obama").iterator().next();
 
Iterator<Edge> edges
                = v1.getEdges(Direction.OUT, "collaborates").iterator();
 
          while (edges.hasNext()) {
             Edge edge = edges.next();
             Vertex vIn = edge.getVertex(Direction.IN);
             index.put("collaboratesWith", vIn.getProperty("name"), edge);
             index.put("country", vIn.getProperty("country"), edge);
          }
 
// Wildcard searching is supported using true parameter.
    String key = "country";
    key = key.concat(oracle.pg.text.solr.SolrIndex.TYPE_DT_STRING);
   
    String queryExpr = "Beyonce AND " + key + ":United*";
    edges = index.get("collaboratesWith", queryExpr, true /** UseWildcard*/).iterator();
    System.out.println("----- Edges with query: " + queryExpr + " -----");
    countE = 0;
    while (edges.hasNext()) {
      System.out.println(edges.next());
      countE++;
    }
    System.out.println("Edges found: "+ countE);

このコード例によって生成される出力は、次のようになります。

----- Edges with name Beyonce AND country_str:United* -----
Edge ID 1000 from Vertex ID 1 {country:str:United States, name:str:Barack Obama, occupation:str:44th president of United States of America, political party:str:Democratic, religion:str:Christianity, role:str:political authority} =[collaborates]=> Vertex ID 2 {country:str:United States, music genre:str:pop soul , name:str:Beyonce, role:str:singer actress} edgeKV[{weight:flo:1.0}]
Edges found: 1
2.6.5.3 Oracle Textのデータ型の処理

Oracle Textを使用するテキスト索引は、プロパティ・グラフ表のKおよびVテキスト列に対して作成されます。使用可能なすべてのデータ型でテキスト索引付け機能を提供するために、V列に数値、空間および日時のキー/値ペアの文字列表現が移入されます。

V列への移入時に使用される日時および数値形式を指定するには、メソッドsetNumberToCharSqlFormatStringおよびsetTimeToCharSqlFormatStringを使用できます。次のコード・スニペットは、プロパティ・グラフ・インスタンスの日時および数値形式の設定方法を示します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(args,       
                                                          szGraphName);
opg.setNumberToCharSqlFormatString("TM9");
opg.setTimeToCharSqlFormatString("SYYYY-MM-DD\"T\"HH24:MI:SS.FF9TZH:TZM");

数値または日時値に対してテキスト検索問合せを実行するときは、プロパティ・グラフに関連付けられた形式を使用したテキスト式を使用する必要があります。OraclePropertyGraphにはユーティリティAPI opg.parseValueToCharSQLFormatStringが含まれ、これにより数値または日時オブジェクトをV列ストレージで使用される形式へ解析できます。次のコード・スニペットは、日付値を使用してこの関数を呼び出し、取得したテキストからテキスト問合せオブジェクトを作成します。

Date d = new java.util.Date(100l);
String szDate = opg.parseValueToCharSQLFormatString(d);

// Create the text query object for Oracle Text
OracleTextQueryObject otqo 
               = OracleTextQueryObject.getInstance(szDate /* query body */, 
                                                   1 /* score */, 
                                                   ScoreRange.POSITIVE /* Score range */,   
                                                   Direction.ASC /* order by direction);

2.6.6 ZookeeperへのコレクションのSolrCloud構成のアップロード

Oracle Spatial and Graphのプロパティ・グラフでSolrCloudテキスト索引を使用する前に、コレクションの構成をZookeeperにアップロードする必要があります。これは、SolrCloudクラスタ・ノードのいずれかでZkCliツールを使用して実行できます。

事前定義済のコレクション構成ディレクトリは、インストール・ホームの下にあるdal/opg-solr-configにあります。次に、PropertyGraph構成ディレクトリのアップロード方法の例を示します。

  1. $ORACLE_HOME/md/property_graph/dal/opg-solr-config.zipを、Solrクラスタ・ノードのいずれかにある/tmpディレクトリにコピーします。次に例を示します。

    scp –r $ORACLE_HOME/md/property_graph/dal/opg-solr-config.zip user@solr-node:/tmp
    
  2. 同じノードでZkCliツールを使用して、次のようなコマンドを実行します。

    cd /tmp
    unzip opg-solr-config.zip
    $SOLR_HOME/bin/zkcli.sh -zkhost 127.0.0.1:2181/solr -cmd upconfig –confname opgconfig -confdir /tmp/opg-solr-config
    

2.6.7 プロパティ・グラフ・データのテキスト索引の構成設定の更新

Oracleのプロパティ・グラフ・サポートは、Apache Lucene、SolrCloudおよびOracle Textとの統合による手動および自動テキスト索引を管理します。作成時に、検索エンジンおよびその他のテキスト索引で使用される構成設定を指定する、OracleIndexParametersオブジェクトを作成する必要があります。プロパティ・グラフのテキスト索引が作成された後、これらの構成設定を変更することはできません。自動索引の場合、すべての頂点索引キーは1つのテキスト索引により管理され、すべてのエッジ索引キーは、最初の頂点またはエッジ・キーが索引付けされたときに指定した構成を使用して、別のテキスト索引により管理されます。

構成設定を変更する必要がある場合、まず現在の索引を無効化し、新しいOracleIndexParametersオブジェクトを使用して再度作成します。次のコード・フラグメントは、既存のプロパティ・グラフに対して2つのApache Luceneベースの自動索引(頂点およびエッジに)を作成し、それらを無効化してから、SolrCloudを使用するために再作成します。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
      …);
 
// Create an automatic index using Apache Lucene.
// Specify Index Directory parameters (number of directories,
// number of connections to database, batch size, commit size,
// enable datatypes, location)
OracleIndexParameters luceneIndexParams =
     OracleIndexParameters.buildFS(4, 4, 10000, 50000, true,
             "/home/oracle/text-index ");
 
// Specify indexed keys
String[] indexedKeys = new String[4];
indexedKeys[0] = "name";
indexedKeys[1] = "role";
indexedKeys[2] = "religion";
indexedKeys[3] = "country";
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex(indexedKeys, Vertex.class, luceneIndexParams.getParameters());
 
// Create auto indexing on weight for all edges
opg.createKeyIndex("weight", Edge.class, luceneIndexParams.getParameters());
 
// Disable auto indexes to change parameters
opg.getOracleIndexManager().disableVertexAutoIndexer();
opg.getOracleIndexManager().disableEdgeAutoIndexer();
 
 
// Recreate text indexes using SolrCloud
// Specify Index Directory parameters: configuration name, Solr Server URL, Solr Node set,
// replication factor, zookeeper timeout (secs),
// maximum number of shards per node,
// number of connections to database, batch size, commit size,
// write timeout (in secs)
String configName = "opgconfig";
String solrServerUrl = "nodea:2181/solr";
String solrNodeSet = "nodea:8983_solr,nodeb:8983_solr," +
   "nodec:8983_solr,noded:8983_solr";
 
int zkTimeout = 15;
int numShards = 4;
int replicationFactor = 1;
int maxShardsPerNode = 1;
 
OracleIndexParameters solrIndexParams =
OracleIndexParameters.buildSolr(configName,
                                solrServerUrl,
                                solrNodeSet,
                                zkTimeout,
                                numShards,
                                replicationFactor,
                                maxShardsPerNode,
                                4,
                                10000,
                                500000,
                                15);
 
// Create auto indexing on above properties for all vertices
opg.createKeyIndex(indexedKeys, Vertex.class, solrIndexParams.getParameters());
 
// Create auto indexing on weight for all edges
opg.createKeyIndex("weight", Edge.class, solrIndexParams.getParameters());

2.6.8 プロパティ・グラフ・データのテキスト索引でのパラレル問合せの使用

Oracle Spatial and Graphのテキスト索引では、パラレル問合せ実行を使用して、特定のキー/値またはキー/テキスト・ペア別に数百万の頂点およびエッジに対してテキスト問合せを実行できます。

パラレル・テキスト問合せは、SolrCloud(またはApache Luceneのサブディレクトリ)内のシャード間での索引のデータの配分を利用する最適化されたソリューションであり、各シャードは別個の索引接続を使用して問合せられます。これには、読取り操作のパフォーマンスを向上させてから索引から複数の要素を取得するために、複数のスレッドとSolrCloud (またはApache Lucene)検索エンジンへの接続が関連します。この方法では、スコアに基づく一致結果のランク付けは行われません。

パラレル・テキスト問合せでは、各要素がシャードからの指定されたK/Vペアに一致する属性を持つすべての頂点(またはエッジ)を保持する配列が生成されます。問合せられたシャードのサブセットは、特定の開始サブディレクトリIDと指定された接続配列のサイズによって区切られます。これにより、サブセットは[開始, 開始 - 1 + 接続配列のサイズ]の範囲内のシャードを考慮します。Nシャードを含む索引にあるすべてのシャードに、([0, N - 1]の範囲の)整数IDが割り当てられることに注意してください。

  • Apache Luceneを使用したパラレル・テキスト問合せ

  • SoltCloudを使用したパラレル・テキスト検索

  • Oracle Textを使用したパラレル・テキスト検索

Apache Luceneを使用したパラレル・テキスト問合せ

LuceneIndexでメソッドgetPartitionedを呼び出して、サブディレクトリのセットへの接続の配列(SearcherManagerオブジェクト)、検索するキー/値ペア、および開始サブディレクトリIDを指定することにより、Apache Luceneを使用したパラレル・テキスト問合せを使用できます。各サブディレクトリは索引のその他のサブディレクトリに依存していないため、各接続が適切なサブディレクトリにリンクしている必要があります

次のコード・フラグメントは、Apache Lucene検索エンジンを使用して、自動テキスト索引を生成し、パラレル・テキスト問合せを実行します。LuceneIndexクラスでのgetPartitionedメソッドへの呼出し数は、サブディレクトリの合計数と使用される接続の数によって制御されます。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 …);

// Create an automatic index
OracleIndexParameters indexParams 
= OracleIndexParameters.buildFS(dop /* number of directories */,
dop /* number of connections
used when indexing */,
10000 /* batch size before commit*/,
500000 /* commit size before Lucene commit*/,
true /* enable datatypes */,
"./lucene-index" /* index location */);

opg.setDefaultIndexParameters(indexParams);

// Create auto indexing on name property for all vertices
System.out.println("Create automatic index on name for vertices");
opg.createKeyIndex("name", Vertex.class);

// Get the LuceneIndex object 
SearcherManager[] conns = new SearcherManager[dop];
LuceneIndex<Vertex> index = (LuceneIndex<Vertex>) opg.getAutoIndex(Vertex.class);

long lCount = 0;
for (int split = 0; split < index.getTotalShards(); 
 split += conns.length) {
// Gets a connection object from subdirectory split to 
//(split + conns.length)
for (int idx = 0; idx < conns.length; idx++) { 
conns[idx] = index.getOracleSearcherManager(idx + split); 
}

// Gets elements from split to split + conns.length
Iterable<Vertex>[] iterAr 
= index.getPartitioned(conns /* connections */, 
 "name"/* key */, 
 "*" /* value */, 
 true /* wildcards */, 
 split /* start split ID */);

lCount = countFromIterables(iterAr); /* Consume iterables in parallel */

// Do not close the connections to the subdirectories after completion,
// because those connections are used by the LuceneIndex object itself.
}

// Count all vertices
System.out.println("Vertices found using parallel query: " + lCount);

SolrCloudを使用したパラレル・テキスト検索

SolrIndexでメソッドgetPartitionedを呼び出して、SolrCloudへの接続の配列(CloudSolrServerオブジェクト)、検索するキー/値ペア、および開始シャードIDを指定することにより、SolrCloudを使用したパラレル・テキスト問合せを使用できます。

次のコード・フラグメントは、SolrCloud検索エンジンを使用して自動テキスト索引を生成し、パラレル・テキスト問合せを実行します。SolrIndexクラスでのgetPartitionedメソッドへの呼出し数は、検索内のシャードの合計数と使用される接続の数によって制御されます。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(…);

String configName = "opgconfig";
String solrServerUrl = args[4];//"localhost:2181/solr"
String solrNodeSet = args[5]; //"localhost:8983_solr";
 
int zkTimeout = 15; // zookeeper timeout in seconds
int numShards = Integer.parseInt(args[6]); // number of shards in the index
int replicationFactor = 1; // replication factor
int maxShardsPerNode = 1; // maximum number of shards per node
 
// Create an automatic index using SolrCloud
OracleIndexParameters indexParams = 
 OracleIndexParameters.buildSolr(configName, 
 solrServerUrl, 
 solrNodeSet, 
 zkTimeout /* zookeeper timeout in seconds */,
 numShards /* total number of shards */,
 replicationFactor /* Replication factor */,
 maxShardsPerNode /* maximum number of shardsper node*/,
 4 /* dop used for scan */,
 10000 /* batch size before commit*/,
 500000 /* commit size before SolrCloud commit*/,
 15 /* write timeout in seconds */);

opg.setDefaultIndexParameters(indexParams);

// Create auto indexing on name property for all vertices
System.out.println("Create automatic index on name for vertices");
opg.createKeyIndex("name", Vertex.class);

// Get the SolrIndex object 
SearcherManager[] conns = new SearcherManager[dop];
SolrIndex<Vertex> index = (SolrIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Open an array of connections to handle connections to SolrCloud needed for parallel text search
CloudSolrServer[] conns = new CloudSolrServer[dop];

for (int idx = 0; idx < conns.length; idx++) {
conns[idx] = index.getCloudSolrServer(15 /* write timeout in 
secs*/);
}

// Iterate to cover all the shards in the index
long lCount = 0;
for (int split = 0; split < index.getTotalShards(); 
 split += conns.length) {
// Gets elements from split to split + conns.length
Iterable<Vertex>[] iterAr = index.getPartitioned(conns /* connections */, 
 "name"/* key */, 
 "*" /* value */, 
 true /* wildcards */, 
 split /* start split ID */);

lCount = countFromIterables(iterAr); /* Consume iterables in parallel */
}

// Close the connections to the sub-directories after completed
for (int idx = 0; idx < conns.length; idx++) { 
conns[idx].shutdown();
} 

// Count results
System.out.println("Vertices found using parallel query: " + lCount);

Oracle Textを使用したパラレル・テキスト検索

OracleTextAutoIndexのメソッドgetPartitionedを呼び出し、Oracle Textへの接続の配列(Connectionオブジェクト)、検索するキー/値ペア、および開始パーティションIDを使用することで、Oracle Textを使用したパラレル・テキスト問合せを使用できます。

次のコード・フラグメントは、Oracle Textを使用して、自動テキスト索引を生成し、パラレル・テキスト問合せを実行します。OracleTextAutoIndexクラスでのgetPartitionedメソッドへの呼出し数は、VT$ (またはGE$表)のパーティションの合計数と使用される接続の数によって制御されます。

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(…);
String prefOwner = "scott";
String datastore = (String) null;
String filter = (String) null;
String storage = (String) null;
String wordlist = (String) null;
String stoplist = (String) null;
String lexer = "OPG_AUTO_LEXER";
String options = (String) null;

OracleIndexParameters params 
                  = OracleTextIndexParameters.buildOracleText(prefOwner,               
                                                              datastore, 
                                                              filter, 
                                                              storage, 
                                                              wordlist, 
                                                              stoplist, 
                                                              lexer, 
                                                              dop, 
                                                              options);

opg.setDefaultIndexParameters(indexParams);
    

// Create auto indexing on all existing properties, use wildcard for all
opg.createKeyIndex(("*", Vertex.class); 


// Create the text query object for Oracle Text
OracleTextQueryObject otqo 
               = OracleTextQueryObject.getInstance("Obama" /* query body */, 
                                                   1 /* score */, 
                                                   ScoreRange.POSITIVE /* Score range */,   
                                                   Direction.ASC /* order by direction*/);

// Get the Connection object 
Connection[] conns = new Connection[dop];
for (int idx = 0; idx < conns.length; idx++) {
conns[idx] = opg.getOracle().clone().getConnection();
}

// Get the auto index object
OracleIndex<Vertex> index = ((OracleIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Iterate to cover all the partitions in the index
long lCount = 0;
for (int split = 0; split < index.getTotalShards(); 
 split += conns.length) {
  // Gets elements from split to split + conns.length
Iterable<Vertex>[] iterAr = index.getPartitioned(conns /* connections */, 
 "name"/* key */, 
 otqo, 
 true /* wildcards */, 
 split /* start split ID */);

lCount = countFromIterables(iterAr); /* Consume iterables in parallel */
}

// Close the connections
for (int idx = 0; idx < conns.length; idx++) {
conns[idx].dispose();
}

// Count results
System.out.println("Vertices found using parallel query: " + lCount);

2.6.9 プロパティ・グラフ・データのテキスト索引でのネイティブ問合せオブジェクトの使用

問合せオブジェクトを直接使用するのは上級ユーザー向けで、これにより、テキスト検索エンジン(Apache LuceneまたはSolrCloud)の基礎となる問合せ機能を最大限に活用できます。たとえば、一致スコアへのブーストの追加やソート句の追加など、テキスト検索に制約を追加することができます。

問合せオブジェクトを使用したテキスト検索を使用すると、制約を満たしながらテキスト問合せと一致する属性(または属性のセット)を持つすべての頂点(またはエッジ)を保持するIterableオブジェクトを生成します。この方法は、一致スコアに基づき結果を自動的にランク付けします。

問合せ本体に句を作成するには、一致させるキー/値ペアで使用するデータ型と、検索エンジンが使用する構成を考慮する必要がある場合があります。検索用語の作成の詳細は、「データ型の処理」を参照してください。

Apache Luceneを使用したネイティブ問合せオブジェクトの使用

LuceneIndexでメソッドget(Query)を呼び出すことで、Apache Luceneを使用してネイティブ問合せオブジェクトを使用できます。LuceneIndexのメソッドgetPartitioned(SearcherManager[], Query, int)を呼び出し、サブディレクトリのセットへの接続の配列(SearcherManagerオブジェクト)、Lucene問合せオブジェクト、および開始サブディレクトリIDを指定することで、ネイティブ問合せオブジェクトを使用したパラレル・テキスト問合せも使用できます。各サブディレクトリは索引のその他のサブディレクトリに依存していないため、各接続が適切なサブディレクトリにリンクしている必要があります。

次のコード・フラグメントは、Apache Lucene検索エンジンを使用して、自動テキスト索引を生成し、Lucene問合せを作成し、パラレル・テキスト問合せを実行します。LuceneIndexクラスでのgetPartitionedメソッドへの呼出し数は、サブディレクトリの合計数と使用される接続の数によって制御されます。

import oracle.pg.text.lucene.LuceneIndex;
import org.apache.lucene.search.*;
import org.apache.lucene.index.*;

...

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 …);

// Create an automatic index
OracleIndexParameters indexParams = OracleIndexParameters.buildFS(dop /* number of directories */,
dop /* number of connections
used when indexing */,
10000 /* batch size before commit*/,
500000 /* commit size before Lucene commit*/,
true /* enable datatypes */,
"./lucene-index" /* index location */);

opg.setDefaultIndexParameters(indexParams);

// Create auto indexing on name and country properties for all vertices
System.out.println("Create automatic index on name and country for vertices");
String[] indexedKeys = new String[2];
indexedKeys[0]="name";
indexedKeys[1]="country";
opg.createKeyIndex(indexedKeys, Vertex.class);

// Get the LuceneIndex object 
LuceneIndex<Vertex> index = (LuceneIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Search first for Key name with property value Beyon* using only string 
//data types
Term term = index.buildSearchTermObject("name", "Beyo*", String.class);
Query queryBey = new WildcardQuery(term);

// Add another condition to query all the vertices whose country is 
//"United States"
String key = index.appendDatatypesSuffixToKey("country", String.class);
String value = index.appendDatatypesSuffixToValue("United States", String.class);

Query queryCountry = new PhraseQuery();
StringTokenizer st = new StringTokenizer(value);
while (st.hasMoreTokens()) {
  queryCountry.add(new Term(key, st.nextToken()));
};

//Concatenate queries
BooleanQuery bQuery = new BooleanQuery();
bQuery.add(queryBey, BooleanClause.Occur.MUST);
bQuery.add(queryCountry, BooleanClause.Occur.MUST);

long lCount = 0;
SearcherManager[] conns = new SearcherManager[dop];
for (int split = 0; split < index.getTotalShards(); split += conns.length) {
  // Gets a connection object from subdirectory split to 
  //(split + conns.length). Skip the cache so we clone the connection and
  // avoid using the connection used by the index.
  for (int idx = 0; idx < conns.length; idx++) { 
    conns[idx] = index.getOracleSearcherManager(idx + split, 
                                      true /* skip looking in the cache*/                 
); 
  }

  // Gets elements from split to split + conns.length
  Iterable<Vertex>[] iterAr = index.getPartitioned(conns /* connections */, 
                                                   bQuery,  
                                                   split /* start split ID */);

  lCount = countFromIterables(iterAr); /* Consume iterables in parallel */

  // Do not close the connections to the sub-directories after completed,
  // as those connections are used by the index itself

}

// Count all vertices
System.out.println("Vertices found using parallel query: " + lCount);

SolrCloudを使用したネイティブ問合せオブジェクトの使用

SolrIndexでメソッドget(SolrQuery)を呼び出すことで、SolrCloudに対しネイティブ問合せオブジェクトを直接使用できます。SolrIndexでメソッドgetPartitioned(CloudSolrServer[],SolrQuery,int)を呼び出し、SolrCloud (CloudSolrServerオブジェクト)への接続の配列、SolrQueryオブジェクト、および開始シャードIDを指定することで、ネイティブ問合せオブジェクトを使用したパラレル・テキスト問合せも使用できます。

次のコード・フラグメントは、Apache SolrCloud検索エンジンを使用して、自動テキスト索引を生成し、SolrQueryオブジェクトを作成し、パラレル・テキスト問合せを実行します。SolrIndexクラスのgetPartitionedメソッドへのコール数は、サブディレクトリの合計数および使用する接続数により制御されます。

import oracle.pg.text.solr.*;
import org.apache.solr.client.solrj.*;

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 …);

String configName = "opgconfig";
String solrServerUrl = args[4];//"localhost:2181/solr"
String solrNodeSet = args[5]; //"localhost:8983_solr";
 
int zkTimeout = 15; // zookeeper timeout in seconds
int numShards = Integer.parseInt(args[6]); // number of shards in the index
int replicationFactor = 1; // replication factor
int maxShardsPerNode = 1; // maximum number of shards per node
 
// Create an automatic index using SolrCloud
OracleIndexParameters indexParams = 
 OracleIndexParameters.buildSolr(configName, 
 solrServerUrl, 
 solrNodeSet, 
 zkTimeout          /* zookeeper timeout in seconds */,
 numShards          /* total number of shards */,
 replicationFactor  /* Replication factor */,
 maxShardsPerNode   /* maximum number of shardsper node*/,
 4                  /* dop used for scan */,
 10000              /* batch size before commit*/,
 500000             /* commit size before SolrCloud commit*/,
 15                 /* write timeout in seconds */
);

opg.setDefaultIndexParameters(indexParams);

// Create auto indexing on name property for all vertices
System.out.println("Create automatic index on name and country for vertices");
String[] indexedKeys = new String[2];
indexedKeys[0]="name";
indexedKeys[1]="country";
opg.createKeyIndex(indexedKeys, Vertex.class);

// Get the SolrIndex object 
SolrIndex<Vertex> index = (SolrIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Search first for Key name with property value Beyon* using only string 
//data types
String szQueryStrBey = index.buildSearchTerm("name", "Beyo*", String.class);
String key = index.appendDatatypesSuffixToKey("country", String.class);
String value = index.appendDatatypesSuffixToValue("United States", String.class);

String szQueryStrCountry = key + ":" + value;
Solrquery query = new SolrQuery(szQueryStrBey + " AND " + szQueryStrCountry);

//Query using get operation
index.get(query);

// Open an array of connections to handle connections to SolrCloud needed 
// for parallel text search
CloudSolrServer[] conns = new CloudSolrServer[dop];

for (int idx = 0; idx < conns.length; idx++) {
conns[idx] = index.getCloudSolrServer(15 /* write timeout in 
secs*/);
}

// Iterate to cover all the shards in the index
long lCount = 0;
for (int split = 0; split < index.getTotalShards(); 
 split += conns.length) {
// Gets elements from split to split + conns.length
Iterable<Vertex>[] iterAr = index.getPartitioned(conns /* connections */, 
 query, 
 split /* start split ID */);

lCount = countFromIterables(iterAr); /* Consume iterables in parallel */
}

// Close the connections to SolCloud after completion
for (int idx = 0; idx < conns.length; idx++) { 
  conns[idx].shutdown();
}

// Count results
System.out.println("Vertices found using parallel query: " + lCount);

2.6.10 プロパティ・グラフ・データのテキスト索引でのネイティブ問合せ結果の使用

ネイティブ問合せ結果を直接プロパティ・グラフ・データに適用すると、テキスト検索エンジン(Apache LuceneまたはSolrCloud)の問合せ機能を最大限に活用できます。この方法では、ユーザーはテキスト・エンジンで様々な種類の問合(ファセット問合せなど)を実行し、取得した結果を頂点(またはエッジ)オブジェクトに解析できます。

問合せ結果を使用したテキスト検索を使用すると、指定した結果オブジェクトからのすべての頂点(またはエッジ)を保持するIterableオブジェクトを生成します。この方法は、結果セットの順番に基づき結果を自動的にランク付けします。

Apache LuceneまたはSolrCloud索引への索引問合せを直接実行するには、一致させるキー/値ペアで使用するデータ型と、検索エンジンが使用する構成を考慮する必要がある場合があります。検索用語の作成の詳細は、「データ型の処理」を参照してください。

  • Apache Luceneを使用したネイティブ問合せ結果の使用

  • SolrCloudを使用したネイティブ問合せ結果の使用

Apache Luceneを使用したネイティブ問合せ結果の使用

LuceneIndexでメソッドget(TopDocs)を呼び出すことで、Apache Luceneを使用してネイティブ問合せ結果を使用できます。TopDocsオブジェクトは、特定のApache Luceneディレクトリを介したテキスト検索問合せに一致するドキュメントのセットを提供します。LuceneIndexは、TopDocsオブジェクトのドキュメントからのすべての頂点(またはエッジ)を保持するIterableオブジェクトを生成します。

Apache Luceneを使用するOracleプロパティ・グラフ・テキスト索引が、複数のApache Luceneディレクトリを使用して作成されます。索引付けされた頂点およびエッジは、ディレクトリ全体に拡散し、ストレージのスケーラビリティと問合せのパフォーマンスを向上させます。プロパティ・グラフのテキスト索引のすべてのデータに対して問合せを実行する必要がある場合、各Apache Luceneディレクトリに対して問合せを実行する必要があります。API getOracleSearcher in LuceneIndexを使用して、ディレクトリに関連付けられたIndexSearcherオブジェクトを簡単に取得できます。

次のコード・フラグメントは、Apache Lucene検索エンジンを使用して、自動テキスト索引を生成し、Lucene問合せを作成し、TopDocsオブジェクトを取得するためにIndexSearcherに対してそれを実行します。頂点のIterableが、後で、結果オブジェクトから作成されます。

import oracle.pg.text.lucene.LuceneIndex;
import org.apache.lucene.search.*;
import org.apache.lucene.index.*;

...

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 …);

// Create an automatic index
OracleIndexParameters indexParams = OracleIndexParameters.buildFS(dop /* number of directories */,
dop /* number of connections
used when indexing */,
10000 /* batch size before commit*/,
500000 /* commit size before Lucene commit*/,
true /* enable datatypes */,
"./lucene-index" /* index location */);

opg.setDefaultIndexParameters(indexParams);

// Create auto indexing on name and country properties for all vertices
System.out.println("Create automatic index on name and country for vertices");
String[] indexedKeys = new String[2];
indexedKeys[0]="name";
indexedKeys[1]="country";
opg.createKeyIndex(indexedKeys, Vertex.class);

// Get the LuceneIndex object 
LuceneIndex<Vertex> index = (LuceneIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Search first for Key name with property value Beyon* using only string 
//data types
Term term = index.buildSearchTermObject("name", "Beyo*", String.class);
Query queryBey = new WildcardQuery(term);

// Add another condition to query all the vertices whose country is 
//"United States"
String key = index.appendDatatypesSuffixToKey("country", String.class);
String value = index.appendDatatypesSuffixToValue("United States", String.class);

Query queryCountry = new PhraseQuery();
StringTokenizer st = new StringTokenizer(value);
while (st.hasMoreTokens()) {
  queryCountry.add(new Term(key, st.nextToken()));
};

//Concatenate queries
BooleanQuery bQuery = new BooleanQuery();
bQuery.add(queryBey, BooleanClause.Occur.MUST);
bQuery.add(queryCountry, BooleanClause.Occur.MUST);

// Get the IndexSearcher object needed to execute the query. 
// The index searcher object is mapped to a single Apache Lucene directory
SearcherManager searcherMgr = 
         index.getOracleSearcherManager(0, true /* skip looking in the cache*/); 
IndexSearcher indexSearcher = searcherMgr.acquire();
// search for the first 1000 results in the current index directory 0
TopDocs docs = index.search(bQuery, 1000); 

long lCount = 0;
Iterable<Vertex> it = index.get(docs);

while (it.hasNext()) {
  System.out.println(it.next());
  lCount++;
}
System.out.println("Vertices found: "+ lCount);

SolrCloudを使用したネイティブ問合せ結果の使用

SolrIndexでメソッドget(QueryResponse)を呼び出すことで、SolrCloudを使用してネイティブ問合せ結果を使用できます。QueryResponseオブジェクトは、特定のSolrCloudコレクションを介したテキスト検索問合せに一致するドキュメントのセットを提供します。SolrIndexは、QueryResponseオブジェクトのドキュメントからのすべての頂点(またはエッジ)を保持するIterableオブジェクトを生成します。

次のコード・フラグメントは、Apache SolrCloud索エンジンを使用して、SolrQueryオブジェクトを生成し、QueryResponseオブジェクトを取得するためにCloudSolrServerオブジェクトに対してそれを実行します。頂点のIterableが、後で、結果オブジェクトから作成されます。

import oracle.pg.text.solr.*;
import org.apache.solr.client.solrj.*;

OraclePropertyGraph opg = OraclePropertyGraph.getInstance(
 …);

String configName = "opgconfig";
String solrServerUrl = args[4];//"localhost:2181/solr"
String solrNodeSet = args[5]; //"localhost:8983_solr";
 
int zkTimeout = 15; // zookeeper timeout in seconds
int numShards = Integer.parseInt(args[6]); // number of shards in the index
int replicationFactor = 1; // replication factor
int maxShardsPerNode = 1; // maximum number of shards per node
 
// Create an automatic index using SolrCloud
OracleIndexParameters indexParams = 
 OracleIndexParameters.buildSolr(configName, 
 solrServerUrl, 
 solrNodeSet, 
 zkTimeout          /* zookeeper timeout in seconds */,
 numShards          /* total number of shards */,
 replicationFactor  /* Replication factor */,
 maxShardsPerNode   /* maximum number of shardsper node*/,
 4                  /* dop used for scan */,
 10000              /* batch size before commit*/,
 500000             /* commit size before SolrCloud commit*/,
 15                 /* write timeout in seconds */
);

opg.setDefaultIndexParameters(indexParams);

// Create auto indexing on name property for all vertices
System.out.println("Create automatic index on name and country for vertices");
String[] indexedKeys = new String[2];
indexedKeys[0]="name";
indexedKeys[1]="country";
opg.createKeyIndex(indexedKeys, Vertex.class);

// Get the SolrIndex object 
SolrIndex<Vertex> index = (SolrIndex<Vertex>) opg.getAutoIndex(Vertex.class);

// Search first for Key name with property value Beyon* using only string 
//data types
String szQueryStrBey = index.buildSearchTerm("name", "Beyo*", String.class);
String key = index.appendDatatypesSuffixToKey("country", String.class);
String value = index.appendDatatypesSuffixToValue("United States", String.class);

String szQueryStrCountry = key + ":" + value;
Solrquery query = new SolrQuery(szQueryStrBey + " AND " + szQueryStrCountry);

CloudSolrServer conn = index.getCloudSolrServer(15 /* write timeout in 
secs*/);

//Query using get operation
QueryResponse qr = conn.query(query, SolrRequest.METHOD.POST);
Iterable<Vertex> it = index.get(qr);

long lCount = 0;

while (it.hasNext()) {
  System.out.println(it.next());
  lCount++;
}

System.out.println("Vertices found: "+ lCount);

2.7 プロパティ・グラフ・データのアクセス制御(グラフレベルおよびOLS)

Oracle Spatial and Graphのプロパティ・グラフ機能は、2つのアクセス制御およびセキュリティ・モデルをサポートします。グラフ・レベルのアクセス制御と、Oracle Label Security (OLS)と統合されたファイングレイン・セキュリティです。

  • グラフレベルのアクセス制御は、所有者以外のユーザーにプロパティ・ブラフのアクセスを許可/禁止する付与/拒否によって行われます。

  • プロパティ・グラフ・データのOLSにより、重要度ラベルをプロパティ・グラフに格納された個別の頂点またはエッジに関連付けることができます。

Oracle Databaseに格納されたプロパティ・グラフ・データへのデフォルトのアクセス制御はグラフ・レベルです。グラフの所有者はグラフの読取り、挿入、削除、更新および選択の権限を他のユーザーに付与できます。

ただし、厳密なセキュリティ要件を持つアプリケーションの場合は、Oracle DatabaseのOracle Label Securityオプションを使用して、ファイングレイン・アクセス制御メカニズムを実施できます。OLSでは、各問合せに対し、特定の要素(頂点またはエッジ)へのアクセスは、そのラベルとユーザーのラベルを比較することで付与されます。(OLSの使用方法の詳細は、『Oracle Label Security管理者ガイド』を参照してください。)

Oracle Label Securityが有効な場合、要素(頂点またはエッジ)は、より高い重要度ラベルを持つ同じ要素がデータベースに存在すると、グラフに挿入できません。たとえば、( Vertex ID 1 {name:str:v1} "SENSITIVE" )のように非常に機密性の高いラベルを持つ頂点があるとします。( Vertex ID 1 {name:str:v1} "PUBLIC" )は、実際には低い権限(PUBLIC)のユーザーが頂点を更新するのを防ぎます。一方、高い権限のユーザーが、低いレベルのセキュリティ・ラベルで作成された頂点またはエッジを上書きする場合、高いセキュリティの新しいラベルは頂点またはエッジに関連付けられ、低い権限のユーザーはそれを参照できなくなります。

トピック:

2.7.1 Oracle Label Security (OLS)のプロパティ・グラフ・データへの適用

このトピックでは、プロパティ・グラフ・データにOLSを適用する方法を説明する例を示します。

プロパティ・グラフは通常のリレーショナル表に格納されるので、この例は通常のリレーショナル表にOLSを適用するのと違いはありません。次に、OLSの構成および有効化、セキュリティ・ラベルを使用するセキュリティ・ポリシーの作成、およびそれをプロパティ・グラフに適用する方法について示します。このコード例は非常に単純化されており、推奨のプラクティスで使用しているユーザー名およびパスワードをそのまま使用しないでください。

  1. SYSDBAとして、userP、userP2、userS、userTS、userTS2およびpgAdminという名前のデータベース・ユーザーを作成します。

    CONNECT / as sysdba;
    
    CREATE USER userP IDENTIFIED BY userPpass;
    GRANT connect, resource, create table, create view, create any index TO userP;
    GRANT unlimited TABLESPACE to userP;
    
    CREATE USER userP2 IDENTIFIED BY userP2pass;
    GRANT connect, resource, create table, create view, create any index TO userP2;
    GRANT unlimited TABLESPACE to userP2;
    
    CREATE USER userS IDENTIFIED BY userSpass;
    GRANT connect, resource, create table, create view, create any index TO userS;
    GRANT unlimited TABLESPACE to userS;
    
    CREATE USER userTS IDENTIFIED BY userTSpass;
    GRANT connect, resource, create table, create view, create any index TO userTS;
    GRANT unlimited TABLESPACE to userTS;
    
    CREATE USER userTS2 IDENTIFIED BY userTS2pass;
    GRANT connect, resource, create table, create view, create any index TO userTS2;
    GRANT unlimited TABLESPACE to userTS2;
    
    CREATE USER pgAdmin IDENTIFIED BY pgAdminpass;
    GRANT connect, resource, create table, create view, create any index TO pgAdmin;
    GRANT unlimited TABLESPACE to pgAdmin;
    
  2. SYSDBAとして、Oracle Label Securityを構成および有効化します。

    ALTER USER lbacsys IDENTIFIED BY lbacsys ACCOUNT UNLOCK;
    EXEC LBACSYS.CONFIGURE_OLS;
    EXEC LBACSYS.OLS_ENFORCEMENT.ENABLE_OLS;
    
  3. SYSTEMとして、sec_adminおよびhr_secに権限を付与します。

    CONNECT system/<system-password>
    GRANT connect, create any index to sec_admin IDENTIFIED BY password;
    GRANT connect, create user, drop user, create role, drop any role TO hr_sec IDENTIFIED BY password;
    
  4. LBACSYSとして、セキュリティ・ポリシーを作成します。

    CONNECT lbacsys/<lbacsys-password>
    
    BEGIN
    SA_SYSDBA.CREATE_POLICY (
      policy_name => 'DEFENSE',
      column_name => 'SL',
      default_options => 'READ_CONTROL,LABEL_DEFAULT,HIDE');
    END;
    /
    
  5. LBACSYSとして、DEFENSE_DBAおよびexecuteをsec_adminおよびhr_sec usersに付与します。

    GRANT DEFENSE_DBA to sec_admin;
    GRANT DEFENSE_DBA to hr_sec;
    
    GRANT execute on SA_COMPONENTS to sec_admin;
    GRANT execute on SA_USER_ADMIN to hr_sec;
    
  6. SEC_ADMINとして、3つのセキュリティ・レベルを作成します(簡略化のため、コンパートメントおよびグループを省略しています)。

    CONNECT sec_admin/<sec_admin-password>;
    
    BEGIN
    SA_COMPONENTS.CREATE_LEVEL ( 
      policy_name => 'DEFENSE', 
      level_num => 1000,
      short_name => 'PUB',
      long_name => 'PUBLIC'); 
    END;
    /
    EXECUTE SA_COMPONENTS.CREATE_LEVEL('DEFENSE',2000,'CONF','CONFIDENTIAL');
    EXECUTE SA_COMPONENTS.CREATE_LEVEL('DEFENSE',3000,'SENS','SENSITIVE');
    
  7. 3つのラベルを作成します。

    EXECUTE SA_LABEL_ADMIN.CREATE_LABEL('DEFENSE',1000,'PUB');
    EXECUTE SA_LABEL_ADMIN.CREATE_LABEL('DEFENSE',2000,'CONF');
    EXECUTE SA_LABEL_ADMIN.CREATE_LABEL('DEFENSE',3000,'SENS');
    
  8. HR_SECとして、ラベルおよび権限を割り当てます。

    CONNECT hr_sec/<hr_sec-password>;
    
    BEGIN
    SA_USER_ADMIN.SET_USER_LABELS (
      policy_name => 'DEFENSE',
      user_name => 'UT',
      max_read_label => 'SENS',
      max_write_label => 'SENS',
      min_write_label => 'CONF',
      def_label => 'SENS',
      row_label => 'SENS');
    END;
    /
    
    EXECUTE SA_USER_ADMIN.SET_USER_LABELS('DEFENSE', 'userTS', 'SENS');
    EXECUTE SA_USER_ADMIN.SET_USER_LABELS('DEFENSE','userTS2','SENS');
    EXECUTE SA_USER_ADMIN.SET_USER_LABELS('DEFENSE', 'userS', 'CONF');
    EXECUTE SA_USER_ADMIN.SET_USER_LABELS ('DEFENSE', userP', 'PUB', 'PUB', 'PUB', 'PUB', 'PUB');
    EXECUTE SA_USER_ADMIN.SET_USER_LABELS ('DEFENSE', 'userP2', 'PUB', 'PUB', 'PUB', 'PUB', 'PUB');
    EXECUTE SA_USER_ADMIN.SET_USER_PRIVS ('DEFENSE', 'pgAdmin', 'FULL');
    
  9. SEC_ADMINとして、セキュリティ・ポリシーを指定したプロパティ・グラフに適用します。名前がOLSEXAMPLEで、グラフの所有者がuserPのプロパティ・グラフがあるとします。OLSセキュリティを適用するために、次の文を実行します。

    CONNECT sec_admin/welcome1;
    
    EXECUTE SA_POLICY_ADMIN.APPLY_TABLE_POLICY ('DEFENSE', 'userP', 'OLSEXAMPLEVT$');
    EXECUTE SA_POLICY_ADMIN.APPLY_TABLE_POLICY ('DEFENSE', 'userP', 'OLSEXAMPLEGE$');
    EXECUTE SA_POLICY_ADMIN.APPLY_TABLE_POLICY ('DEFENSE', 'userP', 'OLSEXAMPLEGT$');
    EXECUTE SA_POLICY_ADMIN.APPLY_TABLE_POLICY ('DEFENSE', 'userP', 'OLSEXAMPLESS$');
    

これでOracle Label Securityは、プロパティ・グラフに格納された個別の頂点またはエッジに関連付けられた重要度ラベルを持つようになりました。

次の例は、OLSEXAMPLEという名前のプロパティ・グラフの作成方法を示し、異なるセキュリティ・ラベルを持つ別のユーザーのグラフ要素の作成、読取り、書込み時の動作を示すフロー例を示します。

// Create Oracle Property Graph
String graphName = "OLSEXAMPLE";
Oracle connPub = new Oracle("jdbc:oracle:thin:@host:port:SID",  "userP", "userPpass");
OraclePropertyGraph graphPub = OraclePropertyGraph.getInstance(connPub, graphName, 48);

// Grant access to other users
graphPub.grantAccess("userP2",  "RSIUD"); // Read, Select, Insert, Update, Delete (RSIUD)
graphPub.grantAccess("userS",   "RSIUD");
graphPub.grantAccess("userTS",  "RSIUD");
graphPub.grantAccess("userTS2", "RSIUD");
 
// Load data
OraclePropertyGraphDataLoader opgdl = OraclePropertyGraphDataLoader.getInstance();
String vfile = "../../data/connections.opv";
String efile = "../../data/connections.ope";
graphPub.clearRepository();
opgdl.loadData(graphPub, vfile, efile, 48, 1000, true, null);
System.out.println("Vertices with user userP and PUBLIC LABEL: " + graphPub.countVertices()); // 78
System.out.println("Vertices with user userP and PUBLIC LABEL: " + graphPub.countEdges());  // 164

// Second user with a higher level 
Oracle connTS = new Oracle("jdbc:oracle:thin:@host:port:SID", "userTS", "userTpassS");
OraclePropertyGraph graphTS = OraclePropertyGraph.getInstance(connTS, "USERP", graphName, 8, 48, null, null);
System.out.println("Vertices with user userTS and SENSITIVE LABEL: " + graphTS.countVertices()); // 78
System.out.println("Vertices with user userTS and SENSITIVE LABEL: " + graphTS.countEdges());  // 164

// Add vertices and edges with the second user
long lMaxVertexID = graphTS.getMaxVertexID();
long lMaxEdgeID = graphTS.getMaxEdgeID();
long size = 10;
System.out.println("\nAdd " + size + " vertices and edges with user userTS and SENSITIVE LABEL\n");
for (long idx = 1; idx <= size; idx++) {
  Vertex v = graphTS.addVertex(idx + lMaxVertexID);
  v.setProperty("name", "v_" + (idx + lMaxVertexID));
  Edge e = graphTS.addEdge(idx + lMaxEdgeID, v, graphTS.getVertex(idx), "edge_" + (idx + lMaxEdgeID));
}
graphTS.commit();

// User userP with a lower level only sees the original vertices and edges, user userTS can see more
System.out.println("Vertices with user userP and PUBLIC LABEL: " + graphPub.countVertices()); // 78
System.out.println("Vertices with user userP and PUBLIC LABEL: " + graphPub.countEdges());  // 164
System.out.println("Vertices with user userTS and SENSITIVE LABEL: " + graphTS.countVertices()); // 88
System.out.println("Vertices with user userTS and SENSITIVE LABEL: " + graphTS.countEdges());  // 174

// Third user with a higher level 
Oracle connTS2 = new Oracle("jdbc:oracle:thin:@host:port:SID", "userTS2", "userTS2pass");
OraclePropertyGraph graphTS2 = OraclePropertyGraph.getInstance(connTS2, "USERP", graphName, 8, 48, null, null);
System.out.println("Vertices with user userTS2 and SENSITIVE LABEL: " + graphTS2.countVertices()); // 88
System.out.println("Vertices with user userTS2 and SENSITIVE LABEL: " + graphTS2.countEdges());  // 174

// Fourth user with a intermediate level 
Oracle connS = new Oracle("jdbc:oracle:thin:@host:port:SID", "userS", "userSpass");
OraclePropertyGraph graphS = OraclePropertyGraph.getInstance(connS, "USERP", graphName, 8, 48, null, null);
System.out.println("Vertices with user userS and CONFIDENTIAL LABEL: " + graphS.countVertices()); // 78
System.out.println("Vertices with user userS and CONFIDENTIAL LABEL: " + graphS.countEdges());  // 164
   
// Modify vertices with the fourth user
System.out.println("\nModify " + size + " vertices with user userS and CONFIDENTIAL LABEL\n");
for (long idx = 1; idx <= size; idx++) {
  Vertex v = graphS.getVertex(idx);
  v.setProperty("security_label", "CONFIDENTIAL");
}
graphS.commit();

// User userP with a lower level that userS cannot see the new vertices
// Users userS and userTS can see them
System.out.println("Vertices with user userP with property security_label: " + OraclePropertyGraphUtils.size(graphPub.getVertices("security_label", "CONFIDENTIAL"))); // 0
System.out.println("Vertices with user userS with property security_label: " + OraclePropertyGraphUtils.size(graphS.getVertices("security_label", "CONFIDENTIAL"))); // 10
System.out.println("Vertices with user userTS with property security_label: " + OraclePropertyGraphUtils.size(graphTS.getVertices("security_label", "CONFIDENTIAL"))); // 10
System.out.println("Vertices with user userP and PUBLIC LABEL: " + graphPub.countVertices()); // 68
System.out.println("Vertices with user userTS and SENSITIVE LABEL: " + graphTS.countVertices()); // 88

前述の例によって生成される出力は次のとおりです。

Vertices with user userP and PUBLIC LABEL: 78
Vertices with user userP and PUBLIC LABEL: 164
Vertices with user userTS and SENSITIVE LABEL: 78
Vertices with user userTS and SENSITIVE LABEL: 164

Add 10 vertices and edges with user userTS and SENSITIVE LABEL

Vertices with user userP and PUBLIC LABEL: 78
Vertices with user userP and PUBLIC LABEL: 164
Vertices with user userTS and SENSITIVE LABEL: 88
Vertices with user userTS and SENSITIVE LABEL: 174
Vertices with user userTS2 and SENSITIVE LABEL: 88
Vertices with user userTS2 and SENSITIVE LABEL: 174
Vertices with user userS and CONFIDENTIAL LABEL: 78
Vertices with user userS and CONFIDENTIAL LABEL: 164

Modify 10 vertices with user userS and CONFIDENTIAL LABEL

Vertices with user userP with property security_label: 0
Vertices with user userS with property security_label: 10
Vertices with user userTS with property security_label: 10
Vertices with user userP and PUBLIC LABEL: 68
Vertices with user userTS and SENSITIVE LABEL: 88

2.8 プロパティ・グラフ・データでのGroovy Shellの使用

Oracle Spatial and Graphプロパティ・グラフのサポートには、組込みのGroovy Shell (元のGremlin Groovyシェル・スクリプトに基づく)が含まれています。このコマンドライン・シェル・インタフェースを使用して、Java APIを参照できます。

Groovy Shellを起動するには、インストール・ホーム(デフォルトでは$ORACLE_HOME/md/property_graph by default)の下にあるdal/groovyディレクトリに移動します。次に例を示します。

cd $ORACLE_HOME/md/property_graph/dal/groovy/

ここには、Oracleデータベースに接続するためのスクリプトgremlin-opg-rdbms.shが含まれています。

注意:

いくつかのgremlinトラバース例を実行するには、最初に次のインポート操作を行う必要があります。
import com.tinkerpop.pipes.util.structures.*;

次の例は、Oracleデータベースに接続し、グラフ名myGraphを持つOraclePropertyGraphのインスタンスを取得し、いくつかのグラフ・データの例をロードして、頂点およびエッジのリストを取得します。

$ sh ./gremlin-rdbms.sh
 
opg-rdbms> cfg = 
cfg = GraphConfigBuilder.forPropertyGraphRdbms() \
.setJdbcUrl("jdbc:oracle:thin:@127.0.0.1:1521:orcl")\
.setUsername("scott").setPassword("tiger") \
.setName("connections") .setMaxNumConnections(2)\
.setLoadEdgeLabel(false) \
.addEdgeProperty("weight", PropertyType.DOUBLE, "1000000") \
.build();

opg-rdbms> opg = OraclePropertyGraph.getInstance(cfg);
==>oraclepropertygraph with name myGraph
 
opg-rdbms> opgdl = OraclePropertyGraphDataLoader.getInstance();
==>oracle.pg.nosql.OraclePropertyGraphDataLoader@576f1cad
 
opg-rdbms> opgdl.loadData(opg, new FileInputStream("../../data/connections.opv"), new FileInputStream("../../data/connections.ope"), 4/*dop*/, 1000/*iBatchSize*/, true /*rebuildIndex*/, null /*szOptions*/); ==>null
 
opg-rdbms> opg.getVertices();
==>Vertex ID 5 {country:str:Italy, name:str:Pope Francis, occupation:str:pope, religion:str:Catholicism, role:str:Catholic religion authority}
[... other output lines omitted for brevity ...]
 
opg-rdbms>  opg.getEdges();
==>Edge ID 1139 from Vertex ID 64 {country:str:United States, name:str:Jeff Bezos, occupation:str:business man} =[leads]=> Vertex ID 37 {country:str:United States, name:str:Amazon, type:str:online retailing} edgeKV[{weight:flo:1.0}]
[... other output lines omitted for brevity ...]

次の例は、インメモリー分析用のいくつかの構成パラメータをカスタマイズします。ここでは、Oracleデータベースに接続し、グラフ名myGraphを持つOraclePropertyGraphのインスタンスを取得し、いくつかのグラフ・データの例をロードし、頂点およびエッジのリストを取得し、インメモリー・アナリストを取得して、組込み分析の1つであるトライアングル・カウンティングを実行します。

$ sh ./gremlin-opg-rdbms.sh
opg-rdbms>
opg-rdbms> dop=2;   // degree of parallelism
==>2
opg-rdbms> confPgx = new HashMap<PgxConfig.Field, Object>();
opg-rdbms> confPgx.put(PgxConfig.Field.ENABLE_GM_COMPILER, false);
==>null
opg-rdbms> confPgx.put(PgxConfig.Field.NUM_WORKERS_IO, dop);
==>null
opg-rdbms> confPgx.put(PgxConfig.Field.NUM_WORKERS_ANALYSIS, 3);
==>null
opg-rdbms> confPgx.put(PgxConfig.Field.NUM_WORKERS_FAST_TRACK_ANALYSIS, 2);
==>null
opg-rdbms> confPgx.put(PgxConfig.Field.SESSION_TASK_TIMEOUT_SECS, 0);
==>null
opg-rdbms> confPgx.put(PgxConfig.Field.SESSION_IDLE_TIMEOUT_SECS, 0);
==>null
opg-rdbms> instance = Pgx.getInstance()
==>null
opg-rdbms> instance.startEngine(confPgx) 
==>null

opg-rdbms> 
cfg = GraphConfigBuilder.forPropertyGraphRdbms() \
.setJdbcUrl("jdbc:oracle:thin:@127.0.0.1:1521:orcl")\
.setUsername("scott").setPassword("tiger") \
.setName("connections") .setMaxNumConnections(2)\
.setLoadEdgeLabel(false) \
.addEdgeProperty("weight", PropertyType.DOUBLE, "1000000") \
.build(); 
opg-rdbms> opg = OraclePropertyGraph.getInstance(cfg);  
==>oraclepropertygraph with name myGraph
 
opg-rdbms> opgdl = OraclePropertyGraphDataLoader.getInstance();
==>oracle.pg.hbase.OraclePropertyGraphDataLoader@3451289b
 
opg-rdbms> opgdl.loadData(opg, "../../data/connections.opv", "../../data/connections.ope", 4/*dop*/, 1000/*iBatchSize*/, true /*rebuildIndex*/, null /*szOptions*/);
==>null
 
opg-rdbms> opg.getVertices();
==>Vertex ID 78 {country:str:United States, name:str:Hosain Rahman, occupation:str:CEO of Jawbone}
...
 
opg-rdbms> opg.getEdges();
==>Edge ID 1139 from Vertex ID 64 {country:str:United States, name:str:Jeff Bezos, occupation:str:business man} =[leads]=> Vertex ID 37 {country:str:United States, name:str:Amazon, type:str:online retailing} edgeKV[{weight:flo:1.0}]
[... other output lines omitted for brevity ...]
 
opg-rdbms> session = Pgx.createSession("session-id-1");
opg-rdbms> g = session.readGraphWithProperties(cfg);
opg-rdbms> analyst = session.createAnalyst();
 
opg-rdbms>  triangles = analyst.countTriangles(false).get();
==>22

Java APIの詳細は、Javadoc参照情報を参照してください。

2.9 RDFグラフでのプロパティ・グラフ・ビューの作成

Oracle Spatial and Graphを使用して、プロパティ・グラフとしてRDFデータを表示し、Oracle Databaseに格納されたRDFグラフを介して、プロパティ・グラフ・ビューを作成することで、グラフの分析操作を実行できます。

RDFモデル(または仮想モデル)を指定して、プロパティ・グラフ機能では2つのビュー(頂点用の<graph_name>VT$ビューおよびエッジ用の<graph_name>GE$ビュー)が作成されます。

PGUtils.createPropertyGraphViewOnRDFメソッドにより、RDFデータに対するプロパティ・グラフ・ビューをカスタマイズできます。

public static void createPropertyGraphViewOnRDF( Connection conn /* a Connection instance to Oracle database */,
      String pgGraphName /* the name of the property graph to be created */,
      String rdfModelName /* the name of the RDF model */,
      boolean virtualModel /* a flag represents if the RDF model
                              is virtual model or not; 
                              true – virtual mode, false – normal model*/,
      RDFPredicate[] predListForVertexAttrs /* an array of RDFPredicate objects specifying how to create vertex view using these predicates; each RDFPredicate includes two fields: an URL of the RDF predicate, the corresponding name of vertex key in the Property Graph. The mapping from RDF predicates to vertex keys will be created based on this parameter.  */,
      RDFPredicate[] predListForEdges /* an array of RDFPredicate specifying how to create edge view using these predicates; each RDFPredicate includes two (or three) fields: an URL of the RDF predicate, the edge label in the Property Graph, the weight of the edge (optional). The mapping from RDF predicates to edges will be created based on this parameter. */)

この操作には、プロパティ・グラフの名前、プロパティ・グラフ・ビューを生成するのに使用するRDFモデルの名前、およびトリプルを頂点またはエッジに解析する方法を決定するマッピングのセットが必要です。createPropertyGraphViewOnRDFメソッドには、RDF述語の頂点用のキー/値プロパティへのマッピング方法を指定するキー/値マッピング配列と、RDF述語のエッジへのマップ方法を指定するエッジ・マッピング配列が必要です。PGUtils.RDFPredicate APIによりRDFアサーションから頂点/エッジへのマップを作成できます。

キー/値マッピングの少なくとも1つのRDF述語に一致するトリプルに基づき、頂点が作成されます。マッピング配列で定義されたRDF述語のいずれかを満たす各トリプルは、トリプルのサブジェクトの内部RDFリソースIDに基づくIDと、自身のマッピングにより定義されたキーおよびトリプルのオブジェクトから取得した値を持つキー/値ペアを持つ頂点へと解析されます。

次の例は、プロパティ名がtitleのキー/値プロパティへのRDF述語URI http://purl.org/dc/elements/1.1/titleのキー/値マッピングを定義します。

String titleURL = "http://purl.org/dc/elements/1.1/title";
// create an RDFPredicate to specify how to map the RDF predicate to vertex keys
RDFPredicate titleRDFPredicate 
              = RDFPredicate.getInstance(titleURL /* RDF Predicate URI */ , 
                                         "title" /* property name */);

エッジのマッピング配列の少なくとも1つのRDF述語に一致するトリプルに基づき、エッジが作成されます。マッピング配列で定義されたRDF述語を満たす各トリプルは、行番号に基づくID、自身のマッピングにより定義されたエッジ・ラベル、トリプルのサブジェクトのRDFリソースIDから取得した出力頂点、トリプルのオブジェクトのRDFリソースIDから取得した入力頂点を持つエッジに解析されます。ここで解析される各トリプルに対し、キー/値マッピングから生成されていない場合は、2つの頂点が作成されます。

次の例は、RDF述語URI http://purl.org/dc/elements/1.1/referenceのラベルreferencesで重みが0.5dのエッジへのエッジ・マッピングを定義します。

String referencesURL = "http://purl.org/dc/terms/references";
// create an RDFPredicate to specify how to map the RDF predicate to edges
RDFPredicate referencesRDFPredicate    
                      = RDFPredicate.getInstance(referencesURL, "references", 0.5d);

次の例は、様々なパブリケーション、作成者および参照を記述する、RDFモデルarticlesに対するプロパティ・グラフ・ビューを作成します。生成されたプロパティ・グラフには、titleおよびcreatorを含む、キー/値プロパティを持つ頂点が含まれます。このプロパティ・グラフのエッジは、パブリケーション間の参照により決定されます。

Oracle oracle = null;
Connection conn = null;
OraclePropertyGraph pggraph = null;
try {
  // create the connection instance to Oracle database
  OracleDataSource ds = new oracle.jdbc.pool.OracleDataSource();
  ds.setURL(jdbcUrl);
  conn = (OracleConnection) ds.getConnection(user, password);
 	
  // define some string variables for RDF predicates
  String titleURL = "http://purl.org/dc/elements/1.1/title";
  String creatorURL = "http://purl.org/dc/elements/1.1/creator";
  String serialnumberURL = "http://purl.org/dc/elements/1.1/serialnumber";
  String widthURL = "http://purl.org/dc/elements/1.1/width";
  String weightURL = "http://purl.org/dc/elements/1.1/weight";
  String onsaleURL = "http://purl.org/dc/elements/1.1/onsale";
  String publicationDateURL = "http://purl.org/dc/elements/1.1/publicationDate";
  String publicationTimeURL = "http://purl.org/dc/elements/1.1/publicationTime";
  String referencesURL = "http://purl.org/dc/terms/references";
     
  // create RDFPredicate[] predsForVertexAttrs to specify how to map 
  // RDF predicate to vertex keys
  RDFPredicate[] predsForVertexAttrs = new RDFPredicate[8];
  predsForVertexAttrs[0] = RDFPredicate.getInstance(titleURL, "title");
  predsForVertexAttrs[1] = RDFPredicate.getInstance(creatorURL, "creator");
  predsForVertexAttrs[2] = RDFPredicate.getInstance(serialnumberURL, 
                                                    "serialnumber");
  predsForVertexAttrs[3] = RDFPredicate.getInstance(widthURL, "width");
  predsForVertexAttrs[4] = RDFPredicate.getInstance(weightURL, "weight");
  predsForVertexAttrs[5] = RDFPredicate.getInstance(onsaleURL, "onsale");
  predsForVertexAttrs[6] = RDFPredicate.getInstance(publicationDateURL, 
                                                    "publicationDate");
  predsForVertexAttrs[7] = RDFPredicate.getInstance(publicationTimeURL, 
                                                    "publicationTime");

  // create RDFPredicate[] predsForEdges to specify how to map RDF predicates to 
  // edges
  RDFPredicate[] predsForEdges = new RDFPredicate[1];
  predsForEdges[0] = RDFPredicate.getInstance(referencesURL, "references", 0.5d);
      
  // create PG view on RDF model
  PGUtils.createPropertyGraphViewOnRDF(conn, "articles", "articles", false, 
                                       predsForVertexAttrs, predsForEdges);

  // get the Property Graph instance
  oracle = new Oracle(jdbcUrl, user, password);
  pggraph = OraclePropertyGraph.getInstance(oracle, "articles", 24);

  System.err.println("------ Vertices from property graph view ------");
  pggraph.getVertices();
  System.err.println("------ Edges from property graph view ------");
  pggraph.getEdges();
}
finally {
  pggraph.shutdown();
  oracle.dispose();
  conn.close();
}

articles RDFモデル(11トリプル)の次のトリプルを指定すると、出力のプロパティ・グラフには2つの頂点、1つは<http://nature.example.com/Article1> (v1)用、およびもう1つは <http://nature.example.com/Article2> (v2)用が含まれます。頂点v1の場合、8つのプロパティを持ち、その値はRDF述語と同じです。たとえば、v1のタイトルは“All about XYZ”です。同様に、頂点v2には、2つのプロパティ、titleおよびcreatorがあります。出力プロパティ・グラフには、エッジ・ラベル“references”で重みが0.5dの、頂点v1から頂点v2への1つのエッジ(eid:1)が含まれます。

<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/title> “All about XYZ”^^xsd:string.
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/creator> “Jane Smith”^^xsd:string. 
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/serialnumber> “123456”^^xsd:integer.
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/width> “10.5”^^xsd:float.
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/weight> “1.08”^^xsd:double. 
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/onsale> “false”^^xsd:boolean. 
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/publicationDate> “2016-03-08”^^xsd:date) 
<http://nature.example.com/Article1> <http://purl.org/dc/elements/1.1/publicationTime> “2016-03-08T10:10:10”^^xsd:dateTime) 
<http://nature.example.com/Article2> <http://purl.org/dc/elements/1.1/title> “A review of ABC”^^xsd:string.
<http://nature.example.com/Article2> <http://purl.org/dc/elements/1.1/creator> “Joe Bloggs”^^xsd:string.
<http://nature.example.com/Article1> <http://purl.org/dc/terms/references> <http://nature.example.com/Article2>.

前述のコードは次のような出力を生成します。内部RDFリソースID値は、異なるOracleデータベースの間では変動する可能性があります。

------ Vertices from property graph view ------
Vertex ID 7299961478807817799 {creator:str:Jane Smith, onsale:bol:false, publicationDate:dat:Mon Mar 07 16:00:00 PST 2016, publicationTime:dat:Tue Mar 08 02:10:10 PST 2016, serialnumber:dbl:123456.0, title:str:All about XYZ, weight:dbl:1.08, width:flo:10.5}
Vertex ID 7074365724528867041 {creator:str:Joe Bloggs, title:str:A review of ABC}
------ Edges from property graph view ------
Edge ID 1 from Vertex ID 7299961478807817799 {creator:str:Jane Smith, onsale:bol:false, publicationDate:dat:Mon Mar 07 16:00:00 PST 2016, publicationTime:dat:Tue Mar 08 02:10:10 PST 2016, serialnumber:dbl:123456.0, title:str:All about XYZ, weight:dbl:1.08, width:flo:10.5} =[references]=> Vertex ID 7074365724528867041 {creator:str:Joe Bloggs, title:str:A review of ABC} edgeKV[{weight:dbl:0.5}]

2.10 2表スキーマを使用したプロパティ・グラフの処理

比較的固定で、シンプルなデータ構造のプロパティ・グラフで、頂点およびエッジの<graph_name>VT$および<graph_name>GE$ キー/値データ表に柔軟性が必要ない場合、2表スキーマを使用してランタイム・パフォーマンスを改善することができます。

2表スキーマのアプローチは、(Oracle Databaseのプロパティ・グラフ・スキーマ・オブジェクトで説明されている)プロパティ・グラフ・スキーマを使用するかわりに使用できます。

  • プロパティ・グラフ・スキーマ・アプローチは、主に異機種間または大きなグラフ(あるいはその両方)のために設計されています。同じプロパティ名の新しい関係および場合によっては新規データ型がオンザフライでグラフ・モデルに導入および追加された動的アプリケーション・ドメインを示すためにグラフ・モデルが使用されている場合、プロパティ・グラフ・スキーマを使用することをお薦めします。

    同じプロパティ名の新しい関係および場合によっては新規データ型がオンザフライでグラフ・モデルに導入および追加された動的アプリケーション・ドメインを示すためにグラフ・モデルが使用されている場合、プロパティ・グラフ・スキーマを使用することをお薦めします。

  • 2表スキーマのアプローチは、同種のグラフ用に設計されています。

    グラフ・モデルが一連の関係がすでに認識されているアプリケーション・ドメインを表し、個別の関係の合計数が比較的小さい場合(1000未満)、2表アプローチを使用することをお薦めします。この状況は、通常、元のデータ・ソースが1つまたは一連の既存のリレーショナル表またはリレーショナル・ビューからの場合に発生します。

2表アプローチが有効である可能性のある例として、すべてのノードが特定の組織の従業員であり、各従業員には限定され固定された属性のセットと使用可能なリレーションシップがある場合があります。2表アプローチが有効でない例として、ノードが様々な属性とリレーションシップを持つ可能性のある個人であり、属性やリレーションシップが動的に追加および変更される可能性がある場合があります。

柔軟なキー/値アプローチ(2表ではない)では、Oracle Spatial and Graphはプロパティ・グラフ・データを、頂点用に<graph_name>VT$、エッジ用に<graph_name>GE$という、柔軟なスキーマで格納します。このスキーマでは、頂点およびエッジは複数の行を使用して格納されます。各行は頂点(またはエッジ)に関係付けられたキー/値プロパティを表し、柔軟なデータ型を持ち、属性T (タイプ)により決定されます。このスキーマ・デザインでは、頂点(エッジ)が様々なプロパティ・セットまたはプロパティ値のデータ型を持つ、異機種間グラフに容易に対応できます。

一方、同種構造のプロパティ・グラフの場合、2表スキーマを使用してグラフ・データを格納できます。このアプローチでは、各頂点は名前付けされた頂点表に単一行として格納され、各エッジは名前付けされたエッジ表の単一行として格納されます。この方法では、行の各列は固定データ型のプロパティに対応します。インメモリー・アナリストはこのアプローチを使用してインメモリー・グラフを作成および管理できます。

注意:

2表アプローチは、主に既存のブループリントに基づいたJava APIにインメモリー・アナリストのグラフ・データを提供するためであり、2表アプローチではテキストの索引付けは機能しません

プロパティ・グラフ・スキーマ・アプローチが使用される場合のみ、グラフ・データ・チェンジ・トラッキングを使用できます。

次のトピックは、2表スキーマを使用したプロパティ・グラフの作成方法と、このデータに対する読取りおよび書込み操作の実行方法について焦点を当てます。

トピック:

2.10.1 2表スキーマの準備

OraclePropertyGraphUtils.prepareTwoTablesGraphVertexTabにより、2表スキーマを使用して頂点表のスキーマをカスタマイズして、すべての頂点をグラフに格納することができます。この操作には、Oracleデータベース、表の所有者、表名、およびプロパティ名とデータ型を指定する2つの配列に対する接続が必要です。デフォルトで、生成された表の表スキーマには、属性VIDが含まれ、これは表の主キーを表し、頂点IDにマッピングされます。

次のコード・スニペットは、2表スキーマを使用して頂点表を作成します。この場合、生成された表employeesNodesには4つの属性が含まれます。nameageaddressおよびSSN (Social Security Number)です。頂点表の主キーは生成された属性VIDです。

import oracle.pgx.common.types.PropertyType;
List<String> propertyNames = new ArrayList<String>();
propertyNames.addAll(new String[4]{ "name", "age", "address", "SSN" });

List<PropertyType> = new ArrayList<PropertyType>();
propertyType.add(PropertyType.STRING);
propertyType.add(PropertyType.INTEGER);
propertyType.add(PropertyType.STRING);
propertyType.add(PropertyType.STRING);

OraclePropertyGraphUtils.prepareTwoTablesGraphVertexTab(conn /* Connection object */,
                                            pg /* table owner */, 
                                            "employeesNodes" /* vertex table name */, 
                                            propertyNames /* property names */, 
                                            propertyTypes /* property data types */,
                                            "pgts" /* table space */, 
                                            null /* storage options */, 
                                            true /* no logging */);

前述のコードは次のような表スキーマを生成します。

CREATE TABLE employeenodes
( VID number not null,
  NAME nvarchar2(15000),
  AGE integer,
  ADDRESS nvarchar2(15000),
  SSN nvarchar2(15000),  
  CONSTRAINT employenodes_pk PRIMARY KEY (VID)
);

同様に、OraclePropertyGraphUtils.prepareTwoTablesGraphEdgeTabにより、2表スキーマを使用してエッジ表のスキーマをカスタマイズして、すべてのエッジをグラフに格納することができます。この操作には、Oracleデータベース、表の所有者、表名、およびプロパティ名とデータ型を指定する2つの配列に対する接続が必要です。デフォルトで、生成された表の表スキーマには、次の属性が含まれます。EID、表の主キーを表し、エッジIDにマップされます。EL、エッジ・ラベルにマップされます。SVIDおよびDVID、それぞれ出力および入力頂点IDです。

次のコード・スニペットは、2表スキーマを使用してエッジ表を作成します。この場合、生成された表organizationEdgesにはweightという名前の属性が含まれます。頂点表の主キーは、生成された属性EIDで、これは表スキーマのデフォルトの属性で、頂点ID(long値)の値にマッピングされます。

import oracle.pgx.common.types.PropertyType;
List<String> propertyNames = new ArrayList<String>();
propertyNames.addAll(new String[1]{ "weight" });

List<PropertyType> = new ArrayList<PropertyType>();
propertyType.add(PropertyType.DOUBLE);
OraclePropertyGraphUtils.prepareTwoTablesGraphEdgeTab(conn /* Connection object */,
                                            pg /* table owner */, 
                                            organizationEdges" /* edge table name */, 
                                            propertyNames /* property names */, 
                                            propertyTypes /* property data types */,
                                            "pgts" /* table space */, 
                                            null /* storage options */, 
                                            true /* no logging */);

前述のコードは次のような表構造を生成します。

CREATE TABLE organizationedges
( EID number not null,
  SVID number not null,
  DVID number not null,
  EL nvarchar2(3100),
  WEIGHT number,
  CONSTRAINT organizationedges_pk PRIMARY KEY (EID)
);

表がすでに存在する場合、prepareTwoTablesGraphEdgeTabprepareTwoTablesGraphEdgeTabの両方は表コンテンツを切り捨てます。

2.10.2 2表スキーマを使用したプロパティ・グラフへのデータの格納

2表スキーマを使用して頂点のセットを頂点表にロードするには、API OraclePropertyGraphUtils.writeTwoTablesGraphVertexAndPropertiesを使用できます。この操作は、TinkerPop Blueprints頂点オブジェクトのIterable(またはIterator)の配列を取り込み、頂点表スキーマで定義されたプロパティのIDと値を読み取ります。この情報に基づき、頂点が新しい行として頂点表に後で挿入されます。頂点にスキーマで定義されたプロパティが含まれていない場合、関連する列の値はNULLに設定されます。

次のコード・スニペットは、OraclePropertyGraph APIを使用して、プロパティ・グラフemployeesGraphDALを作成し、2つの頂点と1つのエッジをロードします。次に、2表スキーマを使用して、頂点表employeesNodesを作成し、それにemployeesGraphDALの頂点からのデータを移入します。頂点v1のプロパティemailは、スキーマで定義されていないため、employeesNode表にロードされないことに注意してください。また、頂点v2のプロパティSSNは、頂点で定義されていないため、NULLに設定されます。

// Create employeesGraphDAL
import oracle.pg.rdbms.*;
Oracle oracle = new Oracle(jdbcURL, username, password);
OraclePropertyGraph opgEmployees
                  = OraclePropertyGraph.getInstance(oracle, "employeesGraphDAL");

// Create vertex v1 and assign it properties as key-value pairs
Vertex v1 = opgEmployees.addVertex(1l);
v1.setProperty("age",  Integer.valueOf(31));
v1.setProperty("name", "Alice");
v1.setProperty("address", "Main Street 12");
v1.setProperty("email", "alice@mymail.com");
v1.setProperty("SSN", "123456789");

Vertex v2 = opgEmployees.addVertex(2l);
v2.setProperty("age",  Integer.valueOf(27));
v2.setProperty("name", "Bob");
v2.setProperty("adress", "Sesame Street 334"); 
       
// Add edge e1
Edge e1 = opgEmployees.addEdge(1l, v1, v2, "managerOf");
e1.setProperty("weight", 0.5d);

opgEmployees.commit();

// Prepare the vertex table using a Two Tables schema
import oracle.pgx.common.types.PropertyType;
List<String> propertyNames = new ArrayList<String>();
propertyNames.addAll(new String[4]{ "name", "age", "address", "SSN" });

List<PropertyType> = new ArrayList<PropertyType>();
propertyType.add(PropertyType.STRING);
propertyType.add(PropertyType.INTEGER);
propertyType.add(PropertyType.STRING);
propertyType.add(PropertyType.STRING);

Connection conn 
     = opgEmployees.getOracle().clone().getConnection(); /* Clone the connection
                                                            from the property graph 
                                                            instance */    
OraclePropertyGraphUtils.prepareTwoTablesGraphVertexTab(conn /* Connection object */,
                                            pg /* table owner */, 
                                            "employeesNodes" /* vertex table name */, 
                                            propertyNames /* property names */, 
                                            propertyTypes /* property data types */,
                                            "pgts" /* table space */, 
                                            null /* storage options */, 
                                            true /* no logging */);

// Get the vertices from the employeesDAL graph
Iterable<Vertex> vertices = opgEmployees.getVertices();

// Load the vertices into the vertex table using a Two-Tables schema
Connection[] conns = new Connection[1]; /* the connection array size defines the
                                           Degree of parallelism (multithreading)
                                        */
conns[1] = conn;
OraclePropertyGraphUtils.writeTwoTablesGraphVertexAndProperties(
                                           conn /* Connectionobject */,
                                            pg /* table owner */, 
                                            "employeesNodes" /* vertex table name */,  
                                            1000 /* batch size*/, 
                                            new Iterable[] {vertices} /* array of 
                                                                vertex iterables */); 

2表スキーマを使用してエッジのセットをエッジ表にロードするには、API OraclePropertyGraphUtils.writeTwoTablesGraphEdgesAndPropertiesを使用できます。この操作は、Blueprintsエッジ・オブジェクトのIterable(またはIterator)の配列を取り込み、エッジ表スキーマで定義されたプロパティのID、EL、SVID、DVIDと値を読み取ります。この情報に基づき、エッジが新しい行としてエッジ表に後で挿入されます。エッジにスキーマで定義されたプロパティが含まれていない場合、指定した列の値はNULLに設定されます。

次のコード・スニペットは、OraclePropertyGraph APIを使用して、プロパティ・グラフemployeesGraphDALを作成し、2つの頂点と1つのエッジをロードします。次に、2表スキーマを使用して、頂点表organizationEdgesを作成し、それにemployeesGraphDALのエッジからのデータを移入します。

// Create employeesGraphDAL
import oracle.pg.rdbms.*;
Oracle oracle = new Oracle(jdbcURL, username, password);
OraclePropertyGraph opgEmployees
                  = OraclePropertyGraph.getInstance(oracle, "employeesGraphDAL");

// Create vertex v1 and assign it properties as key-value pairs
Vertex v1 = opgEmployees.addVertex(1l);
v1.setProperty("age",  Integer.valueOf(31));
v1.setProperty("name", "Alice");
v1.setProperty("address", "Main Street 12");
v1.setProperty("email", "alice@mymail.com");
v1.setProperty("SSN", "123456789");

Vertex v2 = opgEmployees.addVertex(2l);
v2.setProperty("age",  Integer.valueOf(27));
v2.setProperty("name", "Bob");
v2.setProperty("adress", "Sesame Street 334"); 
       
// Add edge e1
Edge e1 = opgEmployees.addEdge(1l, v1, v2, "managerOf");
e1.setProperty("weight", 0.5d);

opgEmployees.commit();

// Prepare the edge table using a Two Tables schema
import oracle.pgx.common.types.PropertyType;
       Connection conn 
            = opgEmployees.getOracle().clone().getConnection(); /* Clone the connection
                                                                   from the property graph 
                                                                   instance */    
List<String> propertyNames = new ArrayList<String>();
propertyNames.addAll(new String[1]{ "weight" });

List<PropertyType> = new ArrayList<PropertyType>();
propertyType.add(PropertyType.DOUBLE);
OraclePropertyGraphUtils.prepareTwoTablesGraphEdgeTab(conn /* Connection object */,
                                            pg /* table owner */, 
                                            organizationEdges" /* edge table name */, 
                                            propertyNames /* property names */, 
                                            propertyTypes /* property data types */,
                                            "pgts" /* table space */, 
                                            null /* storage options */, 
                                            true /* no logging */);

// Get the edges from the employeesDAL graph
Iterator<Edge> edges = opgEmployees.getEdges().iterator();

// Load the edges into the edges table using a Two-Tables schema
Connection[] conns = new Connection[1]; /* the connection array size defines the
                                           Degree of parallelism (multithreading)
                                        */
conns[1] = conn;
OraclePropertyGraphUtils.writeTwoTablesGraphVertexAndProperties(conn /* Connection 
                                                                        object */,
                                            pg /* table owner */, 
                                            "organizationEdges" /* edge table 
                                                                   name */,  
                                            1000 /* batch size*/, 
                                            new Iterator[] {edges} /* array of 
                                                           iterator of edges */); 

格納操作のパフォーマンスを最適化するために、writeTwoTablesGraph APIを呼び出すときに、フラグやヒントのセットを指定できます。これらのヒントとしては次のものがあります。

  • DOP: 並列度。接続配列のサイズが、データのロード時に使用する並列度を定義します。これは、データを表にロードするときに使用するローダー・スレッドの数と同様に、Iterableの読取り時に生成するチャンクの数を決定します。

  • バッチ・サイズ: バッチ・モードでOracleのupdate文で使用するバッチ・サイズを指定する整数。推奨バッチ・サイズは1000です。

2.10.3 2表スキーマを使用したプロパティ・グラフからのデータの読取り

2表スキーマを使用して頂点表から頂点のサブセットを読み取るには、API OraclePropertyGraphUtils.readTwoTablesGraphVertexAndPropertiesを使用できます。この操作は、頂点表の対応する分割で検出されたすべての行を含む、ResultSetオブジェクトの配列を返します。配列の各ResultSetオブジェクトは、頂点行を対応する分割からフェッチするために提供された接続のうちいずれかを使用します。分割は、指定した分割の合計数によって決定されます。

整数ID([0, N - 1]の範囲)がN分割された頂点表の分割に割り当てられます。この方法では、問合せされた分割のサブセットは、開始分割IDと開始分割IDに接続配列のサイズを足したものの間の範囲のID値を持つこれらの分割から構成されます。合計が分割の合計数よりも大きい場合、問合せされた分割のサブセットは、[開始分割ID, N - 1]の範囲のIDを持つ分割から構成されます。

次のコードは、合計で1分割を利用し、2表スキーマを使用して、頂点表からすべての頂点を読み取ります。OraclePropertyGraphでAPIを実行することで、Blueprints Vertex Iterableの配列を容易に作成できることに注意してください。取得された頂点には、頂点表スキーマで定義されたすべてのプロパティが含まれます。

ResultSet[] rsAr = readTwoTablesGraphVertexAndProperties(conns, 
                                                 "pg" /* table owner */,
                                                 "employeeNodes /* vertex table 
                                                                   name */,                                                                                              
                                                 1 /* Total Splits*/, 
                                                 0 /* Start Split);

Iterable<Vertex>[] vertices = getVerticesPartitioned(rsAr /* ResultSet array */,
                                                     true /* skip store to cache */,
                                                     null /* vertex filter 
                                                              callback */,
                                                     null /* optimization flag */);

読取りのパフォーマンスを最適化するために、表から読み取られる各頂点に対し取得するプロパティ名のリストを指定できます。

次のコードは、OraclePropertyGraph APIを使用して、プロパティ・グラフemployeesGraphDALを作成し、2つの頂点と1つのエッジをロードします。次に、2表スキーマを使用して、頂点表employeNodesを作成し、それにemployeesGraphDALの頂点からのデータを移入します。最後に、名前プロパティだけを使用して、頂点表から頂点を読み取ります。

// Create employeesGraphDAL
import oracle.pg.rdbms.*;
Oracle oracle = new Oracle(jdbcURL, username, password);
OraclePropertyGraph opgEmployees
                  = OraclePropertyGraph.getInstance(oracle, "employeesGraphDAL");

// Create vertex v1 and assign it properties as key-value pairs
Vertex v1 = opgEmployees.addVertex(1l);
v1.setProperty("age",  Integer.valueOf(31));
v1.setProperty("name", "Alice");
v1.setProperty("address", "Main Street 12");
v1.setProperty("email", "alice@mymail.com");
v1.setProperty("SSN", "123456789");
  
Vertex v2 = opgEmployees.addVertex(2l);
v2.setProperty("age",  Integer.valueOf(27));
v2.setProperty("name", "Bob");
v2.setProperty("adress", "Sesame Street 334"); 
      
// Add edge e1
Edge e1 = opgEmployees.addEdge(1l, v1, v2, "managerOf");
e1.setProperty("weight", 0.5d);

opgEmployees.commit();

// Prepare the vertex table using a Two Tables schema
import oracle.pgx.common.types.PropertyType;
List<String> propertyNames = new ArrayList<String>();
propertyNames.addAll(new String[4]{ "name", "age", "address", "SSN" });

List<PropertyType> = new ArrayList<PropertyType>();
propertyType.add(PropertyType.STRING);
propertyType.add(PropertyType.INTEGER);
propertyType.add(PropertyType.STRING);
propertyType.add(PropertyType.STRING);

Connection conn 
     = opgEmployees.getOracle().clone().getConnection(); /* Clone the connection
                                                            from the property graph 
                                                            instance */    
OraclePropertyGraphUtils.prepareTwoTablesGraphVertexTab(conn /* Connection object */,
                                            pg /* table owner */, 
                                            "employeesNodes" /* vertex table name */, 
                                            propertyNames /* property names */, 
                                            propertyTypes /* property data types */,
                                            "pgts" /* table space */, 
                                            null /* storage options */, 
                                            true /* no logging */);

// Get the vertices from the employeesDAL graph
Iterable<Vertex> vertices = opgEmployees.getVertices();

// Load the vertices into the vertex table using a Two Tables schema
Connection[] conns = new Connection[1]; /* the connection array size defines the
                                           Degree of parallelism (multithreading)
                                        */
conns[1] = conn;
OraclePropertyGraphUtils.writeTwoTablesGraphVertexAndProperties(conn /* Connection 
                                                                        object */,
                                            pg /* table owner */, 
                                            "employeesNodes" /* vertex table name */,  
                                            1000 /* batch size*/, 
                                            new Iterable[] {vertices} /* array of 
                                                                vertex iterables */); 

// Read the vertices (using only name property)
List<String> vPropertyNames = new ArrayList<String>();
vPropertyNames.add("name");
ResultSet[] rsAr = readTwoTablesGraphVertexAndProperties(conns, 
                                                 "pg" /* table owner */,
                                                 "employeeNodes /* vertex table 
                                                                   name */,  
                                                 vPropertyNames /* list of property 
                                                                   names */,
                                                 1 /* Total Splits*/, 
                                                 0 /* Start Split);

Iterable<Vertex>[] vertices = getVerticesPartitioned(rsAr /* ResultSet array */,
                                                     true /* skip store to cache */,
                                                     null /* vertex filter 
                                                              callback */,
                                                     null /* optimization flag */);

for (int idx = 0; vertices.length; idx++) {
  Iterator<Vertex> it = vertices[idx].iterator();
  while (it.hasNext()) {
    System.out.println(it.next());
  }
}

前述の例では、次のような出力が生成されます。

Vertex ID 1 {name:str:Alice}
Vertex ID 2 {name:str:Bob}

2表スキーマを使用してエッジ表からエッジのサブセットを読み取るには、API OraclePropertyGraphUtils.readTwoTablesGraphEdgeAndPropertiesを使用できます。この操作は、頂点表の対応する分割で検出されたすべての行を含む、ResultSetオブジェクトの配列を返します。配列の各ResultSetオブジェクトは、頂点行を対応する分割からフェッチするために提供された接続のうちいずれかを使用します。分割は、指定した分割の合計数によって決定されます。

頂点の読取りで行われることと同様に、整数ID([0, N - 1]の範囲)がN分割された頂点表のすべての分割に割り当てられます。問合せされた分割のサブセットは、開始分割IDと開始分割IDに接続配列のサイズを足したものの間の範囲のID値を持つこれらの分割から構成されます。

次のコードは、OraclePropertyGraph APIを使用して、プロパティ・グラフemployeesGraphDALを作成し、2つの頂点と1つのエッジをロードします。次に、2表スキーマを使用して、エッジ表organizationEdgesを作成し、それにemployeesGraphDALのエッジからのデータを移入します。最後に、名前の重みだけを使用して、表からエッジを読み取ります。

       // Create employeesGraphDAL
       import oracle.pg.rdbms.*;
       Oracle oracle = new Oracle(jdbcURL, username, password);
       OraclePropertyGraph opgEmployees
                         = OraclePropertyGraph.getInstance(oracle, "employeesGraphDAL");

       // Create vertex v1 and assign it properties as key-value pairs
       Vertex v1 = opgEmployees.addVertex(1l);
       v1.setProperty("age",  Integer.valueOf(31));
       v1.setProperty("name", "Alice");
       v1.setProperty("address", "Main Street 12");
       v1.setProperty("email", "alice@mymail.com");
       v1.setProperty("SSN", "123456789");
  
       Vertex v2 = opgEmployees.addVertex(2l);
       v2.setProperty("age",  Integer.valueOf(27));
       v2.setProperty("name", "Bob");
       v2.setProperty("adress", "Sesame Street 334"); 

       
       // Add edge e1
       Edge e1 = opgEmployees.addEdge(1l, v1, v2, "managerOf");
       e1.setProperty("weight", 0.5d);

       opgEmployees.commit();

// Prepare the edge table using a Two Tables schema
import oracle.pgx.common.types.PropertyType;
List<String> propertyNames = new ArrayList<String>();
propertyNames.addAll(new String[4]{ "weight" });

List<PropertyType> = new ArrayList<PropertyType>();
propertyType.add(PropertyType.DOUBLE);

       Connection conn 
            = opgEmployees.getOracle().clone().getConnection(); /* Clone the connection
                                                                   from the property graph 
                                                                   instance */    
OraclePropertyGraphUtils.prepareTwoTablesGraphEdgeTab(conn /* Connection object */,
                                            pg /* table owner */, 
                                            "organizationEdges" /* edge table 
                                                                   name */, 
                                            propertyNames /* property names */, 
                                            propertyTypes /* property data types */,
                                            "pgts" /* table space */, 
                                            null /* storage options */, 
                                            true /* no logging */);

// Get the edges from the employeesDAL graph
Iterable<Edge> edges = opgEmployees.getVertices();

// Load the vertices into the vertex table using a Two Tables schema
Connection[] conns = new Connection[1]; /* the connection array size defines the
                                           Degree of parallelism (multithreading)
                                        */
conns[1] = conn;
OraclePropertyGraphUtils.writeTwoTablesGraphEdgeAndProperties(conn /* Connection 
                                                                        object */,
                                            pg /* table owner */, 
                                            organizationEdges" /* edge table name */,  
                                            1000 /* batch size*/, 
                                            new Iterable[] {edges} /* array of 
                                                                edge iterables */); 

// Read the edges (using only weight property)
List<String> ePropertyNames = new ArrayList<String>();
ePropertyNames.add("weight");
ResultSet[] rsAr = readTwoTablesGraphVertexAndProperties(conns, 
                                                 "pg" /* table owner */,
                                                 "organizationEdges /* edge table 
                                                                   name */,  
                                                 ePropertyNames /* list of property 
                                                                   names */,                                                                                            
                                                 1 /* Total Splits*/, 
                                                 0 /* Start Split);

Iterable<Edge>[] edges = getEdgesPartitioned(rsAr /* ResultSet array */,
                                                     true /* skip store to cache */,
                                                     null /* edge filter 
                                                              callback */,
                                                     null /* optimization flag */);

for (int idx = 0; edges.length; idx++) {
  Iterator<Edge> it = edges[idx].iterator();
  while (it.hasNext()) {
    System.out.println(it.next());
  }
}

前述の例では、次のような出力が生成されます。

Edge ID 1 from Vertex ID 1 {} =[references]=> Vertex ID 2 {} edgeKV[{weight:dbl:0.5}]

2.11 Oracleフラット・ファイル形式の定義

プロパティ・グラフは2つのフラット・ファイル、特に頂点およびエッジの記述ファイルに定義できます。

プロパティ・グラフは2つのフラット・ファイル、特に頂点およびエッジの記述ファイルに定義できます。

トピック:

2.11.1 プロパティ・グラフ記述ファイルについて

ファイルのペアにより、プロパティ・グラフが記述されます。

  • 頂点ファイル: プロパティ・グラフの頂点を記述します。このファイルには、.opvというファイル名拡張子が付きます。

  • エッジ・ファイル: プロパティ・グラフのエッジを記述します。このファイルには、.opeというファイル名拡張子が付きます。

この2つのファイルは同じベース名を共有することをお薦めします。たとえば、simple.opvおよびsimple.opeによってプロパティ・グラフを定義します。

2.11.2 エッジ・ファイル

エッジ・ファイルの各行は、プロパティ・グラフの1つのエッジを記述する1つのレコードです。1つのレコードはエッジの1つのキー値プロパティを記述できるので、複数のプロパティを含むエッジを記述する場合は複数のレコードが使用されます。

レコードには、カンマで区切られた9つのフィールドが含まれています。各レコードには、値があるかどうかに関係なく、すべてのフィールドを区切るために8つのカンマが含まれている必要があります。

edge_ID, source_vertex_ID, destination_vertex_ID, edge_label, key_name, value_type, value, value, value

次の表では、エッジ・ファイルのレコードを構成するフィールドについて説明します。

表2-3 エッジ・ファイルのレコードの形式

フィールド番号 名前 説明

1

edge_ID

エッジを一意に識別する整数。

2

source_vertex_ID

エッジの出力始点のvertex_ID

3

destination_vertex_ID

エッジの入力終点のvertex_ID

4

edge_label

2つの頂点間の関係を記述する、エッジのエンコードされたラベル。

5

key_name

キー値ペアのキーのエンコードされた名前。

エッジにプロパティが指定されていない場合は、空白(%20)を入力します。この例は、プロパティが指定されていないエッジ100を示してします。

100,1,2,likes,%20,,,,

6

value_type

キー値ペアの値のデータ型を表す整数。

  • 1 String
  • 2 Integer
  • 3 Float
  • 4 Double
  • 5 Timestamp (date)
  • 6 Boolean
  • 7 Long integer
  • 8 Short integer
  • 9 Byte
  • 10 Char
  • 20 Spatial
  • 101 Serializable Javaオブジェクト

7

value

数値でもタイムスタンプ(日付)でもない場合のkey_nameのエンコードされたnullでない値

8

value

数値の場合のkey_nameのエンコードされたnullではない値

9

value

タイムスタンプ(日付)の場合のkey_nameのエンコードされたnullではない値

日付の形式を識別するには、Java SimpleDateFormatクラスを使用します。この例は、2015-03-26Th00:00:00.000-05:00という日付形式について説明しています。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); encode(sdf.format((java.util.Date) value));

エッジの必須グループ化: 1つのエッジには複数のプロパティを指定でき、エッジ・ファイルには、エッジIDとそのエッジの1つのプロパティの組み合わせごとに1つのレコード(フラット・ファイル内の単一テキスト行によって表現される)が含まれます。エッジ・ファイルでは、各エッジのすべてのレコードがまとめてグループ化されている(つまり、他のエッジのレコードが間に入っていない)必要があります。これは任意の方法で行えますが、便利な方法は、エッジ・ファイルのレコードをエッジID別に昇順(または降順)にソートすることです。(ただし、エッジ・ファイルのすべてのレコードをエッジID別にソートする必要があるわけではありません。これはグループ化要件を満たすための単なる1つの方法です)。

2.11.3 頂点ファイル

頂点ファイルの各行は、プロパティ・グラフの1つの頂点を記述する1つのレコードです。1つのレコードは1つの頂点のキー値プロパティを記述できるので、複数のプロパティを持つ頂点を記述する場合は複数のレコード/行が使用されます。

レコードには、カンマで区切られた6つのフィールドが含まれています。各レコードには、値があるかどうかに関係なく、すべてのフィールドを区切るために5つのカンマが含まれている必要があります。

vertex_ID, key_name, value_type, value, value, value

次の表では、頂点ファイルのレコードを構成するフィールドについて説明します。

表2-4 頂点ファイルのレコードの形式

フィールド番号 名前 説明

1

vertex_ID

頂点を一意に識別する整数

2

key_name

キー値ペアのキーの名前

頂点にプロパティが指定されていない場合は、空白(%20)を入力します。この例ではプロパティが指定されていない頂点1について説明します。

1,%20,,,,

3

value_type

キー値ペアの値のデータ型を表す整数。

  • 1 String
  • 2 Integer
  • 3 Float
  • 4 Double
  • 5 Timestamp (date)
  • 6 Boolean
  • 7 Long integer
  • 8 Short integer
  • 9 Byte
  • 10 Char
  • 20 地理空間座標、線、ポリゴン、またはWell-Known Text (WKT)リテラルにできる空間データ
  • 101 Serializable Javaオブジェクト

4

value

数値でも日付でもない場合のkey_nameのエンコードされたnullでない値

5

value

数値の場合のkey_nameのエンコードされたnullではない値

6

value

タイムスタンプ(日付)の場合のkey_nameのエンコードされたnullではない値

日付の形式を識別するには、Java SimpleDateFormatクラスを使用します。この例は、2015-03-26T00:00:00.000-05:00という日付形式について説明しています。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); encode(sdf.format((java.util.Date) value));

頂点の必須グループ化: 1つの頂点には複数のプロパティを指定でき、頂点ファイルには、頂点IDとその頂点の1つのプロパティの組合せごとに1つのレコード(フラット・ファイル内の単一テキスト行によって表される)が含まれます。頂点ファイルでは、各頂点のすべてのレコードがまとめてグループ化されている(つまり、他の頂点のレコードが間に入っていない)必要があります。これは任意の方法で行えますが、便利な方法は、頂点ファイルのレコードを頂点ID別に昇順(または降順)にソートすることです(ただし、頂点ファイルのすべてのレコードを頂点ID別にソートする必要があるわけではありません。これはグループ化要件を満たすための単なる1つの方法です)。

2.11.4 特殊文字のエンコーディング

頂点およびエッジ・ファイルのエンコーディングはUTF-8です。次の表に、頂点またはエッジ・プロパティ(キー値ペア)またはエッジ・ラベルに表示されるときに文字列としてエンコードされる必要のある特殊文字をリストします。その他の文字はエンコーディングの必要はありません。

表2-5 Oracleフラット・ファイル形式での特殊文字コード

特殊文字 文字列のエンコーディング 説明

%

%25

パーセント

\t

%09

タブ

(空白)

%20

スペース

\n

%0A

改行

\r

%0D

復帰

,

%2C

カンマ

2.11.5 Oracleフラット・ファイル形式のプロパティ・グラフの例

Oracleフラット・ファイル形式でのプロパティ・グラフの例を次に示します。この例では、2つの頂点(JohnとMary)と、JohnがMaryの友人であることを示す1つのエッジがあります。

%cat simple.opv
1,age,2,,10,
1,name,1,John,,
2,name,1,Mary,,
2,hobby,1,soccer,,
 
%cat simple.ope
100,1,2,friendOf,%20,,,,

2.11.6 Oracleデータベース表からOracle定義のプロパティ・グラフ・フラット・ファイルへの変換

グラフの頂点とエッジを表すOracleデータベース表を、Oracle定義のフラット・ファイル形式(ファイルの拡張子が.opvおよび.ope)に変換します

Oracleデータベース表に格納されたグラフ・データがある場合、Java APIメソッドを使用して、そのデータをフラット・ファイルに変換し、後でプロパティ・グラフとしてOracleデータベースへロードできます。これにより、既存のOracleデータベース表からフラット・ファイルを生成するための、その他の手動アプローチを使用する必要性がなくなります。

グラフの頂点を格納している表の.opvファイルへの変換

エンティティ(グラフの頂点として表すことができる)を含むOracleデータベース表を、.opv形式のプロパティ・グラフのフラット・ファイルへ変換できます。

たとえば、次のリレーショナル表があるとします。EmployeeTab (empID integer not null, hasName varchar(255), hasAge integer, hasSalary number)

この表には次のデータがあるとします。

101, Jean, 20, 120.0
102, Mary, 21, 50.0
103, Jack, 22, 110.0
……

各従業員はグラフで頂点として表示できます。頂点IDは、employeeIDまたはハッシュなどのヒューリスティックを使用して生成されたIDの値である可能性があります。列hasName、hasAge、およびhasSalaryは属性として表示することができます。

JavaメソッドOraclePropertyGraphUtils.convertRDBMSTable2OPVおよびそのJavadoc情報は次のとおりです。

/**
* conn: is an connect instance to the Oracle relational database
* rdbmsTableName: name of the RDBMS table to be converted
* vidColName is the name of an column in RDBMS table to be treated as vertex ID
* lVIDOffset is the offset will be applied to the vertex ID
* ctams defines how to map columns in the RDBMS table to the attributes
* dop degree of parallelism
* dcl an instance of DataConverterListener to report the progress and control the behavior when errors happen 
*/
OraclePropertyGraphUtils.convertRDBMSTable2OPV(
       Connection conn, 
       String rdbmsTableName, 
       String vidColName, 
       long lVIDOffset, 
       ColumnToAttrMapping[] ctams, 
       int dop, 
       OutputStream opvOS, 
       DataConverterListener dcl);

次のコード・スニペットは、この表をOracle定義の頂点ファイル(.opv)に変換します。

// location of the output file
String opv = "./EmployeeTab.opv"; 
OutputStream opvOS = new FileOutputStream(opv);
// an array of ColumnToAttrMapping objects; each object defines how to map a column in the RDBMS table to an attribute of the vertex in an Oracle Property Graph.
ColumnToAttrMapping[] ctams = new ColumnToAttrMapping[3];
// map column "hasName" to attribute "name" of type String
ctams[0] = ColumnToAttrMapping.getInstance("hasName", "name", String.class);
// map column "hasAge" to attribute "age" of type Integer
ctams[1] = ColumnToAttrMapping.getInstance("hasAge", "age", Integer.class);
// map column "hasSalary" to attribute "salary" of type Double
ctams[2] = ColumnToAttrMapping.getInstance("hasSalary", "salary",Double.class);
// convert RDBMS table "EmployeeTab" into opv file "./EmployeeTab.opv", column "empID" is the vertex ID column, offset 1000l will be applied to vertex ID, use ctams to map RDBMS columns to attributes, set DOP to 8
OraclePropertyGraphUtils.convertRDBMSTable2OPV(conn, "EmployeeTab", "empID", 1000l, ctams, 8, opvOS, (DataConverterListener) null);

注意:

オフセット値1000lの最後の文字である小文字の"l"は、その前の値が長整数であることを示します。

変換結果は次のようになります。

1101,name,1,Jean,,
1101,age,2,,20,
1101,salary,4,,120.0,
1102,name,1,Mary,,
1102,age,2,,21,
1102,salary,4,,50.0,
1103,name,1,Jack,,
1103,age,2,,22,
1103,salary,4,,110.0,

この場合、表EmployeeTabの各行は、3つの属性を持つ1つの頂点に変換されます。たとえば、データ"101, Jean, 20, 120.0"は、IDが1101で属性がname/"Jean", age/20, salary/120.0である頂点に変換されます。オフセット1000lが適用されるため、元のempID 101と頂点ID 1101の間にはオフセットがあります。オフセットはグラフ要素のID値の衝突を避けるのに有用です。

グラフのエッジを格納している表の.opeファイルへの変換

エンティティの関係(グラフのエッジとして表すことができる)を含むOracleデータベース表を、.ope形式のプロパティ・グラフのフラット・ファイルへ変換できます。

たとえば、次のリレーショナル表があるとします。EmpRelationTab (relationID integer not null, source integer not null, destination integer not null, relationType varchar(255), startDate date)

この表には次のデータがあるとします。

90001, 101, 102, manage, 10-May-2015
90002, 101, 103, manage, 11-Jan-2015
90003, 102, 103, colleague, 11-Jan-2015
……

各関係(行)はグラフでエッジとして表示できます。特に、エッジIDは、relationIDまたはハッシュなどのヒューリスティックを使用して生成されたIDと同じである可能性があります。列relationTypeはエッジ・ラベルの定義に使用でき、列startDateはエッジ属性として扱うことができます。

JavaメソッドOraclePropertyGraphUtils.convertRDBMSTable2OPEおよびそのJavadoc情報は次のとおりです。

/**
* conn: is an connect instance to the Oracle relational database
* rdbmsTableName: name of the RDBMS table to be converted
* eidColName is the name of an column in RDBMS table to be treated as edge ID
* lEIDOffset is the offset will be applied to the edge ID
* svidColName is the name of an column in RDBMS table to be treated as source vertex ID of the edge
* dvidColName is the name of an column in RDBMS table to be treated as destination vertex ID of the edge
* lVIDOffset is the offset will be applied to the vertex ID
* bHasEdgeLabelCol a Boolean flag represents if the given RDBMS table has a column for edge labels; if true, use value of column elColName as the edge label; otherwise, use the constant string elColName as the edge label
* elColName is the name of an column in RDBMS table to be treated as edge labels
* ctams defines how to map columns in the RDBMS table to the attributes
* dop degree of parallelism
* dcl an instance of DataConverterListener to report the progress and control the behavior when errors happen 
*/
OraclePropertyGraphUtils.convertRDBMSTable2OPE(
        Connection conn, 
        String rdbmsTableName, 
        String eidColName, 
        long lEIDOffset, 
        String svidColName, 
        String dvidColName, 
        long lVIDOffset, 
        boolean bHasEdgeLabelCol, 
        String elColName, 
        ColumnToAttrMapping[] ctams, 
        int dop, 
        OutputStream opeOS, 
        DataConverterListener dcl);

次のコード・スニペットは、この表をOracle定義のエッジ・ファイル(.ope)に変換します。

// location of the output file
String ope = "./EmpRelationTab.ope"; 
OutputStream opeOS = new FileOutputStream(ope);
// an array of ColumnToAttrMapping objects; each object defines how to map a column in the RDBMS table to an attribute of the edge in an Oracle Property Graph.
ColumnToAttrMapping[] ctams = new ColumnToAttrMapping[1];
// map column "startDate" to attribute "since" of type Date
ctams[0] = ColumnToAttrMapping.getInstance(“startDate", “since",Date.class);
// convert RDBMS table “EmpRelationTab" into ope file “./EmpRelationTab.opv", column “relationID" is the edge ID column, offset 10000l will be applied to edge ID, the source and destination vertices of the edge are defined by columns “source" and “destination", offset 1000l will be applied to vertex ID, the RDBMS table has an column “relationType" to be treated as edge labels, use ctams to map RDBMS columns to edge attributes, set DOP to 8
OraclePropertyGraphUtils.convertRDBMSTable2OPE(conn, “EmpRelationTab", “relationID", 10000l, “source", “destination", 1000l, true, “relationType", ctams, 8, opeOS, (DataConverterListener) null);

注意:

オフセット値10000lの最後の文字である小文字の"l"は、その前の値が長整数であることを示します。

変換結果は次のようになります。

100001,1101,1102,manage,since,5,,,2015-05-10T00:00:00.000-07:00
100002,1101,1103,manage,since,5,,,2015-01-11T00:00:00.000-07:00
100003,1102,1103,colleague,since,5,,,2015-01-11T00:00:00.000-07:00

この場合、表EmpRelationTabの各行は、属性sinceを持つ個別のエッジに変換されます。たとえば、データ"90001, 101, 102, manage, 10-May-2015"は、IDが100001で頂点1101から頂点1102にリンクしているエッジに変換されます。このエッジは属性since/"2015-05-10T00:00:00.000-07:00"を持ちます。オフセット10000lが適用されるため、元のrelationID "90001"とエッジID "100001"の間にはオフセットがあります。同様に、オフセット1000lが出力および入力頂点IDに適用されます。

2.11.7 頂点およびエッジのCSVファイルからOracle定義のプロパティ・グラフ・フラット・ファイルへの変換

アプリケーションによってはCSV (カンマ区切り値)形式を使用してグラフの頂点およびエッジをエンコードするものがあります。この形式では、CSVファイルの各レコードが、そのすべてのプロパティを含む、1つの頂点またはエッジを表します。グラフの頂点を表すCSVファイルを、Oracle定義のフラット・ファイル形式の定義(頂点の場合.opv、エッジの場合.ope)に変換できます

変換されるCSVファイルには、列名と、列を表す属性のタイプを指定するヘッダーが含まれることがあります。ヘッダーに属性名のみが含まれる場合、コンバータは、値のデータ型が文字列だと見なします。

CSVからOPVまたはOPEに変換するJava APIは、頂点またはエッジを読み取る(CSVから)元の、InputStreamを受信し、それを.opvまたは.ope形式でOutputStreamに書き込みます。コンバータAPIは変換プロセスをカスタマイズすることもできます。

次のサブトピックで、頂点およびエッジの変換手順を示します。

  • 頂点: CSVファイルのOracle定義フラット・ファイル形式(.opv)への変換

  • エッジ: CSVファイルのOracle定義フラット・ファイル形式(.ope)への変換

どちらの手順も非常に似ていますが、頂点に固有、およびエッジに固有という違いがあります。

頂点: CSVファイルのOracle定義フラット・ファイル形式(.opv)への変換

CSVファイルにヘッダーが含まれていない場合、すべての属性名(その値のデータ型にマップされる)を記述しているColumnToAttrMapping配列を、CSVファイルに現れるのと同じ順番で、指定する必要があります。さらに、頂点のIDなどの特別な列を含む、CSVファイルからの列はすべて、配列で記述される必要があります。同じCSVファイルの最初の行に列のヘッダーを指定する場合、このパラメータはnullに設定する必要があります。

列を表しているCSVファイルを変換するには、いずれかのconvertCSV2OPV APIを使用することができます。これらのAPIのうち最も単純なものには次が必要です。

  • CSVファイルから頂点を読み取るためのInputStream

  • 頂点IDを表している列の名前(この列はCSVファイルにある必要があります)

  • VIDに追加する整数オフセット(オフセットはグラフ要素のID値の衝突を避けるのに有用です)

  • ColumnToAttrMapping配列(ヘッダーがファイルで指定されている場合、これはnullである必要があります)

  • 並列度(DOP)

  • 変換の前にオフセットを示す整数(スキップする頂点のレコード数)

  • 頂点フラット・ファイル(.opv)が書き込まれるOutputStream

  • オプションで、変換の進捗をトラッキングし、エラーの発生時に何を実行するかを決定するのに使用できるDataConverterListener

追加のパラメータは、CSVファイルの別の形式を指定するのに使用できます。

  • レコードのトークンを区切るのに使用する区切り文字。デフォルトはカンマ文字','です。

  • 文字列値を引用するのに使用され、カンマなどの特殊文字を含めることができる、引用符。文字列値そのものに引用符が表れた場合、重複か、またはバックスラッシュ文字'\'をその前に配置することのいずれかで、エスケープする必要があります。次に例を示します。

    • """Hello, world"", the screen showed…"

    • "But Vader replied: \"No, I am your father.\""

  • 日付値を解析するのに使用する日付書式。CSVの変換の場合、このパラメータはnullにすることができますが、CSVに特定の日付書式がある場合には指定することをお薦めします。特定の日付書式を指定すると、日付値を解析しようとするときに、その書式が最初のオプションとして使用されるので、パフォーマンス上役立ちます。日付書式の例は、次のとおりです。

    • "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"

    • "MM/dd/yyyy HH:mm:ss"

    • "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"

    • "dddd, dd MMMM yyyy hh:mm:ss"

    • "yyyy-MM-dd"

    • "MM/dd/yyyy"

  • CSVファイルに文字列値と改行文字が含まれることを示すフラグ。このパラメータがtrueに設定されていると、改行文字または引用符が値として含まれているファイル内のすべての文字列は、引用符で囲まれる必要があります。

    • "The first lines of Don Quixote are:""In a village of La Mancha, the name of which I have no desire to call to mind""."

次のコード・フラグメントは、ColumnToAttrMapping配列を作成する方法と、APIを使用して、CSVファイルを.opvファイルに変換する方法を示します。

    String inputCSV             = "/path/mygraph-vertices.csv";
    String outputOPV            = "/path/mygraph.opv"; 
    ColumnToAttrMapping[] ctams = new ColumnToAttrMapping[4];
    ctams[0]                    = ColumnToAttrMapping.getInstance("VID",   Long.class);
    ctams[1]                    = ColumnToAttrMapping.getInstance("name",  String.class);
    ctams[2]                    = ColumnToAttrMapping.getInstance("score", Double.class);
    ctams[3]                    = ColumnToAttrMapping.getInstance("age",   Integer.class);
    String vidColumn            = "VID";

    isCSV = new FileInputStream(inputCSV);
    osOPV = new FileOutputStream(new File(outputOPV));
      
    // Convert Vertices
    OraclePropertyGraphUtilsBase.convertCSV2OPV(isCSV, vidColumn, 0, ctams, 1, 0, osOPV, null);
    isOPV.close();
    osOPV.close();

この例では、変換されるCSVファイルには、ヘッダーが含まれず、4つの列(列ID、名前、スコア、および年齢)が含まれる必要があります。次にCVSの例を示します。

1,John,4.2,30
2,Mary,4.3,32
3,"Skywalker, Anakin",5.0,46
4,"Darth Vader",5.0,46
5,"Skywalker, Luke",5.0,53

結果の.opvファイルは次のようになります。

1,name,1,John,,
1,score,4,,4.2,
1,age,2,,30,
2,name,1,Mary,,
2,score,4,,4.3,
2,age,2,,32,
3,name,1,Skywalker%2C%20Anakin,,
3,score,4,,5.0,
3,age,2,,46,
4,name,1,Darth%20Vader,,
4,score,4,,5.0,
4,age,2,,46,
5,name,1,Skywalker%2C%20Luke,,
5,score,4,,5.0,
5,age,2,,53,

エッジ: CSVファイルのOracle定義フラット・ファイル形式(.ope)への変換

CSVファイルにヘッダーが含まれていない場合、すべての属性名(その値のデータ型にマップされる)を記述しているColumnToAttrMapping配列を、CSVファイルに現れるのと同じ順番で、指定する必要があります。さらに、適用される場合はエッジのID、必要なSTART_ID、END_ID、TYPEなどの特別な列を含む、CSVファイルからの列はすべて、配列で記述される必要があります。同じCSVファイルの最初の行に列のヘッダーを指定する場合、このパラメータはnullに設定する必要があります。

列を表しているCSVファイルを変換するには、いずれかのconvertCSV2OPE APIを使用することができます。これらのAPIのうち最も単純なものには次が必要です。

  • CSVファイルから頂点を読み取るためのInputStream

  • エッジIDを表している列の名前(これはCSVファイルではオプションで、存在しない場合は行番号がIDとして使用されます)

  • EIDに追加する整数オフセット(オフセットはグラフ要素のID値の衝突を避けるのに有用です)

  • 出力頂点IDを表している列の名前(この列はCSVファイルにある必要があります)

  • 入力頂点IDを表している列の名前(この列はCSVファイルにある必要があります)

  • VIDへのオフセット(lOffsetVID)。このオフセットは元のSVIDおよびDVID値の先頭に追加されます。(このAPIのバリエーションは2つの引数(lOffsetSVIDおよびlOffsetDVID)をとります。1つはSVID用のオフセット、もう1つはDVID用オフセットです)

  • エッジ・ラベル列がCSVファイルにあるかどうかを示すブール・フラグ。

  • エッジ・ラベルを表している列の名前(この列がCSVファイルにない場合は、このパラメータはすべてのエッジ・ラベルの定数として使用されます)

  • ColumnToAttrMapping配列(ヘッダーがファイルで指定されている場合、これはnullである必要があります)

  • 並列度(DOP)

  • 変換の前にオフセットを示す整数(スキップするエッジのレコード数)

  • エッジ・フラット・ファイル(.ope)が書き込まれるOutputStream

  • オプションで、変換の進捗をトラッキングし、エラーの発生時に何を実行するかを決定するのに使用できるDataConverterListener

追加のパラメータは、CSVファイルの別の形式を指定するのに使用できます。

  • レコードのトークンを区切るのに使用する区切り文字。デフォルトはカンマ文字','です。

  • 文字列値を引用するのに使用され、カンマなどの特殊文字を含めることができる、引用符。文字列値そのものに引用符が表れた場合、重複か、またはバックスラッシュ文字'\'をその前に配置することのいずれかで、エスケープする必要があります。次に例を示します。

    • """Hello, world"", the screen showed…"

    • "But Vader replied: \"No, I am your father.\""

  • 日付値を解析するのに使用する日付書式。CSVの変換の場合、このパラメータはnullにすることができますが、CSVに特定の日付書式がある場合には指定することをお薦めします。特定の日付書式を指定すると、日付値を解析しようとするときに、その書式が最初のオプションとして使用されるので、パフォーマンス上役立ちます。日付書式の例は、次のとおりです。

    • "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"

    • "MM/dd/yyyy HH:mm:ss"

    • "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"

    • "dddd, dd MMMM yyyy hh:mm:ss"

    • "yyyy-MM-dd"

    • "MM/dd/yyyy"

  • CSVファイルに文字列値と改行文字が含まれることを示すフラグ。このパラメータがtrueに設定されていると、改行文字または引用符が値として含まれているファイル内のすべての文字列は、引用符で囲まれる必要があります。

    • "The first lines of Don Quixote are:""In a village of La Mancha, the name of which I have no desire to call to mind""."

次のコード・フラグメントは、APIを使用して、CSVファイルを.opeファイルとnullのColumnToAttrMapping配列に変換する方法を示します。

    String inputOPE    = "/path/mygraph-edges.csv";
    String outputOPE   = "/path/mygraph.ope"; 
    String eidColumn   = null;             // null implies that an integer sequence will be used
    String svidColumn  = "START_ID";
    String dvidColumn  = "END_ID";
    boolean hasLabel   = true;
    String labelColumn = "TYPE";

    isOPE = new FileInputStream(inputOPE);
    osOPE = new FileOutputStream(new File(outputOPE));
      
    // Convert Edges
    OraclePropertyGraphUtilsBase.convertCSV2OPE(isOPE, eidColumn, 0, svidColumn, dvidColumn, hasLabel, labelColumn, null, 1, 0, osOPE, null);

前述の例を使用して変換される入力CSVには、列名とその型を指定するヘッダーが含まれる必要があります。次にCSVファイルの例を示します。

START_ID:long,weight:float,END_ID:long,:TYPE
1,1.0,2,loves
1,1.0,5,admires
2,0.9,1,loves
1,0.5,3,likes
2,0.0,4,likes
4,1.0,5,is the dad of
3,1.0,4,turns to
5,1.0,3,saves from the dark side

結果の.opeファイルは次のようになります。

1,1,2,loves,weight,3,,1.0,
2,1,5,admires,weight,3,,1.0,
3,2,1,loves,weight,3,,0.9,
4,1,3,likes,weight,3,,0.5,
5,2,4,likes,weight,3,,0.0,
6,4,5,is%20the%20dad%20of,weight,3,,1.0,
7,3,4,turns%20to,weight,3,,1.0,
8,5,3,saves%20from%20the%20dark%20side,weight,3,,1.0,

2.12 Pythonユーザー・インタフェースの例

$ORACLE_HOME/md/property_graph/pyopg/にあるPythonスクリプトの例は、Oracle Spatial and Graphプロパティ・グラフとともに使用でき、必要に応じてその例(またはそのコピー)を変更および拡張できます。

注意:

名前は頂点またはエッジ・ファイルには表示されませんが、ここではフィールド参照を単純にするために提供されています。

例を実行するためにユーザー・インタフェースを起動するには、スクリプトpyopg.shを使用します。

例には次のものが含まれています。

  • 例1: Oracleデータベースに接続して、頂点およびエッジの数の単純なチェックを実行します。これを実行するには、次のようにします。

    sh ./pyopg.sh
     
    connectRDBMS("connections", "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "tiger");print "vertices", countV()
    print "edges", countE()
    

    前の例で、mygraphはOracleデータベースに格納されているグラフの名前で、残りの引数はOracleデータベースにアクセスするための接続情報です。これは環境に合わせてカスタマイズする必要があります。

  • 例2: Oracleデータベースに接続して、いくつかの分析機能を実行します。これを実行するには、次のようにします。

    connectRDBMS("connections", "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "tiger");
    print "vertices", countV()
    print "edges", countE()
    
    import pprint
    
    analyzer = analyst()
    print "# triangles in the graph", analyzer.countTriangles()
    
    graph_communities = [{"commid":i.getName(),"size":i.size()} for i in analyzer.communities().iterator()]
    
    import pandas as pd
    import numpy as np
    
    community_frame = pd.DataFrame(graph_communities)
    community_frame[:5]
    
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(16,12));
    community_frame["size"].plot(kind="bar", title="Communities and Sizes")
    ax.set_xticklabels(community_frame.index);
    plt.show()
    

    前の例は、Oracleデータベースに接続して、頂点およびエッジに関する基本情報を出力し、インメモリー・アナリストを取得し、トライアングル数を計算し、コミュニティ検出を実行して、最後にコミュニティとそのサイズを棒グラフに表します。

Pythonインタフェースの例の詳細は、インストール・ホームの下にある次のディレクトリを参照してください。

$ORACLE_HOME/md/property_graph/pyopg/doc/