3 インメモリー・グラフ・サーバー(PGX)の使用

Oracle Graphのインメモリー・グラフ・サーバーでは、一連の分析関数をサポートします。

この章では、インメモリー・グラフ・サーバー(プロパティ・グラフ・インメモリー分析とも呼ばれ、Javadoc、コマンドライン、パスの説明、エラー・メッセージ、例ではPGXと省略されます)を使用する例について説明します。内容は次のとおりです。

3.1 インメモリー・グラフ・サーバー(PGX)の概要

インメモリー・グラフ・サーバー(PGX)は、高速でパラレルなグラフ問合せおよび分析のためのインメモリー・グラフ・サーバーです。サーバーは軽量のインメモリー・データ構造を使用して、グラフ・アルゴリズムの高速実行を可能にします。

インメモリー・グラフ・サーバー(PGX)は、高速でパラレルなグラフ問合せおよび分析のためのインメモリー・グラフ・サーバーです。サーバーは軽量のインメモリー・データ構造を使用して、グラフ・アルゴリズムの高速実行を可能にします。

Oracle Databaseまたはファイルからグラフをグラフ・サーバーにロードするオプションは複数あります。

グラフ・サーバーは、スタンドアロン(埋込みApache Tomcatインスタンスを含む)でデプロイすることも、Oracle WebLogic ServerまたはApache Tomcatにデプロイすることもできます。

3.1.1 インメモリー・グラフ・サーバー(PGX)への接続

インメモリー・グラフ・サーバーには、複数のグラフ・クライアントが同時に接続できます。クライアント・リクエストは、グラフ・サーバーによって非同期に処理されます。クライアント・リクエストは最初にキューに入れられ、リソースが使用可能になると後で処理されます。クライアントはサーバーをポーリングして、リクエストが終了したかどうかを確認できます。内部的には、サーバーはパラレル・グラフ・アルゴリズムを実行するための独自のエンジン(スレッド・プール)を保持します。エンジンは、各分析リクエストをできるだけ多くのスレッドと同時に処理しようとします。

各クライアントには、セッションと呼ばれる独自のプライベート・ワークスペースがあります。セッションは相互に分離されています。各クライアントは、他のクライアントから独立して、グラフ・インスタンスを独自のセッションにロードできます。

ノート:

  • 複数のクライアントが同じグラフ・インスタンスをロードする場合、グラフ・サーバーはフード下の複数のクライアント間で1つのグラフ・インスタンスを共有できます。
  • 各クライアントは、独自のセッションのロードされたグラフに頂点またはエッジ・プロパティを追加できます。このようなプロパティは一時プロパティであり、各セッションに対してプライベートであり、別のセッションからは参照できません。クライアントがグラフの変更バージョンを作成すると、グラフ・サーバーはそのクライアントのプライベート・グラフ・インスタンスを作成します。

3.2 ユーザーの認証および認可

Oracle Graph Server (PGX)では、Oracle Databaseをアイデンティティ・マネージャとして使用します。

つまり、既存のOracle Database資格証明(ユーザー名とパスワード)を使用してグラフ・サーバーにログインすると、グラフ・サーバーで実行できるアクションは、Oracleデータベースで付与されているロールによって決まります。

認証にOracle Databaseを使用するための基本ステップ

  1. Oracle Graph Server and Client (バージョン12.2以降)でサポートされているOracle Databaseバージョン(Autonomous Databaseを含む)を使用します。
  2. グラフ・サーバー(PGX)へのユーザー・アクセス権を付与および取り消すために、ADMINアクセス権(または非自律型データベースの場合はSYSDBAアクセス権)があることを確認します。
  3. グラフ・サーバーへのアクセス権を付与する予定のすべての既存ユーザーに、少なくともCREATE SESSION権限が付与されていることを確認します。
  4. Graph Serverが実行されているホストからJDBCを介してデータベースにアクセスできることを確認します。
  5. ADMIN (または非自律型データベースではSYSDBA)として、次のプロシージャを実行し、グラフ・サーバーで必要なロールを作成します。

    ノート:

    Oracle Graph Server and ClientディストリビューションのPL/SQLパッケージをターゲットのOracle Databaseにインストールする場合、このステップは必要ありません。次のコードに示すすべてのロールは、PL/SQLのインストールの一部として自動的に作成されます。PL/SQLパッケージはAutonomous Databaseにインストールできないため、グラフ・サーバーをAutonomous Databaseとともに使用する場合は、SQL Developer Webを使用して次の文を実行することをお薦めします。
    
    DECLARE
      PRAGMA AUTONOMOUS_TRANSACTION;
      role_exists EXCEPTION;
      PRAGMA EXCEPTION_INIT(role_exists, -01921);
      TYPE graph_roles_table IS TABLE OF VARCHAR2(50);
      graph_roles graph_roles_table;
    BEGIN
      graph_roles := graph_roles_table(
        'GRAPH_DEVELOPER',
        'GRAPH_ADMINISTRATOR',
        'PGX_SESSION_CREATE',
        'PGX_SERVER_GET_INFO',
        'PGX_SERVER_MANAGE',
        'PGX_SESSION_READ_MODEL',
        'PGX_SESSION_MODIFY_MODEL',
        'PGX_SESSION_NEW_GRAPH',
        'PGX_SESSION_GET_PUBLISHED_GRAPH',
        'PGX_SESSION_COMPILE_ALGORITHM',
        'PGX_SESSION_ADD_PUBLISHED_GRAPH');
      FOR elem IN 1 .. graph_roles.count LOOP
      BEGIN
        dbms_output.put_line('create_graph_roles: ' || elem || ': CREATE ROLE ' || graph_roles(elem));
        EXECUTE IMMEDIATE 'CREATE ROLE ' || graph_roles(elem);
      EXCEPTION
        WHEN role_exists THEN
          dbms_output.put_line('create_graph_roles: role already exists. continue');
        WHEN OTHERS THEN
          RAISE;
        END;
      END LOOP;
    EXCEPTION
      when others then
        dbms_output.put_line('create_graph_roles: hit error ');
        raise;
    END;
    /
  6. ロールGRAPH_DEVELOPERおよびGRAPH_ADMINISTRATORにデフォルト権限を割り当てて、複数の権限をグループ化します。

    ノート:

    Oracle Graph Server and ClientディストリビューションのPL/SQLパッケージをターゲットのOracle Databaseにインストールする場合、このステップは必要ありません。次のコードに示す権限付与はすべて、PL/SQLのインストールの一部として自動的に実行されます。PL/SQLパッケージはAutonomous Databaseにインストールできないため、グラフ・サーバーをAutonomous Databaseとともに使用する場合は、SQL Developer Webを使用して次の文を実行することをお薦めします。
    
    GRANT PGX_SESSION_CREATE TO GRAPH_ADMINISTRATOR;
    GRANT PGX_SERVER_GET_INFO TO GRAPH_ADMINISTRATOR;
    GRANT PGX_SERVER_MANAGE TO GRAPH_ADMINISTRATOR;
    GRANT PGX_SESSION_CREATE TO GRAPH_DEVELOPER;
    GRANT PGX_SESSION_NEW_GRAPH TO GRAPH_DEVELOPER;
    GRANT PGX_SESSION_GET_PUBLISHED_GRAPH TO GRAPH_DEVELOPER;
    GRANT PGX_SESSION_MODIFY_MODEL TO GRAPH_DEVELOPER;
    GRANT PGX_SESSION_READ_MODEL TO GRAPH_DEVELOPER;
  7. グラフ・サーバー(PGX)にアクセスする必要があるすべてのデータベース開発者にロールを割り当てます。次に例を示します。
    GRANT graph_developer TO <graphuser>

    <graphuser>はデータベースのユーザーです。個別の権限(PGX_の接頭辞が付いたロール)をユーザーに直接割り当てることもできます。

  8. 管理アクセス権が必要なユーザーに管理者ロールを割り当てます。次に例を示します。
    GRANT graph_administrator to <administratoruser>

    <administratoruser>は、データベースのユーザーです。

3.2.1 データベース認証のためのグラフ・サーバーの準備

インストールのpgx.confファイルを見つけます。

RPMを使用してグラフ・サーバーをインストールした場合、ファイルは/etc/oracle/graph/pgx.confにあります。

webappsパッケージを使用してTomcatまたはWebLogic Serverにデプロイする場合、pgx.confファイルは、Webアプリケーション・アーカイブ・ファイル(WARファイル)内のWEB-INF/classes/pgx.confにあります。

ヒント: Linuxでは、vimを使用すると、先に解凍しなくてもWARファイル内でファイルを直接編集できます。例: vim pgx-webapp-21.1.0.war

pgx.confファイル内で、レルム・オプションのjdbc_url行を見つけます。

...
"pgx_realm": {
  "implementation": "oracle.pg.identity.DatabaseRealm",
  "options": {
    "jdbc_url": "<REPLACE-WITH-DATABASE-URL-TO-USE-FOR-AUTHENTICATION>",
    "token_expiration_seconds": 3600,
...

前のステップで構成したデータベースを指すJDBC URLにテキストを置き換えます。次に例を示します。

...
"pgx_realm": {
  "implementation": "oracle.pg.identity.DatabaseRealm",
  "options": {
    "jdbc_url": "jdbc:oracle:thin:@myhost:1521/myservice",
    "token_expiration_seconds": 3600,
...

Autonomous Databaseを使用している場合、次のようにJDBC URLを指定します。

...
"pgx_realm": {
  "implementation": "oracle.pg.identity.DatabaseRealm",
  "options": {
    "jdbc_url": "jdbc:oracle:thin:@my_identifier_low?TNS_ADMIN=/etc/oracle/graph/wallet",
    "token_expiration_seconds": 3600,
...

/etc/oracle/graph/walletはAutonomous Databaseサービス・コンソールからダウンロードした解凍済ウォレット・ファイルへのパスの例で、my_identifier_low/etc/oracle/graph/wallet/tnsnames.oraに指定された接続識別子のいずれかです。

ここで、グラフ・サーバーを起動します。RPMを使用してインストールした場合は、rootユーザーとして、またはsudoを指定して次のコマンドを実行します。

sudo systemctl start pgx

3.2.2 データベース認証を使用したJShellからサーバーへの接続

JShellクライアントを使用すると、データベース認証を使用してリモート・モードでサーバーに接続できます。

リモート・モードでサーバーに接続するには:
./bin/opg-jshell --base_url https://localhost:7007 --username <database_user>

データベース・パスワードの入力を要求されます。

Javaクライアント・プログラムを使用している場合は、次の例に示すように、サーバーに接続できます。
import oracle.pg.rdbms.*
import oracle.pgx.api.*
 
...
 
ServerInstance instance = GraphServer.getInstance("https://localhost:7007", "<database user>", "<database password>");
PgxSession session = instance.createSession("my-session");

...

内部的には、ユーザーはJSON Webトークン(JWT)を使用してグラフ・サーバーで認証されます。トークンの有効期限の詳細は、トークンの有効期限を参照してください。

3.2.3 データベースからのデータの読取り

ログインすると、接続情報をグラフ構成に指定しなくてもデータベースからグラフ・サーバーにデータを読み込むことができるようになりました。

データベース・ユーザーが存在し、データベースのグラフ・データに対する読取りアクセス権を持っている必要があります。

たとえば、次のグラフ構成では、hrという名前のプロパティ・グラフがメモリーに読み込まれ、データベースで<database user>/<database password>として認証されます。

GraphConfig config = GraphConfigBuilder.forPropertyGraphRdbms()
    .setName("hr")
    .addVertexProperty("FIRST_NAME", PropertyType.STRING)
    .addVertexProperty("LAST_NAME", PropertyType.STRING)
    .addVertexProperty("EMAIL", PropertyType.STRING)
    .addVertexProperty("CITY", PropertyType.STRING)
    .setPartitionWhileLoading(PartitionWhileLoading.BY_LABEL)
    .setLoadVertexLabels(true)
    .setLoadEdgeLabel(true)
    .build();
PgxGraph hr = session.readGraphWithProperties(config);

次の例は、接続情報を構成ファイル自体に指定せずにリレーショナル表からグラフ・サーバーに読み込むJSON形式のグラフ構成です。

{
    "name":"hr",
    "vertex_id_strategy":"no_ids",
    "vertex_providers":[
        {
            "name":"Employees",
            "format":"rdbms",
            "database_table_name":"EMPLOYEES",
            "key_column":"EMPLOYEE_ID",
            "key_type":"string",
            "props":[
                {
                    "name":"FIRST_NAME",
                    "type":"string"
                },
                {
                    "name":"LAST_NAME",
                    "type":"string"
                }
            ]
        },
        {
            "name":"Departments",
            "format":"rdbms",
            "database_table_name":"DEPARTMENTS",
            "key_column":"DEPARTMENT_ID",
            "key_type":"string",
            "props":[
                {
                    "name":"DEPARTMENT_NAME",
                    "type":"string"
                }
            ]
        }
    ],
    "edge_providers":[
        {
            "name":"WorksFor",
            "format":"rdbms",
            "database_table_name":"EMPLOYEES",
            "key_column":"EMPLOYEE_ID",
            "source_column":"EMPLOYEE_ID",
            "destination_column":"EMPLOYEE_ID",
            "source_vertex_provider":"Employees",
            "destination_vertex_provider":"Employees"
        },
        {
            "name":"WorksAs",
            "format":"rdbms",
            "database_table_name":"EMPLOYEES",
            "key_column":"EMPLOYEE_ID",
            "source_column":"EMPLOYEE_ID",
            "destination_column":"JOB_ID",
            "source_vertex_provider":"Employees",
            "destination_vertex_provider":"Jobs"
        }
    ]
}

データベースからグラフ・サーバーにデータを読み込む方法の詳細は、キーストアへのデータベース・パスワードの格納を参照してください。

3.2.4 キーストアへのデータベース・パスワードの格納

PGXでは、データベースからメモリーにデータを読み込むためにデータベース・アカウントが必要です。アカウントは、権限の低いアカウントである必要があります(グラフ・データを使用したセキュリティのベスト・プラクティスを参照)。

データベースからのデータの読取りで説明されているように、トークンがそのデータベース・ユーザーに対して有効であるかぎり、追加の認証を指定しなくてもデータベースからグラフ・サーバーにデータを読み込むことができます。ただし、別のユーザーからグラフにアクセスする場合は、そのユーザーのパスワードが保護のためにJavaキーストア・ファイルに格納されているかぎり、それは可能です。

JDKにバンドルされたkeytoolコマンドを使用して、そのようなキーストア・ファイルをコマンドラインで生成できます。例として次のスクリプトを参照してください。

# Add a password for the 'database1' connection
keytool -importpass -alias database1 -keystore keystore.p12
# 1. Enter the password for the keystore
# 2. Enter the password for the database
 
# Add another password (for the 'database2' connection)
keytool -importpass -alias database2 -keystore keystore.p12
 
# List what's in the keystore using the keytool
keytool -list -keystore keystore.p12

Javaバージョン8以下を使用している場合は、前述の例のkeytoolコマンドに追加パラメータ-storetype pkcs12を渡す必要があります。

複数のパスワードを1つのキーストア・ファイルに格納できます。各パスワードは、指定された別名を使用して参照できます。

プロパティ・グラフ・スキーマからロードするPGXグラフ構成ファイルを記述します

次に、PGXグラフ構成ファイルをJSON形式で作成します。このファイルは、データのロード元、データの外観、および使用するキーストア別名をPGXに伝えます。次の例に、Oracleプロパティ・グラフ形式で格納されたデータを読み取るグラフ構成を示します。

{
  "format": "pg",
  "db_engine": "rdbms",
  "name": "hr",
  "jdbc_url": "jdbc:oracle:thin:@myhost:1521/orcl",
  "username": "hr",
  "keystore_alias": "database1",
  "vertex_props": [{
    "name": "COUNTRY_NAME",
    "type": "string"
  }, {
    "name": "DEPARTMENT_NAME",
    "type": "string"
  }, {
    "name": "SALARY",
    "type": "double"
  }],
  "partition_while_loading": "by_label",
  "loading": {
    "load_vertex_labels": true,
    "load_edge_label": true
  }
}

(使用可能な構成フィールドの完全なリストについては、意味およびデフォルト値を含め、https://docs.oracle.com/cd/E56133_01/latest/reference/loader/database/pg-format.htmlを参照してください。)

または、PGXグラフ構成ファイルを記述し、グラフをリレーショナル表から直接ロードします

次の例では、HRサンプル・データのサブセットをリレーショナル表からPGXにグラフとして直接ロードします。構成ファイルでは、頂点およびエッジ・プロバイダの概念を使用して、リレーショナル形式からグラフ形式へのマッピングを指定します。

ノート:

vertex_providersプロパティおよびedge_providersプロパティを指定すると、データがグラフの最適化表現にロードされます。

{
    "name":"hr",
    "jdbc_url":"jdbc:oracle:thin:@myhost:1521/orcl",
    "username":"hr",
    "keystore_alias":"database1",
    "vertex_id_strategy": "no_ids",
    "vertex_providers":[
        {
            "name":"Employees",
            "format":"rdbms",
            "database_table_name":"EMPLOYEES",
            "key_column":"EMPLOYEE_ID",
            "key_type": "string",
            "props":[
                {
                    "name":"FIRST_NAME",
                    "type":"string"
                },
                {
                    "name":"LAST_NAME",
                    "type":"string"
                },
                {
                    "name":"EMAIL",
                    "type":"string"
                },
                {
                    "name":"SALARY",
                    "type":"long"
                }
            ]
        },
        {
            "name":"Jobs",
            "format":"rdbms",
            "database_table_name":"JOBS",
            "key_column":"JOB_ID",
            "key_type": "string",
            "props":[
                {
                    "name":"JOB_TITLE",
                    "type":"string"
                }
            ]
        },
        {
            "name":"Departments",
            "format":"rdbms",
            "database_table_name":"DEPARTMENTS",
            "key_column":"DEPARTMENT_ID",
            "key_type": "string",
            "props":[
                {
                    "name":"DEPARTMENT_NAME",
                    "type":"string"
                }
            ]
        }
    ],
    "edge_providers":[
        {
            "name":"WorksFor",
            "format":"rdbms",
            "database_table_name":"EMPLOYEES",
            "key_column":"EMPLOYEE_ID",
            "source_column":"EMPLOYEE_ID",
            "destination_column":"EMPLOYEE_ID",
            "source_vertex_provider":"Employees",
            "destination_vertex_provider":"Employees"
        },
        {
            "name":"WorksAs",
            "format":"rdbms",
            "database_table_name":"EMPLOYEES",
            "key_column":"EMPLOYEE_ID",
            "source_column":"EMPLOYEE_ID",
            "destination_column":"JOB_ID",
            "source_vertex_provider":"Employees",
            "destination_vertex_provider":"Jobs"
        },
        {
            "name":"WorkedAt",
            "format":"rdbms",
            "database_table_name":"JOB_HISTORY",
            "key_column":"EMPLOYEE_ID",
            "source_column":"EMPLOYEE_ID",
            "destination_column":"DEPARTMENT_ID",
            "source_vertex_provider":"Employees",
            "destination_vertex_provider":"Departments",
            "props":[
                {
                    "name":"START_DATE",
                    "type":"local_date"
                },
                {
                    "name":"END_DATE",
                    "type":"local_date"
                }
            ]
        }
    ]
}

頂点IDとエッジIDに関するノート:

PGXでは、デフォルトで、グラフ内の頂点およびエッジごとに一意の識別子が存在するよう強制されるため、これらは、PgxGraph.getVertex(ID id)およびPgxGraph.getEdge(ID id)の使用、または組込みのid()メソッドを使用したPGQL問合せによって取得できます。

頂点IDを生成するデフォルトの戦略は、グラフのロード時に指定されたキーを使用することです。その場合、各頂点は、頂点のすべてのタイプにわたって一意である頂点キーを持つ必要があります。エッジの場合、デフォルトではエッジ・データにキーは必要なく、エッジIDはPGXによって自動的に生成されます。エッジIDの生成は、確定的であるとはかぎりません。必要に応じて、エッジ・キーをIDとしてロードすることもできます。

ただし、パーティション化されたグラフでこのような識別子を定義するのは煩雑である可能性があるため、前の例に示すように、vertex_id_strategyおよびedge_id_strategyグラフ構成フィールドを値no_idに設定することによって、頂点またはエッジ(あるいはその両方)の要件を無効にできます。頂点(各エッジ) IDを無効にするということは、PGXが頂点(各エッジ) ID (前述のIDを含む)を使用したAPIへのコールを禁止することを意味します。

これらのAPIをコールするが、グローバルに一意のIDがリレーショナル表にない場合は、新しいIDを生成するunstable_generated_ids生成方針を指定できます。名前が示すとおり、入力表内で元のIDとの相関はなく、これらのIDが安定しているという保証はありません。エッジIDと同じように、同じ入力表を2回ロードすると、同じ頂点に対して2つの異なるIDが生成される可能性があります。

データの読み取り

ここで、次のいずれかの方法を使用して、キーストアと構成ファイルの両方をPGXに渡すことにより、データベースに接続してデータを読み取るようPGXに指示できます。

  • グラフ・シェルで対話的に

    グラフ・シェルを使用している場合は、--secret_storeオプションで起動します。キーストア・パスワードを入力し、現在のセッションにキーストアをアタッチするよう要求されます。次に例を示します。

    cd /opt/oracle/graph
    ./bin/opg-jshell --secret_store /etc/my-secrets/keystore.p12
     
     enter password for keystore /etc/my-secrets/keystore.p12:
    

    シェルの内部では、通常のPGX APIを使用し、readGraphWithProperties APIに書き込んだJSONファイルを渡すことで、グラフをメモリーに読み込むことができます。

    opg-jshell-rdbms> var graph = session.readGraphWithProperties("config.json")
    graph ==> PgxGraph[name=hr,N=215,E=415,created=1576882388130]
    
  • PGXの事前ロード済グラフとして
    サーバー管理者として、サーバー起動時にメモリーにグラフをロードするようPGXに指定できます。そのためには、/etc/oracle/graph/pgx.confにあるPGX構成ファイルを変更し、グラフ構成ファイルのパスをpreload_graphsセクションに追加します。次に例を示します。
    {
      ...
      "preload_graphs": [{
        "name": "hr", 
        "path": "/path/to/config.json"
      }],
      ...
    }
    rootユーザーとして、/etc/systemd/system/pgx.serviceにあるサービス・ファイルを編集し、ExecStartコマンドを変更してパスワードを含むキーストアの場所を指定します。
    
    ExecStart=/bin/bash start-server --secret-store /etc/keystore.p12

    ノート:

    これを機能させるため、/etc/keystore.p12をパスワードで保護しないでください。かわりに、oraclegraphユーザーのみが読取り可能なファイル・システム権限によってファイルを保護します。
    ファイルを編集したら、次を使用して変更をリロードします。
    sudo systemctl daemon-reload
    最後に、サーバーを起動します。
    sudo systemctl start pgx
  • Javaアプリケーション

    キーストアをJavaアプリケーションに登録するには、PgxSessionオブジェクトでregisterKeystore() APIを使用します。次に例を示します。

    import oracle.pgx.api.*;
     
    class Main {
       
      public static void main(String[] args) throws Exception {
        String baseUrl = args[0];
        String keystorePath = "/etc/my-secrets/keystore.p12";
        char[] keystorePassword = args[1].toCharArray();
        String graphConfigPath = args[2];
        ServerInstance instance = Pgx.getInstance(baseUrl);
        try (PgxSession session = instance.createSession("my-session")) {
          session.registerKeystore(keystorePath, keystorePassword);
          PgxGraph graph = session.readGraphWithProperties(graphConfigPath);
          System.out.println("N = " + graph.getNumVertices() + " E = " + graph.getNumEdges());
        }
      }
    }
    
    前述のサンプル・プログラムは、Oracle Graphクライアント・パッケージを使用してコンパイルおよび実行できます。次に例を示します。
    cd $GRAPH_CLIENT
    // create Main.java with above contents
    javac -cp 'lib/*' Main.java
    java -cp '.:conf:lib/*' Main http://myhost:7007 MyKeystorePassword path/to/config.json
    

グラフ・クライアント・アプリケーションのセキュア・コーディングのヒント

グラフ・クライアント・アプリケーションを作成する場合は、いずれのファイルまたはコードにもパスワードやその他のシークレットをクリア・テキストで格納しないでください。

コマンドライン引数からパスワードやその他のシークレットを受け入れないでください。かわりに、JDKからConsole.html#readPassword()を使用してください。

3.2.5 トークンの有効期限

デフォルトでは、トークンは1時間有効です。

内部的には、グラフ・クライアントは30分未満で期限が切れるトークンを自動的に更新します。これは、データベースで資格証明を再認証することでも構成できます。デフォルトでは、トークンは最大で24回だけ自動的に更新できるため、その後は再度ログインする必要があります。

自動更新の最大数に達した場合は、GraphServer#reauthenticate (instance, "<user>", "<password>") APIを使用すると、セッション・データを失うことなく再度ログインできます。次に例を示します。


opg> var graph = session.readGraphWithProperties(config) // fails because token cannot be renewed anymore
opg> GraphServer.reauthenticate(instance, "<user>", "<password>") // log in again
opg> var graph = session.readGraphWithProperties(config)          // works now 
 

3.2.6 拡張アクセス構成

pgx.confレルム・オプションの次のフィールドをカスタマイズして、ログイン動作をカスタマイズできます。

表3-1 拡張アクセス構成のオプション

フィールド名 説明 デフォルト
token_expiration_seconds 生成されたベアラー・トークンが期限切れになるまでの秒数。 3600 (1時間)
connect_timeout_milliseconds 指定したJDBC URLへの接続試行がタイムアウトになり、ログイン試行が拒否されるまでの時間(ミリ秒)。 10000
max_pool_size 各ユーザーに許可される最大JDBC接続数。この数に達すると、現行ユーザーのデータベースからの読取り試行は失敗します。 64
max_num_users 許容するアクティブなサインイン済ユーザーの最大数。この数に達すると、グラフ・サーバーはログイン試行を拒否します。 512
max_num_token_refresh トークンを自動的にリフレッシュできる最大回数。この回数を超えると、ログインが再度要求されます。 24

トークンの有効期限が切れる前にクライアント側でリフレッシュ時間を構成するには、次のAPIを使用してログインします。

int refreshTimeBeforeTokenExpiry = 900; // in seconds, default is 1800 (30 minutes)
ServerInstance instance = GraphServer.getInstance("https://localhost:7007", "<database user>", "<database password>",
    refreshTimeBeforeTokenExpiry);

ノート:

前述のオプションは、レルムの実装がoracle.pg.identity.DatabaseRealmに構成されている場合にのみ機能します。
3.2.6.1 ロールおよび権限のカスタマイズ

ロールを追加および削除し、ロールの権限を指定することで、ロール・マッピングに対する権限を全面的にカスタマイズできます。ロールのかわりに個々のユーザーを認可することもできます。

このトピックでは、権限マッピングをカスタマイズする方法の例を示します。

3.2.6.1.1 ロールの追加と削除

認可リストを変更することで、新しいロール権限マッピングを追加したり、既存のマッピングを削除できます。

次に例を示します。


CREATE ROLE MY_CUSTOM_ROLE_1
GRANT PGX_SESSION_CREATE TO MY_CUSTOM_ROLE1 
GRANT PGX_SERVER_GET_INFO TO MY_CUSTOM_ROLE1 
GRANT MY_CUSTOM_ROLE1 TO SCOTT
3.2.6.1.2 個々のユーザーの権限の定義

ロールの権限の定義に加えて、個々のユーザーの権限を定義できます。

次に例を示します。

GRANT PGX_SESSION_CREATE TO SCOTT 
GRANT PGX_SERVER_GET_INFO TO SCOTT 

3.2.7グラフ・サーバーへのアクセス権の取消し

ユーザーがグラフ・サーバーにアクセスできることを取り消すには、pgx.confファイルでアクセス・ルールを定義した方法に応じて、データベースからユーザーを削除するか、ユーザーから対応するロールを取り消します。

次に例を示します。

REVOKE graph_developer FROM scott

グラフの権限の取消し

グラフに対するMANAGE権限を持っている場合は、PgxGraph#revokePermission APIを使用して、ユーザーまたはロールからグラフ・アクセス権を取り消すことができます。次に例を示します。

PgxGraph g = ...
g.revokePermission(new PgxRole("GRAPH_DEVELOPER")) // revokes previously granted role access
g.revokePermission(new PgxUser("SCOTT")) // revokes previously granted user access

3.2.8 カスタム認可ルールの例

開発者にカスタム認可ルールを定義できます。

例3-1 開発者によるカスタム・グラフ・アルゴリズムの使用の許可

開発者がカスタム・グラフ・アルゴリズムをコンパイルできるようにするには(カスタムPGXグラフ・アルゴリズムの使用を参照)、次の静的権限を権限のリストに追加します。

GRANT PGX_SESSION_COMPILE_ALGORITHM TO GRAPH_DEVELOPER

例3-2 開発者によるグラフの公開の許可

グラフ・サーバー・ユーザーがグラフを公開したり、Oracle Databaseで作成された他のユーザーとグラフを共有できるようにすると、データベース認可モデルが破損します。データベースでグラフを操作する場合は、かわりにデータベースでGRANT文を使用します。PGグラフでこれを実行する方法の例は、OPG_APIS.GRANT_ACCESS APIを参照してください。リレーショナル表から読み取る場合は、表に対して通常のGRANT READ文を使用します。

開発者がグラフを公開できるようにするには、次の静的権限を権限のリストに追加します。

GRANT PGX_SESSION_ADD_PUBLISHED_GRAPH TO GRAPH_DEVELOPER

グラフのみを公開しても、他のユーザーがグラフにアクセスできるようにはなりません。アクセスのタイプも指定する必要があります。グラフには、次の3つのレベルの権限があります。

  1. READ: PGX APIを介してまたはPGQL問合せでグラフ・データを読み取り、グラフでアナリストまたはカスタム・アルゴリズムを実行してサブグラフを作成したり、特定のグラフをクローニングできます。
  2. EXPORT: PgxGraph#store() APIを介してグラフをエクスポートします。READ権限が含まれます。グラフをエクスポートするには、EXPORT権限に加えて、ファイル・システムに対するWRITE権限も必要です。
  3. MANAGE: グラフまたはスナップショットを公開し、グラフに対して権限を付与または取り消します。EXPORT権限が含まれます。

グラフの作成者は、グラフに付与されたMANAGE権限を自動的に取得します。MANAGE権限を持っている場合は、グラフに対するREADまたはEXPORT権限を他のロールまたはユーザーに付与できます。グラフには、MANAGEを付与できません。次のuserAというユーザーの例に、その方法を示します。

import oracle.pgx.api.*
import oracle.pgx.common.auth.*
 
 
PgxSession session = GraphServer.getInstance("<base-url>", "<userA>", "<password-of-userA").createSession("userA")
PgxGraph g = session.readGraphWithProperties("examples/sample-graph.json", "sample-graph")
g.grantPermission(new PgxRole("GRAPH_DEVELOPER"), PgxResourcePermission.READ)
g.publish()
これで、次のuserBの例に示すように、GRAPH_DEVELOPERロールを持つ他のユーザーがこのグラフにアクセスし、そのグラフに対するREADアクセス権を持つことができます。
PgxSession session = GraphServer.getInstance("<base-url>", "<userB>", "<password-of-userB").createSession("userB")
PgxGraph g = session.getGraph("sample-graph")
g.queryPgql("select count(*) from match (v)").print().close()

同様に、次の例に示すように、グラフはロールではなく個々のユーザーと共有できます。

g.grantPermission(new PgxUser("OTHER_USER"), PgxResourcePermission.EXPORT)

OTHER_USERは、グラフgに対するEXPORT権限を受け取るユーザーのユーザー名です。

例3-3 開発者による事前ロード済グラフへのアクセスの許可

開発者が事前にロードされたグラフ(グラフ・サーバーの起動時にロードされたグラフ)にアクセスできるようにするには、pgx.confファイルで事前にロードされたグラフに対する読取り権限を付与します。次に例を示します。

"preload_graphs": [{
  "path": "/data/my-graph.json",
  "name": "global_graph"
}],
"authorization": [{
  "pgx_role": "GRAPH_DEVELOPER",
  "pgx_permissions": [{
    "preloaded_graph": "global_graph"
    "grant": "read"
  },
...

READ、EXPORTまたはMANAGE権限を付与できます。

例3-4 開発者によるHadoop分散ファイル・システム(HDFS)またはローカル・ファイル・システムへのアクセスの許可

開発者がHDFSからファイルを読み取れるようにするには、まずHDFSディレクトリを宣言してから、読取りまたは書込み権限にマップする必要があります。次に例を示します。


CREATE OR REPLACE DIRECTORY pgx_file_location AS 'hdfs:/data/graphs'
GRANT READ ON DIRECTORY pgx_file_location TO GRAPH_DEVELOPER

同様に、GRANT WRITEを使用して別の権限を追加し、書込みアクセスを許可できます。グラフをエクスポートするには、このような書込みアクセス権が必要です。

ローカル・ファイル・システム(グラフ・サーバーが実行されている場所)へのアクセス権は、同じ方法で付与できます。唯一の違いは、場所がhdfs:接頭辞のない絶対ファイル・パスになります。次に例を示します。

CREATE OR REPLACE DIRECTORY pgx_file_location AS '/opt/oracle/graph/data'

前述の構成に加えて、グラフ・サーバー・プロセスを実行するオペレーティング・システム・ユーザーには、これらのディレクトリへの実際の読取りまたは書込みに対応するディレクトリ権限が必要です。

3.3 Oracle Databaseのグラフとグラフ・サーバーの同期の維持

FlashbackSynchronizer APIを使用すると、データベースでのグラフに対する変更をメモリー内の対応するPgxGraphオブジェクトに自動的に適用できるため、両方の同期が保たれます。

このAPIでは、Oracleのフラッシュバック・テクノロジを使用して最後のフェッチ以降のデータベースでの変更をフェッチし、ChangeSet APIを使用してそれらの変更をグラフ・サーバーにプッシュします。変更が適用された後、グラフ・サーバーの通常のスナップショット・セマンティクスが適用されます。デルタ・フェッチが適用されるたびに新しいインメモリー・スナップショットが作成されます。スナップショットの作成に対して同時に実行されている問合せまたはアルゴリズムは、対応するセッションがsession.setSnapshot(graph, PgxSession.LATEST_SNAPSHOT)プロシージャをコールしてPgxGraphオブジェクトを最新の状態にリフレッシュするまで、変更の影響を受けません。

Oracle Flashbackテクノロジの詳細は、データベース開発ガイドを参照してください。

同期化の前提条件

Oracleデータベースでフラッシュバックが有効になっている必要があり、同期の実行に使用するデータベース・ユーザーには次のものが必要です。

  • 同期を維持する必要があるすべての表に対する読取りアクセス権。
  • フラッシュバックAPIを使用するための権限。次に例を示します。
    grant execute on dbms_flashback to <user>

また、ユースケースに必要な時間の間変更を保持するようにデータベースを構成する必要もあります。

同期化の制限事項

シンクロナイザAPIの現在の制限事項は次のとおりです。

  • すべてのプロバイダがデータベース表であるパーティション化されたグラフ構成のみがサポートされます。
  • 頂点とエッジの両方のID方法を次のように設定する必要があります。
    "vertex_id_strategy": "keys_as_ids",
    "edge_id_strategy": "keys_as_ids"
    
  • 各エッジ/頂点プロバイダは、キー・マッピングを作成するように構成する必要があります。グラフ構成の各プロバイダ・ブロックに、次のロード・セクションを追加します。
    "loading": {
      "create_key_mapping": true
    }
    

    これは、頂点およびエッジにグローバルに一意のID列が必要であることを意味します。

  • 各エッジ/頂点プロバイダは、ユーザー名フィールドを設定して表の所有者を指定する必要があります。たとえば、ユーザーSCOTTが表を所有している場合は、その表のプロバイダ・ブロックにユーザー名を適宜設定します。
    "username": "scott"
  • ルート・ロード・ブロックで、スナップショット・ソースをchange_setに設定する必要があります。
    "loading": {
      "snapshots_source": "change_set"
    }
    

一部のオプションを含め、詳細な例は、次のトピックを参照してください。

3.3.1 同期化の例

同期の実行例として、PERSONSおよびFRIENDSHIPSというOracle Database表があるとします。

CREATE TABLE PERSONS (
  PERSON_ID NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
  NAME VARCHAR2(200),
  BIRTHDATE DATE,
  HEIGHT FLOAT DEFAULT ON NULL 0,
  INT_PROP INT DEFAULT ON NULL 0,
  CONSTRAINT person_pk PRIMARY KEY (PERSON_ID)
);
 
CREATE TABLE FRIENDSHIPS ( 
  FRIENDSHIP_ID NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
  PERSON_A NUMBER,
  PERSON_B NUMBER,
  MEETING_DATE DATE,
  TS_PROP TIMESTAMP,
  CONSTRAINT fk_PERSON_A_ID FOREIGN KEY (PERSON_A) REFERENCES persons(PERSON_ID),
  CONSTRAINT fk_PERSON_B_ID FOREIGN KEY (PERSON_B) REFERENCES persons(PERSON_ID)
);

これらの表にサンプル・データをいくつか追加します。

INSERT INTO PERSONS (NAME, HEIGHT, BIRTHDATE) VALUES ('John', 1.80, to_date('13/06/1963', 'DD/MM/YYYY'));
INSERT INTO PERSONS (NAME, HEIGHT, BIRTHDATE) VALUES ('Mary', 1.65, to_date('25/09/1982', 'DD/MM/YYYY'));
INSERT INTO PERSONS (NAME, HEIGHT, BIRTHDATE) VALUES ('Bob', 1.75, to_date('11/03/1966', 'DD/MM/YYYY'));
INSERT INTO PERSONS (NAME, HEIGHT, BIRTHDATE) VALUES ('Alice', 1.70, to_date('01/02/1987', 'DD/MM/YYYY'));
 
INSERT INTO FRIENDSHIPS (PERSON_A, PERSON_B, MEETING_DATE) VALUES (1, 3, to_date('01/09/1972', 'DD/MM/YYYY'));
INSERT INTO FRIENDSHIPS (PERSON_A, PERSON_B, MEETING_DATE) VALUES (2, 4, to_date('19/09/1992', 'DD/MM/YYYY'));
INSERT INTO FRIENDSHIPS (PERSON_A, PERSON_B, MEETING_DATE) VALUES (4, 2, to_date('19/09/1992', 'DD/MM/YYYY'));
INSERT INTO FRIENDSHIPS (PERSON_A, PERSON_B, MEETING_DATE) VALUES (3, 2, to_date('10/07/2001', 'DD/MM/YYYY'));

グラフ構成の接続情報を使用した同期化

次に、グラフ構成の接続情報を使用して同期化します。次のサンプル・グラフ構成(KeystoreGraphConfigExample)があり、これらの表をグラフとして読み取ります。

{
  "name": "PeopleFriendships",
  "optimized_for": "updates",
  "edge_id_strategy": "keys_as_ids",
  "edge_id_type": "long",
  "vertex_id_type": "long",
  "jdbc_url": "<jdbc_url>",
  "username": "<username>",
  "keystore_alias": "<keystore_alias>",
  "vertex_providers": [
    {
      "format": "rdbms",
      "username": "<username>",
      "key_type": "long",
      "name": "person",
      "database_table_name": "PERSONS",
      "key_column": "PERSON_ID",
      "props": [
        ...
      ],
      "loading": {
        "create_key_mapping": true
      }
    }
  ],
  "edge_providers": [
    {
      "format": "rdbms",
      "username": "<username>",
      "name": "friendOf",
      "source_vertex_provider": "person",
      "destination_vertex_provider": "person",
      "database_table_name": "FRIENDSHIPS",
      "source_column": "PERSON_A",
      "destination_column": "PERSON_B",
      "key_column": "FRIENDSHIP_ID",
      "key_type":"long",
      "props": [
        ...
      ],
      "loading": {
        "create_key_mapping": true
      }
    }
  ],
  "loading": {
    "snapshots_source": "change_set"
  }
}

(前述の例では、値<jdbc_url><username>および<keystore_alias>を、データベースに接続するための値に置き換えます。)

Oracle Property Graph JShellをオープンし(起動時にデータベース・パスワードを含むキーストアを必ず登録します)、グラフをメモリーにロードします。

var pgxGraph = session.readGraphWithProperties("persons_graph.json");

次の出力行は、サンプル・グラフに4つの頂点と4つのエッジがあることを示しています。

pgxGraph ==> PgxGraph[name=PeopleFriendships,N=4,E=4,created=1594754376861]

ここで、データベースに戻り、新しく数行を挿入します。

INSERT INTO PERSONS (NAME, BIRTHDATE, HEIGHT) VALUES ('Mariana',to_date('21/08/1996','DD/MM/YYYY'),1.65);
INSERT INTO PERSONS (NAME, BIRTHDATE, HEIGHT) VALUES ('Francisco',to_date('13/06/1963','DD/MM/YYYY'),1.75);
INSERT INTO FRIENDSHIPS (PERSON_A, PERSON_B, MEETING_DATE) VALUES (1, 6, to_date('13/06/2013','DD/MM/YYYY'));
COMMIT;

JShellに戻ると、FlashbackSynchronizer APIを使用してこれらの変更を自動的にフェッチして適用できるようになっています。

var synchronizer = new Synchronizer.Builder<FlashbackSynchronizer>().setType(FlashbackSynchronizer.class).setGraph(pgxGraph).build()
pgxGraph = synchronizer.sync()

出力からわかるように、新しい頂点およびエッジが適用されています。

pgxGraph ==> PgxGraph[name=PeopleFriendships,N=6,E=5,created=1594754376861]

pgxGraph = synchronizer.sync()は、次のコールと同等です。

synchronizer.sync()
session.setSnapshot(pgxGraph, PgxSession.LATEST_SNAPSHOT)

変更のフェッチおよび適用の分割

synchronizer.sync()を呼び出すと、1回のコールで変更のフェッチと適用が行われます。しかし、このプロセスを別個の fetch()呼出しとapply()呼出しに分割することで、より複雑な更新ロジックをエンコードできます。次に例を示します。

synchronizer.fetch() // fetches changes from the database
if (synchronizer.getGraphDelta().getTotalNumberOfChanges() > 100) {
  // only create snapshot if there have been more than 100 changes
  synchronizer.apply() 
}

明示的なOracle接続を使用した同期化

シンクロナイザAPIでは、クライアント側の変更をフェッチします。つまり、クライアントはデータベースに接続する必要があります。前述の例では、そのために、ロードされたPgxGraphオブジェクトのグラフ構成で入手できる接続情報を読み取りました。ただし、次のような場合にPgxGraphオブジェクトから接続情報を取得できないことがあります。

  • 関連付けられたグラフ構成にデータベース接続情報が含まれておらず、ログイン・ユーザーの資格証明を使用してグラフがロードされた場合。または、
  • 関連付けられたグラフ構成に、サーバー側に格納された接続に対応するデータソースIDが含まれている場合。

このような場合は、Oracle接続オブジェクトを渡して、変更のフェッチに使用するシンクロナイザ・オブジェクトを構築できます。次に例(ExampleGraphConfig.json)を示します。

String jdbcUrl = "<JDBC URL>";
String username = "<USERNAME>";
String password = "<PASSWORD>";
Connection connection = DriverManager.getConnection(jdbcUrl, username, password)
Synchronizer synchronizer = new Synchronizer.Builder<FlashbackSynchronizer>()
    .setType(FlashbackSynchronizer.class)
    .setGraph(pgxGraph)
    .setConnection(connection)
    .build();

3.4 インメモリー・アナリストの構成

起動時にインメモリー・アナリストに単一のJSONファイルを割り当てることで、インメモリー・アナリスト・エンジンとその実行時動作を構成できます。

このファイルには、次の表に示すパラメータを含めることができます。表の後に例が続きます。

構成ファイルを指定するには、インメモリー・アナリストへの構成ファイルの指定を参照してください。

ノート:

  • パラメータ値の相対パスは常に、指定された構成ファイルの親ディレクトリを基準として解決されます。たとえば、構成ファイルが/pgx/conf/pgx.confである場合、そのファイル内のファイル・パスgraph-configs/my-graph.bin.json/pgx/conf/graph-configs/my-graph.bin.jsonに解決されます。

  • パラメータのデフォルト値は、広範囲のアルゴリズムにわたって最高のパフォーマンスを得るように最適化されています。作業負荷に応じて、様々な戦略、サイズおよびしきい値を試行してパフォーマンスをさらに向上させることができます。

表3-2 インメモリー・アナリストの構成パラメータ

パラメータ 説明 デフォルト

admin_request_cache_timeout

integer

管理要求の結果がキャッシュから削除されるまでの秒数。完了していないリクエストまたはまだ消費されていないリクエストは、このタイムアウトから除外されます。ノート: これが関連するのは、PGXがWebアプリケーションとしてデプロイされている場合のみです。

60

allow_idle_timeout_overwrite

boolean

trueの場合、セッションによってデフォルトのアイドル・タイムアウトが上書きされる可能性があります。

true

allow_local_filesystem

boolean

クライアント/サーバー・モードでローカル・ファイル・システムからロードできます。デフォルトはfalseです。trueに設定する場合、ディレクトリをリストするプロパティdatasource_dir_whitelistを追加で指定します。サーバーは、ここにリストされているディレクトリからのみ読み取ることができます。

false

allow_override_scheduling_information

boolean

trueの場合、タスクの重み、タスク優先度、スレッド数などのスケジューリング情報のオーバーライドをすべてのユーザーに許可します

true

allowed_remote_loading_locations 文字列の配列

リモートの場所(http、https、ftp、ftps、s3、hdfs)からPGXエンジンへのグラフのロードを許可します。デフォルトは空です。サポートされる値は* (アスタリスク)です。これは、すべてのリモートの場所が許可されることを意味します。この設定の値に関係なく、事前ロードされたグラフは任意の場所からロードされていることに注意してください。警告: * (アスタリスク)を指定するのは、PGXリモート・インタフェースのユーザーにローカル・ファイル・システム上のファイルへのアクセスを明示的に許可する場合のみにする必要があります。

[]

allow_task_timeout_overwrite

boolean

trueの場合、セッションによってデフォルトのタスク・タイムアウトが上書きされる可能性があります。

true

allow_user_auto_refresh

boolean

trueの場合、ユーザーはロードするグラフの自動リフレッシュを有効化できます。falseの場合、preload_graphsに指定されているグラフのみ、自動リフレッシュを有効にできます。

false

allowed_remote_loading_locations 文字列の配列 (このオプションにより、セキュリティが低下する可能性があります。処理の内容を理解している場合にのみ使用してください。)リモートの場所(http、https、ftp、ftps、s3、hdfs)からPGXエンジンへのグラフのロードを許可します。空の場合は、デフォルトの場合と同様に、リモートの場所は許可されません。配列で*を指定すると、すべてのリモートの場所が許可されます。現在サポートされているのは値*のみです。この設定の値に関係なく、事前ロードされたグラフは任意の場所からロードされていることに注意してください。 []
basic_scheduler_config object 分岐結合プールのバックエンドの構成パラメータ。 null

bfs_iterate_que_task_size

integer

BFS反復QUEフェーズのタスク・サイズ。

128

bfs_threshold_parent_read_based

number

親読取りベースのアクセス戦略に切り替えるBFS横断レベル・アイテムのしきい値。

0.05

bfs_threshold_read_based

integer

読取りベースのアクセス戦略に切り替えるBFS横断レベル・アイテムのしきい値。

1024

bfs_threshold_single_threaded

integer

BFSのトラバース・レベル・アイテムの頂点がシングル・スレッドでアクセスされる回数。

128

character_set

string

PGX全体で使用する標準文字セット。デフォルトはUTF-8です。ノート: 一部の書式には互換性がない可能性があります。

utf-8

cni_diff_factor_default

integer

共通の隣接イテレータ実装で使用されるデフォルトの差分係数値。

8

cni_small_default

integer

部分配列が小さいとみなされるしきい値を示すために、共通の隣接イテレータ実装で使用されるデフォルト値。

128

cni_stop_recursion_default

integer

バイナリ検索アプローチが適用される最小サイズを示すために、共通の隣接イテレータ実装で使用されるデフォルト値。

96

datasource_dir_whitelist 文字列の配列 allow_local_filesystemが設定されている場合は、ファイルの読取りが許可されているディレクトリのリスト。 []

dfs_threshold_large

integer

多数の頂点に対して最適化されたデータ構造にDFS実装が切り替わる判断の基準となる頂点へのアクセス数。

4096

enable_csrf_token_checks

boolean

trueの場合、PGX Webアプリケーションは、クライアントが送信したクロスサイト・リクエスト・フォージェリ(CSRF)のトークンCookieとリクエスト・パラメータが存在して一致することを検証します。これを行うのは、CSRF攻撃を回避するためです。

true

enable_gm_compiler

boolean

[solaris studioを使用したプロファイリングに該当します]有効にする場合、ラベルは「er_label」コマンドを使用して実験されます。

false

enable_shutdown_cleanup_hook

boolean

trueの場合、PGXは、JVMのシャットダウン時にPGXを自動的にシャットダウンするJVMシャットダウン・フックを追加します。注意: シャットダウン・フックを非アクティブにし、PGXを明示的にシャットダウンしない場合、一時ディレクトリの汚染につながる可能性があります。

true

enterprise_scheduler_config

object

エンタープライズ・スケジューラの構成パラメータ。

null

enterprise_scheduler_flags

object

[enterprise_schedulerに該当します]エンタープライズ・スケジューラ固有の設定。

null

explicit_spin_locks

boolean

trueの場合、ロックが使用可能になるまで、ループ内で明示的にスピンします。falseの場合、JVMに依存するJDKのロックを使用し、コンテキスト切替えとスピンのどちらを行うかを決定します。この値をtrueに設定すると、通常はパフォーマンスが向上します。

true

graph_algorithm_language enum[GM_LEGACY, GM, JAVA] 使用するフロントエンド・コンパイラ。 gm
graph_validation_lever enum[low, high] 新しくロードまたは作成されたグラフで実行される検証のレベル。 low
ignore_incompatible_backend_operations boolean trueの場合、RTSまたはFJプールで互換性のない操作および構成値を検出した場合のみログに記録します。falseの場合、例外がスローされます。 false
in_place_update_consistency enum[ALLLOW_INCONSISTENCIES, CANCEL_TASKS] インプレース更新の発生時に使用される一貫性モデル。インプレース更新が有効な場合のみ該当します。現在、更新がインプレースで適用されるのは、更新が構造的でない場合のみです(プロパティを変更するのみ)。現在2つのモデルが実装されています。1つのモデルでは更新の発生時に新しいタスクのみを遅延させますが、もう1つのモデルでは実行中のタスクも遅延させます。 allow_inconsistencies
init_pgql_on_startup boolean trueの場合、PGQLはPGXの起動時に直接初期化されます。そうでない場合、PGQLの初回使用時に初期化されます。 true
interval_to_poll_max integer いったん達するとジョブ・ステータスのポーリング間隔が固定される指数関数的バックオフの上限(ミリ秒) 1000
java_home_dir string Javaのホームディレクトリへのパス。<system-java-home-dir>に設定した場合は、java.homeシステム・プロパティを使用します。 null
large_array_threshold integer 配列のサイズが大きすぎて通常のJava配列を使用できなくなるときのしきい値。これは使用しているJVMによって異なります。(Integer.MAX_VALUE - 3にデフォルト設定されます) 2147483644

max_active_sessions

integer

1回にアクティブにできるセッションの最大数。

1024

max_distinct_strings_per_pool integer [string_pooling_strategyが索引付けられている場合のみ該当します]プーリングを停止するまでのプロパティごとの個別の文字列数。制限に達すると、例外がスローされます。 65536

max_off_heap_size

integer

OutOfMemoryErrorがスローされるまでPGXが割り当てられるオフピーク・メモリーの最大容量(MB)。ノート: 端数処理および同期トレードオフのため、この制限を超えないことは保証されていません。これは、PGXが新しいメモリー割当てリクエストを拒否し始める場合のみ、しきい値として機能します。

<available-physical-memory>

max_queue_size_per_session

integer

キューに入れることができるセッション当たりの保留中タスクの最大数。セッションが最大数に達した場合、そのセッションの新しい着信リクエストは拒否されます。負の値は、制限がないことを意味します。

-1

max_snapshot_count

integer

エンジンで同時にロードされる可能性のあるスナップショットの数。自動または強制更新により新規スナップショットを作成できます。グラフのスナップショットの数がこのしきい値に到達すると、自動更新はこれ以上実行されなくなり、強制更新の結果、1つ以上のスナップショットがメモリーから削除されるまでは例外になります。値ゼロは、無制限数のスナップショットをサポートすることを示します。

0

memory_allocator enum[basic_allocator, enterprise_allocator] 使用するメモリー・アロケータ。 basic_allocator

memory_cleanup_interval

integer

メモリー・クリーンアップ間隔(秒)。

600

ms_bfs_frontier_type_strategy

enum[auto_grow, short, int]

MS-BFSフロンティアに使用するタイプ方針。

auto_grow

num_spin_locks

integer

生成された各アプリケーションがインスタンス化の際に作成するスピン・ロックの数。トレードオフ: 数値が小さくなると、メモリー消費量が少なくなり、数値が大きくなると、実行速度が速くなります(アルゴリズムがスピン・ロックを使用している場合)。

1024

parallelism integer スレッド・プールで使用されるワーカー・スレッド数。ノート: コール元のスレッドが別のスレッド・プールの一部である場合、この値は無視され、親プールの並列度が使用されます。 <number-of-cpus>
pattern_matching_supernode_cache_threshold integer スーパーノードになるノードの近隣の最小数。これは、パターン一致エンジン用です。 1000
pooling_factor number [string_pooling_strategyがon_heapの場合のみ該当します]この値は、文字列プールがプロパティのサイズと同じ大きさになることを阻止します。同じ大きさになると、プーリングの表示が無効になる可能性があります。 0.25

preload_graphs

オブジェクトの配列

起動時に登録されるグラフ構成のリスト。各アイテムには、グラフ構成へのパス、グラフの名前、およびグラフを公開するかどうかが含まれます。

[]

random_generator_strategy enum[non_deterministic, deterministic] PGXで乱数を生成する方法。 non_deterministic

random_seed

long

[決定論的乱数ジェネレータにのみ該当します]インメモリー・アナリストで使用される決定論的乱数ジェネレータのシード。デフォルトは-24466691093057031です。

-24466691093057031

release_memory_threshold

double

使用されたメモリーのしきい値パーセンテージ(小数)。この値を超えると、エンジンは未使用のグラフを解放し始めます。例: 値が0.0の場合、参照カウントがゼロになると同時にグラフが解放されます。つまり、そのグラフをロードしたすべてのセッションが破棄され、タイムアウトします。値が1.0の場合、グラフは解放されず、メモリーに収まらないグラフが必要になると、エンジンはすぐにOutOfMemoryErrorsをスローします。値が0.7の場合、合計メモリー使用量が使用可能な合計メモリーの70%未満であるかぎり、その時点でこれらを使用しているセッションがない場合でも、すべてのグラフがメモリー内に保持されます。消費量が70%を超え、別のグラフをロードする必要がある場合、メモリー使用量が70%を再度下回るまで、使用されていないグラフは解放されます。

0.85

revisit_threshold integer 到達するノードからの一致結果の最大数。 4096
scheduler enum[basic_scheduler, enterprise_scheduler] 使用するスケジューラ。basic_schedulerは、基本機能を備えたスケジューラを使用します。enterprise_schedulerは、複数のタスクを同時に実行してパフォーマンスを改善するための拡張エンタープライズ機能を備えたスケジューラを使用します。 advanced_scheduler

session_idle_timeout_secs

integer

アイドル・セッションのタイムアウト(秒)。ゼロ(0)はタイムアウトしないことを意味します

0

session_task_timeout_secs

integer

セッション(アルゴリズム、I/Oタスク)によって送信された長時間実行タスクの中断のタイムアウト(秒)。ゼロ(0)はタイムアウトしないことを意味します。

0

small_task_length

integer

処理の合計量がデフォルトのタスクの長さより小さい場合のタスクの長さ(タスク・スティーリング戦略の場合のみ該当します)。

128

spark_streams_interface

string

インタフェースの名前は、スパーク・データ通信に使用されます。

null

strict_mode

boolean

trueの場合、エンジンで構成の問題(無効なキー、不一致、その他の潜在的なエラーなど)が発生する場合は常に、例外がスローされ、ERRORレベルでログに記録されます。falseの場合、エンジンでは(重大度に応じて) ERROR/WARNレベルで問題が記録され、最適な推測を行われ、例外がスローされるのではなく、合理的なデフォルトが使用されます。

true

string_pooling_strategy enum[indexed, on_heap, none] [use_string_poolが有効な場合にのみ該当します]使用する文字列プーリング戦略。 on_heap

task_length

integer

デフォルトのタスクの長さ(タスク・スティーリング戦略の場合のみ該当します)。100から10000の範囲にする必要があります。トレードオフ: 小さい数値を指定すると、より詳細なタスクが生成され、スティーリング・スループットが高くなります。大きい値を指定すると、メモリー消費量およびGCアクティビティが減少します。

4096

tmp_dir

string

コンパイル・アーティファクトおよび他の一時データを格納するための一時ディレクトリ。<system-tmp-dir>に設定すると、基礎となるシステムの標準のtmpディレクトリ(Linuxでは/tmp)が使用されます。

<system-tmp-dir>

udf_config_directory

string

UDFファイルを含むディレクトリ・パス。

null

use_memory_mapper_for_reading_pgb boolean trueの場合、可能な場合はPGB形式でグラフを読み取るためにメモリーにマップされたファイルを使用します。falseの場合は、常にストリームベースの実装を使用します。 true
use_memory_mapper_for_storing_pgb boolean trueの場合、可能な場合はPGB形式でグラフを格納するためにメモリーにマップされたファイルを使用します。falseの場合は、常にストリームベースの実装を使用します。 true

エンタープライズ・スケジューラ・パラメータ

次のパラメータは、高度なスケジューラが使用されている場合にのみ該当します。(基本スケジューラを使用している場合は無視されます。)

  • analysis_task_config

    分析タスクの構成。型: object。デフォルト: prioritymediummax_threads<no-of-CPUs>weight<no-of-CPUs>

  • fast_analysis_task_config

    高速分析タスクの構成。型: object。デフォルト: priorityhighmax_threads<no-of-CPUs>weight1

  • maxnum_concurrent_io_tasks

    同時タスクの最大数。型: integer。デフォルト: 3

  • num_io_threads_per_task

    高速分析タスクの構成。型: object。デフォルト: <no-of-cpus>

基本的なスケジューラ・パラメータ

次のパラメータは、基本スケジューラが使用されている場合にのみ該当します。(高度なスケジューラを使用している場合は無視されます。)

  • num_workers_analysis

    分析タスクに使用するワーカー・スレッドの数。型: integer。デフォルト: <no-of-CPUs>

  • num_workers_fast_track_analysis

    ファスト・トラック分析タスクに使用するワーカー・スレッドの数。型: integer。デフォルト: 1

  • num_workers_io

    I/Oタスク(ロード/リフレッシュ/ディスクからの読取り/ディスクへの書込み)に使用するワーカー・スレッドの数。ファイルベース・ローダーは常にシングル・スレッドであるため、この値による影響はありません。データベース・ローダーは、I/Oワーカーごとに新しい接続をオープンします。デフォルト: <no-of-CPUs>

例3-5 最小インメモリー・アナリスト構成

次の例を使用すると、インメモリー・アナリストにより、32のワーカーを含む分析スレッド・プールが初期化されます。(その他すべてのパラメータに対してデフォルト値が使用されます。)

{
  "enterprise_scheduler_config": {
    "analysis_task_config": {
      "max_threads": 32
    }
  }
}

例3-6 事前ロードされた2つのグラフ

より多くのフィールドを設定し、PGXの起動時にメモリーにロードされるよう2つの固定グラフを指定します。

{ 
  "enterprise_scheduler_config": {
    "analysis_task_config": {
      "max_threads": 32
    },
    "fast_analysis_task_config": {
      "max_threads": 32
    }
  }, 
  "memory_cleanup_interval": 600,
  "max_active_sessions": 1, 
  "release_memory_threshold": 0.2, 
  "preload_graphs": [
    {
      "path": "graph-configs/my-graph.bin.json",
      "name": "my-graph"
    },
    {
      "path": "graph-configs/my-other-graph.adj.json",
      "name": "my-other-graph",
      "publish": false
    }
  ]
}

3.4.1 インメモリー・アナリストへの構成ファイルの指定

インメモリー・アナリスト構成ファイルは、ServerInstance#startEngine (またはそのバリアントのいずれか)が呼び出されるたびに、その開始時にインメモリー・アナリストによって解析されます。構成ファイルへのパスはインメモリー・アナリストに書き込むか、プログラムで指定できます。このトピックでは、ファイルを指定するいくつかの方法について説明します

プログラムを使用する場合

すべての構成フィールドがJava列挙として存在します。例:

Map<PgxConfig.Field, Object> pgxCfg = new HashMap<>();
pgxCfg.put(PgxConfig.Field.MEMORY_CLEANUP_INTERVAL, 600);

ServerInstance instance = ...
instance.startEngine(pgxCfg);

明示的に設定されていないパラメータはすべて、デフォルト値が使用されます。

ファイルを明示的に使用する場合

マップのかわりに、インメモリー・アナリスト構成JSONファイルへのパスを書き込むことができます。例:

instance.startEngine("path/to/pgx.conf"); // file on local file system
instance.startEngine("classpath:/path/to/pgx.conf"); // file on current classpath

その他すべてのプロトコルについては、JSONファイルへの入力ストリームに直接書き込むことができます。例:

InputStream is = ...
instance.startEngine(is);

ファイルを暗黙的に使用する場合

引数を指定せずにstartEngine()をコールした場合、インメモリー・アナリストは次の場所で構成ファイルを検索し、ファイルが見つかったときに停止します。

  • Javaシステム・プロパティpgx_confで見つかったファイル・パス。例: java -Dpgx_conf=conf/my.pgx.config.json ...

  • 現在のクラスパスのルート・ディレクトリにあるpgx.confというファイル

  • 現在のSystem.getProperty("user.dir")ディレクトリを基準にしたルート・ディレクトリ内のpgx.confという名前のファイル

ノート: 構成の指定はオプションです。特定の構成ファイルでフィールドが見つからない場合や、構成ファイルが指定されていない場合、各フィールドのデフォルト値が使用されます。

ローカル・シェルの使用

シェルがローカルのインメモリー・アナリスト・インスタンスを構成する方法を変更するには、$PGX_HOME/conf/pgx.confを編集します。変更内容は、$PGX_HOME/bin/pgxを次回起動したときに反映されます。

また、次の例に示すように、構成ファイルの場所を変更することもできます。

./bin/opg --pgx_conf path/to/my/other/pgx.conf

システム・プロパティの設定

インメモリー・アナリストが実行されているJVMに-Dpgx.<FIELD>=<VALUE>引数を書き込むことによって、Javaシステム・プロパティを使用して任意のパラメータを設定できます。システム・プロパティの設定により他の構成が上書きされることに注意してください。次の例では、他の構成の内容に関係なく、最大オフヒープ・サイズを256GBに設定します。

java -Dpgx.max_off_heap_size=256000 ...

環境変数の設定

インメモリー・アナリストが実行されるJVMの環境変数に「PGX_」を追加することによって、環境変数を使用して任意のパラメータを設定することもできます。環境変数の設定によって他の構成が上書きされることに注意してください。ただし、システム・プロパティと環境変数が同じパラメータに設定されている場合、システム・プロパティの値が使用されます。次の例では、環境変数を使用して最大オフヒープ・サイズを256GBに設定します。

PGX_MAX_OFF_HEAP_SIZE=256000 java ...

3.5 グラフ・スナップショットのディスクへの格納

Javaまたはシェルを使用してグラフをメモリーに読み込んだ後、PageRankアルゴリズムの実行や頂点プロパティとしての値の保存など、グラフに変更を加えた場合は、このグラフのスナップショットをディスクに保存できます。

これは、新しいバージョンに移行するためにインメモリー・アナリスト・サーバーを停止する必要がある場合や、他のなんらかの理由でサーバーを停止する必要がある場合など、グラフの状態をメモリーに保存する際に役立ちます。

(HTTP/RESTへのグラフの格納は現在サポートされていません)。

グラフの状態をメモリーに保存する場合、たとえば新しいバージョンに移行するためにインメモリー・アナリスト・サーバーを停止する必要がある場合や、他のなんらかの理由でサーバーを停止する必要がある場合は、グラフのスナップショットをバイナリ形式のファイル(PGBファイルと呼ばれる)で保存できます。

一般的には、実行されたグラフ問合せと分析APIを保存し、インメモリー・アナリストを再起動した後、APIを再ロードして再実行することをお薦めします。ただし、グラフの状態を保存する必要がある場合は、次の例のロジックを使用して、シェルからグラフ・スナップショットを保存できます。

3層デプロイメントでは、ファイルはサーバー側のファイル・システムに書き込まれます。また、書き込むファイルの場所がインメモリー・アナリスト・サーバーで指定されていることを確認する必要があります。(Autonomous Databaseを使用したOracle Graphの3層デプロイメントで説明されているように、3層デプロイメントでは、PGXサーバー・ファイル・システムにアクセスするには、許可されている場所のリストを指定する必要があります。)

opg-jshell> var graph = session.createGraphBuilder().addVertex(1).addVertex(2).addVertex(3).addEdge(1,2).addEdge(2,3).addEdge(3, 1).build()
graph ==> PgxGraph[name=anonymous_graph_1,N=3,E=3,created=1581623669674]

opg-jshell> analyst.pagerank(graph)
$3 ==> VertexProperty[name=pagerank,type=double,graph=anonymous_graph_1]

// Now save the state of this graph

opg-jshell> g.store(Format.PGB, "/tmp/snapshot.pgb")
$4 ==> {"edge_props":[],"vertex_uris":["/tmp/snapshot.pgb"],"loading":{},"attributes":{},"edge_uris":[],"vertex_props":[{"name":"pagerank","dimension":0,"type":"double"}],"error_handling":{},"vertex_id_type":"integer","format":"pgb"}

// reload from disk 
opg-jshell> var graphFromDisk = session.readGraphFile("/tmp/snapshot.pgb")
graphFromDisk ==> PgxGraph[name=snapshot,N=3,E=3,created=1581623739395]

// previously computed properties are still part of the graph and can be queried
opg-jshell> graphFromDisk.queryPgql("select x.pagerank match (x)").print().close()

次の例は基本的に前の例と同じですが、パーティション化されたグラフを使用しています。パーティション化されたグラフの場合、複数のPGBファイル(グラフの頂点/エッジ・パーティションごとに1つ)が生成されます。

-jshell> analyst.pagerank(graph)
$3 ==> VertexProperty[name=pagerank,type=double,graph=anonymous_graph_1]// store graph including all props to disk
// Now save the state of this graph
opg-jshell> var storedPgbConfig = g.store(ProviderFormat.PGB, "/tmp/snapshot")
$4 ==> {"edge_props":[],"vertex_uris":["/tmp/snapshot.pgb"],"loading":{},"attributes":{},"edge_uris":[],"vertex_props":[{"name":"pagerank","dimension":0,"type":"double"}],"error_handling":{},"vertex_id_type":"integer","format":"pgb"}
// Reload from disk 
opg-jshell> var graphFromDisk = session.readGraphWithProperties(storedPgbConfig)
graphFromDisk ==> PgxGraph[name=snapshot,N=3,E=3,created=1581623739395]
// Previously computed properties are still part of the graph and can be queried
opg-jshell> graphFromDisk.queryPgql("select x.pagerank match (x)").print().close()

3.6 組込みアルゴリズムの実行

インメモリー・グラフ・サーバー(PGX)には、一連の組込みアルゴリズムが含まれており、Java APIとして使用できます。

次の表に、使用可能なアルゴリズムの概要をカテゴリ別に示します。

ノート:

これらのアルゴリズムは、Analystインタフェースを介して起動できます。詳細は、JavadocのAnalystクラスを参照してください。

表3-3 組込みアルゴリズムの概要

カテゴリ アルゴリズム
クラシック・グラフ・アルゴリズム プリム法
コミュニティ検出 コンダクタンス最小化(SomanおよびNarangアルゴリズム)、Infomap、ラベル伝播、Louvain
接続されたコンポーネント 強力に接続されたコンポーネント、弱く接続されたコンポーネント(WCC)
リンク予測 Whom To Follow (WTF)アルゴリズム
行列因数分解 行列因数分解
その他 グラフ・トラバース・アルゴリズム
パス検索 フィルタされたパス上のすべての頂点およびエッジ、ベルマン–フォード法、双方向ダイクストラ法、距離指標計算、高次頂点計算、ダイクストラ法、列挙単純パス、高速パス検索、最大フロー・パス、フィルタされた高速パス検索、ホップ距離アルゴリズム
ランキングとウォーキング 近接中心性アルゴリズム、次数中心性アルゴリズム、固有ベクトル中心性、Hyperlink-Induced Topic Search (HITS)、PageRankアルゴリズム、Random Walk with Restart、Stochastic Approach for Link-Structure Analysis (SALSA)アルゴリズム、頂点媒介中心性アルゴリズム
構造評価 Adamic-Adar指標、Bipartite Check、コンダクタンス、循環検出アルゴリズム、次数分布アルゴリズム、離心性アルゴリズム、K-コア、ローカル・クラスタリング係数(LCC)、モジュール性、パーティション・コンダクタンス、到達可能性アルゴリズム、Topological Orderingアルゴリズム、トライアングル・カウンティング・アルゴリズム

次のトピックでは、例としてトライアングル・カウンティングおよびPageRank分析を使用したインメモリー・グラフ・サーバー(PGX)の使用について説明します。

3.6.1 インメモリー・アナリストについて

インメモリー・アナリストには、一連の組込みアルゴリズムが含まれており、Java APIとして使用できます。APIの詳細は、製品のドキュメント・ライブラリに同梱されているJavadocに記載されています。特に、サポートされているインメモリー・アナリスト・メソッドのリストについては、BuiltinAlgorithmsインタフェースのメソッド・サマリーを参照してください。

たとえば、PageRankプロシージャの署名は次のとおりです。

/**
   * Classic pagerank algorithm. Time complexity: O(E * K) with E = number of edges, K is a given constant (max
   * iterations)
   *
   * @param graph
   *          graph
   * @param e
   *          maximum error for terminating the iteration
   * @param d
   *          damping factor
   * @param max
   *          maximum number of iterations
   * @return Vertex Property holding the result as a double
   */
  public <ID extends Comparable<ID>> VertexProperty<ID, Double> pagerank(PgxGraph graph, double e, double d, int max);

3.6.2 トライアングル・カウンティング・アルゴリズムの実行

トライアングル・カウンティングの場合、countTriangles()sortByDegreeブール・パラメータを使用して、グラフを最初に角度でソートする(true)かソートしない(false)かを制御できます。trueの場合、さらに多くのメモリーが使用されますが、アルゴリズムの実行は高速になります。ただし、グラフが非常に大きい場合、この最適化をオフにしてメモリー不足を回避することができます。

シェルを使用したトライアングル・カウンティングの実行

opg> analyst.countTriangles(graph, true)
==> 1

Javaを使用したトライアングル・カウンティングの実行

import oracle.pgx.api.*;
 
Analyst analyst = session.createAnalyst();
long triangles = analyst.countTriangles(graph, true);

このアルゴリズムでは、サンプル・グラフ内の1つのトライアングルを検出します。

ヒント:

インメモリー・アナリスト・シェルを使用する場合、ロギング・レベルを変更すると、実行中のログ出力の量を増加できます。:h :loglevelを指定した:loglevelコマンドの実行に関する情報を参照してください。

3.6.3 PageRankアルゴリズムの実行

PageRankは、グラフ内のそれぞれの頂点(ノード)について0から1の間のランク値を計算し、その値をdoubleプロパティに格納します。このため、アルゴリズムによって、出力に対してタイプdouble頂点プロパティが作成されます。

インメモリー・アナリストでは、頂点プロパティとエッジ・プロパティの2つのタイプがあります。

  • 永続プロパティ: データ・ソースからグラフとともにロードされ固定されたディスク上のデータのインメモリー・コピーであるため、永続となるプロパティ。永続プロパティは読取り専用のため変更できず、セッション間で共有されます。

  • 一時プロパティ: 値が書き込めるのは一時プロパティのみで、これらはセッションに対してプライベートです。一時プロパティを作成するには、PgxGraphオブジェクトでcreateVertexPropertyおよびcreateEdgePropertyをコールするか、プロパティ・オブジェクトでclone()を使用して既存のプロパティをコピーします。

    一時プロパティには、アルゴリズムによる計算結果が保持されます。たとえば、PageRankアルゴリズムでは、グラフ内の頂点ごとに0から1の間のランク値を計算し、これらの値をpg_rankという一時プロパティに格納します。一時プロパティは、アナリスト・オブジェクトが破棄されると破棄されます。

この例では、PageRank値が最も高い上位3つの頂点を取得します。タイプdoubleの一時頂点プロパティを使用して、計算したPageRank値を保持します。PageRankアルゴリズムでは入力パラメータに次のデフォルト値を使用します。エラー許容値 = 0.001、減衰係数 = 0.85および最大反復数 = 100です。

シェルを使用したPageRankの実行

opg> rank = analyst.pagerank(graph, 0.001, 0.85, 100);
==> ...
opg> rank.getTopKValues(3)
==> 128=0.1402019732468347
==> 333=0.12002296283541904
==> 99=0.09708583862990475

Javaを使用したPageRankの実行

import java.util.Map.Entry;
import oracle.pgx.api.*;
 
Analyst analyst = session.createAnalyst();
VertexProperty<Integer, Double> rank = analyst.pagerank(graph, 0.001, 0.85, 100);
for (Entry<Integer, Double> entry : rank.getTopKValues(3)) {
 System.out.println(entry.getKey() + "=" + entry.getValue());
}

3.7 カスタムPGXグラフ・アルゴリズムの使用

カスタムPGXグラフ・アルゴリズムを使用すると、Javaでグラフ・アルゴリズムを記述し、効率的なパラレル実装に自動的にコンパイルできます。

次のサブトピックに示す情報より詳細な情報は、PGXアルゴリズムの仕様(https://docs.oracle.com/cd/E56133_01/latest/PGX_Algorithm_Language_Specification.pdf)を参照してください。

3.7.1 カスタムPGXアルゴリズムの作成

PGXアルゴリズムは、@graphAlgorithmを使用して注釈が付けられる単一のクラス定義を含む通常の.javaファイルです。次に例を示します。

import oracle.pgx.algorithm.annotations.GraphAlgorithm;

@GraphAlgorithm
public class MyAlgorithm {
    ...
}

PGXアルゴリズム・クラスには、エントリ・ポイントとして使用されるパブリック・メソッドが1つだけ含まれている必要があります。次に例を示します。

import oracle.pgx.algorithm.PgxGraph;
import oracle.pgx.algorithm.VertexProperty;
import oracle.pgx.algorithm.annotations.GraphAlgorithm;
import oracle.pgx.algorithm.annotations.Out;

@GraphAlgorithm
public class MyAlgorithm {
    public int myAlgorithm(PgxGraph g, @Out VertexProperty<Integer> distance) {
        System.out.println("My first PGX Algorithm program!");

        return 42;
    }
}

通常のJavaメソッドと同様に、PGXアルゴリズム・メソッドは値(この例では整数)を戻すことができます。さらに興味深いのは、頂点プロパティdistanceを出力パラメータとしてマークする@Out注釈です。コール元は、出力パラメータを参照渡しします。このようにして、呼出し側は、アルゴリズムの終了後に変更されたプロパティへの参照を持ちます。

3.7.1.1 コレクション

コレクションを作成するには、.create()関数をコールします。たとえば、VertexProperty<Integer>は次のように作成されます。

VertexProperty<Integer> distance = VertexProperty.create();

特定の頂点vでプロパティの値を取得するには:

distance.get(v);

同様に、特定の頂点vのプロパティを値eに設定するには、次のようにします。

distance.set(v, e);

コレクションのプロパティを作成することもできます。

VertexProperty<VertexSequence> path = VertexProperty.create();

ただし、PGXアルゴリズムの割当ては常に(参照ではなく)で行われます。これを明示的にするには、コレクションの割当て時に.clone()をコールする必要があります

VertexSequence sequence = path.get(v).clone();

値で渡される値のもう1つの結果として、Javaメソッド.equals()ではなく、==演算子を使用して等価性をチェックできるようになりました。次に例を示します。

PgxVertex v1 = G.getRandomVertex();
PgxVertex v2 = G.getRandomVertex();
System.out.println(v1 == v2);
3.7.1.2 反復

PGXアルゴリズムの最も一般的な操作は、反復(すべての頂点のループ、頂点の近隣のループなど)およびグラフ・トラバーサル(幅優先/深さ優先など)です。すべてのコレクションが、forEachメソッドとforSequentialメソッドを公開します。これらのメソッドにより、コレクションはそれぞれ並列、および順に反復処理できます。

次に例を示します。

  • グラフの頂点を並列して反復処理するには:
    G.getVertices().forEach(v -> {
        ...
    });
    
  • グラフの頂点を順に反復処理するには:
    G.getVertices().forSequential(v -> {
        ...
    });
    
  • グラフの頂点をrから幅優先順にトラバースするには:
    import oracle.pgx.algorithm.Traversal;
    
    Traversal.inBFS(G, r).forward(n -> {
        ...
    });
    

    forward (またはbackward)ラムダ内部では、currentLevel()をコールすることによってBFS (またはDFS)トラバーサルの現在のレベルにアクセスできます。

3.7.1.3 削減

これらの並列ブロック内では一般に、ラムダ外部に定義された変数をアトミックに更新するか、この変数まで削減します。これらのアトミック型削減は、Scalar<T>: reduceAdd、reduceMul、reduceAndなどでメソッドとして使用できます。たとえば、グラフ内の頂点の数をカウントするには、次のようにします。

public int countVertices() {
    Scalar<Integer> count = Scalar.create(0);

    G.getVertices().forEach(n -> {
        count.reduceAdd(1);
    });

    return count.get();
}

複数の値を原子的に更新する必要がある場合があります。たとえば、最小のプロパティ値と、この最小値にプロパティ値が到達した頂点を検出できます。並列実行による2つの別々の削減文のために、状態の一貫性が失われる場合があります。

この問題を解決するために、ReductionsクラスにはargMin関数とargMax関数が用意されています。argMinの最初の引数は現在の値で、2番目の引数は潜在的な新しい最小値です。また、ArgMinMaxオブジェクトに対してandUpdateコールを連鎖的に実行することにより、他の変数およびこれらの(アトミックな)更新先の値を示すことができます。次に例を示します。

VertexProperty<Integer> rank = VertexProperty.create();
int minRank = Integer.MAX_VALUE;
PgxVertex minVertex = PgxVertex.NONE;

G.getVertices().forEach(n ->
    argMin(minRank, rank.get(n)).andUpdate(minVertex, n)
);

3.7.2 PGXアルゴリズムのコンパイルおよび実行

カスタムPGXアルゴリズムをコンパイルおよび実行できるようにするには、次のいくつかの処理を実行する必要があります。

  1. conf/pgx.confファイルに2つの構成パラメータを設定します。
    • graph_algorithm_languageオプションをJAVAに設定します。
    • java_home_dirオプションを、Javaホームへのパスに設定します(<system-java-home-dir>を使用して、PGXにシステム・プロパティからJavaホームを推測させます)。
  2. セッションを(シェルで暗黙的に、またはJavaで明示的に)作成します。次に例を示します。
    cd $PGX_HOME
    ./bin/opg
    
  3. PGXアルゴリズムをコンパイルします。次に例を示します。
    myAlgorithm = session.compileProgram("/path/to/MyAlgorithm.java")
  4. アルゴリズムを実行します。次に例を示します。
    graph = session.readGraphWithProperties("/path/to/config.edge.json")
    property = graph.createVertexProperty(PropertyType.INTEGER)
    myAlgorithm.run(graph, property)
    

3.7.3 カスタムPGXアルゴリズムの例: PageRank

PGXアルゴリズムとしてのpagerankの実装を次に示します。

import oracle.pgx.algorithm.PgxGraph;
import oracle.pgx.algorithm.Scalar;
import oracle.pgx.algorithm.VertexProperty;
import oracle.pgx.algorithm.annotations.GraphAlgorithm;
import oracle.pgx.algorithm.annotations.Out;

@GraphAlgorithm
public class Pagerank {
  public void pagerank(PgxGraph G, double tol, double damp, int max_iter, boolean norm, @Out VertexProperty<Double> rank) {
    Scalar<Double> diff = Scalar.create();
    int cnt = 0;
    double N = G.getNumVertices();

    rank.setAll(1 / N);
    do {
      diff.set(0.0);
      Scalar<Double> dangling_factor = Scalar.create(0d);

      if (norm) {
        dangling_factor.set(damp / N * G.getVertices().filter(v -> v.getOutDegree() == 0).sum(rank::get));
      }

      G.getVertices().forEach(t -> {
        double in_sum = t.getInNeighbors().sum(w -> rank.get(w) / w.getOutDegree());
        double val = (1 - damp) / N + damp * in_sum + dangling_factor.get();
        diff.reduceAdd(Math.abs(val - rank.get(t)));
        rank.setDeferred(t, val);
      });
      cnt++;
    } while (diff.get() > tol && cnt < max_iter);
  }
}

3.8 サブグラフの作成

サブグラフはロードしたグラフに基づいて作成できます。フィルタ式を使用するか、2部グラフの左側のセットを指定する頂点(ノード)コレクションに基づく2部サブグラフを作成できます。

グラフのメモリーへの読込みの詳細は、キーストアへのデータベース・パスワードの格納を参照してください。

3.8.1 フィルタ式について

フィルタ式は、各エッジで評価される式です。式によって、結果(この場合はサブグラフ)に含まれるようにエッジを完成させる述部を定義できます。

4つの頂点(ノード)と4つのエッジで構成されるグラフの例を考えてみます。フィルタ式src.prop == 10と一致するエッジについては、元の頂点のpropプロパティが10になります。次の図に示すように、2つのエッジがフィルタ式と一致します。

図3-1 src.prop == 10と一致するエッジ

図3-1の説明が続きます
「図3-1 src.prop == 10と一致するエッジ」の説明

次の図は、フィルタを適用した結果のグラフを示します。フィルタは頂点333に対応するエッジ、およびその頂点自体を除外します。

図3-2 簡易フィルタで作成されたグラフ

図3-2の説明が続きます
「図3-2 簡易フィルタで作成されたグラフ」の説明

フィルタ式を使用すると、単一の頂点または頂点集合の選択が難しくなります。たとえば、プロパティ値10の頂点のみは選択できません。これは、頂点を照合するには、10が出力と入力のプロパティ値のどちらであるかエッジを照合する必要があるためです。ただし、エッジを一致させるときは、出力頂点、入力頂点、およびその結果のエッジ自体が自動的に含まれます。

3.8.2 簡易フィルタを使用したサブグラフの作成

次の例は、「フィルタ式について」で説明したサブグラフの作成を示します。

シェルを使用したサブグラフの作成

subgraph = graph.filter(new VertexFilter("vertex.prop == 10"))

Javaを使用したサブグラフの作成

import oracle.pgx.api.*;
import oracle.pgx.api.filter.*;

PgxGraph graph = session.readGraphWithProperties(...);
PgxGraph subgraph = graph.filter(new VertexFilter("vertex.prop == 10"));

3.8.3 複合フィルタを使用したサブグラフの作成

この例では、少し複雑なフィルタを使用しています。ここでは、識別子の出力エッジの数(出力srcまたは入力dst)を計算するoutDegree関数を使用します。次のフィルタ式は、costプロパティ値が50を超え、outDegreeが1を超える入力頂点(ノード)のエッジと一致します。

dst.outDegree() > 1 && edge.cost > 50

次の図に示すように、サンプル・グラフの1つのエッジがこのフィルタ式と一致します。

図3-3 outDegreeフィルタと一致するエッジ

図3-3の説明が続きます
「図3-3 outDegreeフィルタと一致するエッジ」の説明

次の図は、フィルタを適用した結果のグラフを示します。フィルタは頂点99と1908に対応するエッジを除外するため、その頂点も除外します。

図3-4 outDegreeフィルタで作成されたグラフ

図3-4の説明が続きます
「図3-4 outDegreeフィルタで作成されたグラフ」の説明

3.8.4 頂点集合を使用した2部サブグラフの作成

2部サブグラフは、左側に使用される頂点(ノード)集合を指定して作成できます。2部サブグラフには、左側の頂点集合と右側の頂点集合の間にのみエッジがあります。左側の2つのノード間など、これらの集合内にエッジはありません。インメモリー・アナリストでは、入力および出力エッジがすべて削除されたために分離された頂点は、2部サブグラフの一部ではありません。

次の図に、2部サブグラフを示します。プロパティは示していません。

次の例では、フィルタ式についてに示されている単純なグラフから2部サブグラフを作成します。左側の頂点集合を作成し、それに頂点を入力します。

シェルを使用した2部サブグラフの作成

opg> s = graph.createVertexSet()
==> ...
opg> s.addAll([graph.getVertex(333), graph.getVertex(99)])
==> ...
opg> s.size()
==> 2
opg> bGraph = graph.bipartiteSubGraphFromLeftSet(s)
==> PGX Bipartite Graph named sample-sub-graph-4

Javaを使用した2部サブグラフの作成

import oracle.pgx.api.*;
 
VertexSet<Integer> s = graph.createVertexSet();
s.addAll(graph.getVertex(333), graph.getVertex(99));
BipartiteGraph bGraph = graph.bipartiteSubGraphFromLeftSet(s);

サブグラフを作成すると、インメモリー分析で、頂点が左側にあるかどうかを示すブール頂点(ノード)プロパティが自動的に作成されます。このプロパティには一意の名前を指定できます。

結果の2部サブグラフは次のようになります。

頂点1908は2部サブグラフから除外されます。頂点に繋がっている唯一のエッジは、128から1908に伸びています。エッジはサブグラフの2部プロパティに反しているため削除されています。頂点1908にはその他のエッジがないため、これも削除されています。

3.9 データベースの変更を処理するための自動デルタ・リフレッシュの使用

定期的にグラフを自動的にリフレッシュして(自動リフレッシュ)、Oracle Database内のプロパティ・グラフ表(VT$およびGE$表)に格納されているプロパティ・グラフへの変更とインメモリー・グラフとの同期化を維持できます。

リレーショナル表からメモリー内のPGXにグラフを直接ロードする場合、自動リフレッシュ機能はサポートされないことに注意してください。

3.9.1 自動リフレッシュ用のインメモリー・サーバーの構成

自動リフレッシュは多くのスナップショットを作成できるので、メモリー使用率が高くなる可能性があります。デフォルトで、グラフの自動リフレッシュを有効にするオプションは管理者だけが使用できます。

すべてのユーザーにグラフの自動リフレッシュを許可するには、次の行をインメモリー・アナリストの構成ファイル($ORACLE_HOME/md/property_graph/pgx/conf/pgx.confに配置)に含める必要があります。

{
  "allow_user_auto_refresh": true
}

3.9.2 基本的な自動リフレッシュの構成

自動リフレッシュはグラフ構成のロード・セクションで構成されています。このトピックの例では、自動リフレッシュを設定して、毎分、更新があるかどうかをチェックし、ソースが変更されていたら新しいスナップショットを作成します。

次のブロック(JSON形式)により、サンプル・グラフの構成ファイルの自動リフレッシュ機能を有効にします。

{
  "format": "pg",
  "jdbc_url": "jdbc:oracle:thin:@mydatabaseserver:1521/dbName",
  "username": "scott",
  "password": "<password>",
  "name": "my_graph",
  "vertex_props": [{
    "name": "prop",
    "type": "integer"
  }],
  "edge_props": [{
    "name": "cost",
    "type": "double"
  }],
  "separator": " ",
  "loading": {
    "auto_refresh": true,
    "update_interval_sec": 60
  },
}

自動リフレッシュ設定を含んでいる追加のロード・セクションに注意してください。Java APIを使用して、同じグラフ構成をプログラムで作成することもできます。

GraphConfig config = GraphConfigBuilder.forPropertyGraphRdbms()
  .setJdbcUrl("jdbc:oracle:thin:@mydatabaseserver:1521/dbName")
  .setUsername("scott")
  .setPassword("<password>")
  .setName("my_graph")
  .addVertexProperty("prop", PropertyType.INTEGER)
  .addEdgeProperty("cost", PropertyType.DOUBLE)
  .setAutoRefresh(true)
  .setUpdateIntervalSec(60)
  .build();

3.9.3 インメモリー・アナリストまたはJavaアプリケーションを使用したグラフの読取り

グラフ構成を作成した後、通常のAPIを使用してグラフをインメモリー・アナリストにロードできます。

opg> G = session.readGraphWithProperties("graphs/my-config.pg.json")

グラフをロードすると、バックグラウンド・タスクが自動的に開始され、データ・ソースに更新があるかどうかを定期的にチェックします。

3.9.4 グラフの特定のスナップショットのチェックアウト

データベースには更新があるかどうかの問合せが毎分実行されます。時間間隔の経過後にデータベースのグラフが変更されている場合、グラフはリロードされ、新しいスナップショットがインメモリーで自動的に作成されます。

PgxSessiongetAvailableSnapshots()メソッドを使用して、グラフの使用可能なインメモリー・スナップショットを"チェックアウト"(ポインタを別のバージョンに移動)できます。出力の例は次のとおりです。

opg> session.getAvailableSnapshots(G)
==> GraphMetaData [getNumVertices()=4, getNumEdges()=4, memoryMb=0, dataSourceVersion=1453315103000, creationRequestTimestamp=1453315122669 (2016-01-20 10:38:42.669), creationTimestamp=1453315122685 (2016-01-20 10:38:42.685), vertexIdType=integer, edgeIdType=long]
==> GraphMetaData [getNumVertices()=5, getNumEdges()=5, memoryMb=3, dataSourceVersion=1452083654000, creationRequestTimestamp=1453314938744 (2016-01-20 10:35:38.744), creationTimestamp=1453314938833 (2016-01-20 10:35:38.833), vertexIdType=integer, edgeIdType=long]

前の出力例には、2つのエントリが含まれ、1つは最初にロードされた4つの頂点と4つのエッジのあるグラフで、もう1つは自動リフレッシュで作成された5つの頂点と5つのエッジのあるグラフです。

グラフの特定のスナップショットをチェックアウトするには、PgxSessionのsetSnapshot()メソッドを使用し、ロードするスナップショットのcreationTimestampを指定します。

たとえば、Gが5つの頂点と5つのエッジのある新しいグラフを指しているが、古いバージョンのグラフを分析したい場合、スナップショットを1453315122685に設定する必要があります。インメモリー・アナリスト・シェルの場合:

opg> G.getNumVertices()
==> 5
opg> G.getNumEdges()
==> 5

opg> session.setSnapshot( G, 1453315122685 )
==> null

opg> G.getNumVertices()
==> 4
opg> G.getNumEdges()
==> 4

PgxSessionreadGraphAsOf()メソッドを使用して、グラフの特定のスナップショットを直接ロードすることもできます。これはreadGraphWithProperty()とその後にsetSnapshot()を使用してグラフをロードするショートカットです。次に例を示します。

opg> G = session.readGraphAsOf( config, 1453315122685 )

どのスナップショットが現在インメモリーで使用可能なのかがわからない、または気にしていない場合は、最大許容期間を指定することで、どれくらい“古い”スナップショットを許容できるかの時間範囲を指定することもできます。たとえば、スナップショットの最大存続期間を60分に設定するには、次を使用できます。

opg> G = session.readGraphWithProperties( config, 60l, TimeUnit.MINUTES )

メモリー内に指定した最大期間よりも若い(新しい)スナップショットが1つ以上ある場合、それらのスナップショットのうち最も若い(新しい)ものが返されます。すべての使用可能なスナップショットが指定した最大期間よりも古い場合、または使用可能なスナップショットがまったくない場合は、新しいスナップショットが自動的に作成されます。

3.9.5 高度な自動リフレッシュ構成

自動リフレッシュ構成の拡張オプションを指定できます。

内部で、インメモリー・アナリストは最後のチェック以降の変更をデータベースからフェッチし、差分(変更)を前のスナップショットに適用することで、新しいスナップショットを作成します。2つのタイマーがあります。1つはデータベースから差分をフェッチしてキャッシュするためのもので、もう1つは実際に差分を適用して新しいスナップショットを作成するためのものです。

さらに、キャッシュされる差分の数のしきい値を指定できます。キャッシュされた変更の数がこのしきい値よりも多くなると、新しいスナップショットが自動的に作成されます。キャッシュされた変更の数は、単に頂点の変更の数の合計にエッジの変更の数を足したものです。

2つの理由で、差分は定期的にフェッチされ、インメモリー・アナリスト・サーバーにキャッシュされます。

  • 実際のスナップショットの作成プロセスを高速化するため

  • しばらくするとデータベースが変更を"忘れる"可能性がある場合を把握するため

しきい値と更新タイマーの両方を指定できます。つまり、新しいスナップショットが作成される前に、両方の条件がチェックされます。これらのパラメータのうち少なくとも1つは指定して、差分キャッシュが大きくなりすぎるのを防ぐ必要があります。ソースに変更を問合せる間隔は省略できません。

次のパラメータは、5分ごとにデータ・ソースに新しい差分を問合せる構成を示しています。新しいスナップショットは20分ごとに作成されるか、またはキャッシュされた差分が1000の変更のサイズに到達すると作成されます。

{
  "format": "pg",
  "jdbc_url": "jdbc:oracle:thin:@mydatabaseserver:1521/dbName",
  "username": "scott",
  "password": "<your_password>",
  "name": "my_graph",

  "loading": {
    "auto_refresh": true,
    "fetch_interval_sec": 300,
    "update_interval_sec": 1200,
    "update_threshold": 1000,
    "create_edge_id_index": true,
    "create_edge_id_mapping": true
  }
}

3.10 インメモリー・アナリスト・サーバーの起動

Apache Tomcatの事前構成済バージョンがバンドルされているため、スクリプトを実行してインメモリー・アナリスト・サーバーを起動できます。

サーバーを起動する前に構成する必要がある場合は、インメモリー・アナリスト・サーバーの構成を参照してください。

PGXはsystemdと統合され、バックグラウンドでLinuxサービスとして実行されます。

PGXサーバーをデーモン・プロセスとして起動するには、rootユーザーとして、またはsudoを指定して次のコマンドを実行します。

sudo systemctl start pgx

サーバーを停止するには、rootユーザーとして、またはsudoを指定して次のコマンドを実行します。

sudo systemctl stop pgx

サーバーが起動しない場合は、次を実行してエラーがないかどうかを確認できます。

journalctl -u pgx.service

Oracle Linuxでsystemdを使用して対話する方法の詳細は、Oracle Linux管理者のドキュメントを参照してください。

3.10.1 インメモリー・アナリスト・サーバーの構成

/etc/oracle/graph/server.confファイルを変更することで、インメモリー・アナリスト・サーバーを構成できます。次の表に、JSON形式で指定できる有効な構成オプションを示します。

表3-4 インメモリー・アナリスト・サーバーの構成オプション

オプション 説明 デフォルト

authorization

string

クライアントを認可のためにロールにマップするファイル。

server.auth.conf

ca_certs

文字列の配列

信頼できる証明書(PEM形式)のリスト。「enable_tls」がfalseに設定されている場合、このオプションは無効です。

[この表の後の情報を参照してください。]

enable_client_authentication

boolean

trueの場合、クライアントはTLSハンドシェイク中に認証されます。詳細は、TLSプロトコルを参照してください。「enable_tls」がfalseの場合、このフラグは無効です。

true

enable_tls

boolean

trueの場合、サーバーはトランスポート層セキュリティ(TLS)を有効にします。

true

port

integer

PGXサーバーがリスニングする必要があるポート

7007

server_cert

string

TLSクライアントに提示されるサーバー証明書(PEM形式)へのパス。「enable_tls」がfalseに設定されている場合、このオプションは無効です

null

server_private_key

string

サーバーの秘密キー(PKCS#8、PEM形式)。「enable_tls」がfalseに設定されている場合、このオプションは無効です

null

インメモリー・アナリストWebサーバーでは、デフォルトで双方向SSL/TLS (トランスポート層セキュリティ)が有効になります。サーバーはTLS 1.2を適用し、攻撃に対して脆弱であることがわかっている特定の暗号スイートを無効にします。TLSハンドシェイク時に、サーバーとクライアントの両方が証明書を相互に提示し、それを使用してお互いの正当性を検証します。クライアント証明書は、クライアント・アプリケーションの認可にも使用されます。

server.conf構成ファイルの例を次に示します。

{ 
  "port": 7007, 
  "server_cert": "certificates/server_certificate.pem", 
  "server_private_key": "certificates/server_key.pem", 
  "ca_certs": [ "certificates/ca_certificate.pem" ], 
  "authorization": "auth/server.auth.conf",
  "enable_tls": true,
  "enable_client_authentication": true
}

次に、server.auth.conf構成ファイルの例を示します。証明書DN文字列で識別されるクライアント(アプリケーション)をロールにマップします。

{ 
  "authorization": [{
    "dn": "CN=Client, OU=Development, O=Oracle, L=Belmont, ST=California, C=US", 
   "admin": false
  }, {
    "dn": "CN=Admin, OU=Development, O=Oracle, L=Belmont, ST=California, C=US", 
   "admin": true
  }]
}

サーバー構成でクライアント側の認証またはSSL/TLS認証を完全に無効にできます。ただし、本番で使用する場合は、双方向SSL/TLSを有効にすることをお薦めします。

3.11 Apache Tomcatへのデプロイ

このトピックの例では、Apache Tomcatでグラフ・サーバーをWebアプリケーションとしてデプロイする方法について説明します。

グラフ・サーバーは、Apache Tomcat 9.0.x以降で動作します。

  1. Oracle Software Delivery CloudからOracle Graph Webapps zipファイルをダウンロードします。このファイルには、すぐにデプロイできるJava Webアプリケーション・アーカイブ(.warファイル)が含まれます。ファイル名は、oracle-graph-webapps-<version>.zipのようになります
  2. 選択したディレクトリに、このファイルを解凍します。
  3. Tomcatの.warファイルを見つけます。ネーミング・パターン: graph-server-<version>-pgx<version>-tomcat.warに従っています。
  4. グラフ・サーバーを構成します。
    1. Webアプリケーション・アーカイブ内のWEB-INF/classes/pgx.confファイルを変更して、認証およびその他のサーバー設定を変更します。
    2. 必要に応じて、Webアプリケーション・アーカイブ内のWEB-INF/classes/log4j2.xmlファイルを変更して、ロギング設定を変更します。
    3. 必要に応じて、Webアプリケーション・アーカイブ内のWEB-INF/web.xmlファイルを変更して、その他のサーブレット固有のデプロイメント・ディスクリプタを変更します。
  5. .warファイルをTomcatのwebappsディレクトリにコピーします。次に例を示します。
    cp graph-server-<version>-pgx<version>-tomcat.war $CATALINA_HOME/webapps/pgx.war
  6. TLS/暗号化の正しい使用など、Tomcat固有の設定を構成します
  7. ポート8080がすでに使用されていないことを確認します。
  8. Tomcatを起動します。
    cd $CATALINA_HOME 
    ./bin/startup.sh 

    これで、グラフ・サーバーは、localhost:8080/pgxでリスニングします。

    次のコマンドを実行すると、JShellからサーバーに接続できます。
    $ <client_install_dir>/bin/opg-jshell --base_url https://localhost:8080/pgx -u <graphuser>

3.11.1 認証メカニズムについて

インメモリー・アナリストのWebデプロイメントでは、デフォルトでBASIC Authを使用します。本番のデプロイメントでは、より安全な認証メカニズムに変更する必要があります。

証メカニズムを変更するには、Webアプリケーション・アーカイブ(WAR)ファイルでweb.xmlデプロイメント記述子のsecurity-constraint要素を変更します。

3.12 Oracle WebLogic Serverへのデプロイ

このトピックの例では、Oracle WebLogic Serverでグラフ・サーバーをWebアプリケーションとしてデプロイする方法について説明します。

この例では、Oracle WebLogic Serverを使用してグラフ・サーバーをデプロイする方法を示します。グラフ・サーバーでは、WebLogic Serverバージョン12.1.xおよび12.2.x.をサポートしています。

  1. Oracle Software Delivery CloudからOracle Graph Webapps zipファイルをダウンロードします。このファイルには、すぐにデプロイできるJava Webアプリケーション・アーカイブ(.warファイル)が含まれます。ファイル名は、oracle-graph-webapps-<version>.zipのようになります
  2. 選択したディレクトリに、このファイルを解凍します
  3. Weblogicサーバーの.warファイルを見つけます。
    1. Weblogic Serverバージョン12.1.xの場合、Webアプリケーション・アーカイブgraph-server-<version>-pgx<version>-wls121x.warを使用します
    2. Weblogic Serverバージョン12.2.xの場合、Webアプリケーション・アーカイブgraph-server-<version>-pgx<version>-wls122x.warを使用します
  4. グラフ・サーバーを構成します。
    1. Webアプリケーション・アーカイブ内のWEB-INF/classes/pgx.confファイルを変更して、認証およびその他のサーバー設定を変更します。
    2. 必要に応じて、Webアプリケーション・アーカイブ内のWEB-INF/classes/log4j2.xmlファイルを変更して、ロギング設定を変更します。
    3. 必要に応じて、Webアプリケーション・アーカイブ内のWEB-INF/web.xmlファイルを変更して、その他のサーブレット固有のデプロイメント・ディスクリプタを変更します。
    4. 必要に応じて、Webアプリケーション・アーカイブ内のWEB-INF/weblogic.xmlファイルを変更して、その他のWebLogic Server固有のデプロイメント・ディスクリプタを変更します。
  5. TLS/暗号化の正しい使用など、WebLogic固有の設定を構成します
  6. WebLogic Serverに.warファイルをデプロイします。次の例では、コマンドラインからこれを実行する方法を示します。
    . $MW_HOME/user_projects/domains/mydomain/bin/setDomainEnv.sh
    . $MW_HOME/wlserver/server/bin/setWLSEnv.sh
    java weblogic.Deployer -adminurl http://localhost:7001 -username <username> -password <password> -deploy -source <path-to-war-file>
    

3.12.1 Oracle WebLogic Serverのインストール

Oracle WebLogic Serverの最新バージョンをダウンロードし、インストールするには、次を参照してください

http://www.oracle.com/technetwork/middleware/weblogic/documentation/index.html

3.13 インメモリー・アナリスト・サーバーへの接続

プロパティ・グラフのインメモリー・アナリストが、Oracle Databaseが稼働しているコンピュータにインストールされた後、あるいはApache TomcatまたはOracle WebLogic Server上のWebアプリケーションとしてOracle Databaseサーバー・ソフトウェアがインストールされていないクライアント・システムにインストールされた後に、インメモリー・アナリスト・サーバーに接続できます。

3.13.1 インメモリー・アナリスト・シェルによる接続

インメモリー・アナリスト・インスタンスへの最も単純な接続方法は、サーバーのベースURLを指定することです。次のベースURLでは、SCOTTユーザーをポート8080でリスニングするローカル・インスタンスに接続できます。

http://scott:<password>@localhost:8080/pgx

このベースURLでインメモリー・アナリスト・シェルを開始するには、--base_urlコマンドライン引数を使用します

cd $PGX_HOME
./bin/opg-jshell --base_url http://scott:<password>@localhost:8080/pgx

同じ方法でリモート・インスタンスに接続できます。ただし、インメモリー・アナリストでは現在、Control APIのリモート・サポートは提供されていません。

3.13.1.1 HTTPリクエストのロギングについて

インメモリー分析シェルでは、デフォルトでデバッグ・メッセージをすべて非表示にします。どのHTTPリクエストが実行されたかを確認するには、この例に示すように、oracle.pgxのログ・レベルをDEBUGに設定します。

opg> /loglevel oracle.pgx DEBUG
===> log level of oracle.pgx logger set to DEBUG
opg> session.readGraphWithProperties("sample_http.adj.json", "sample")
10:24:25,056 [main] DEBUG RemoteUtils - Requesting POST http://scott:<password>@localhost:8080/pgx/core/session/session-shell-6nqg5dd/graph HTTP/1.1 with payload {"graphName":"sample","graphConfig":{"uri":"http://path.to.some.server/pgx/sample.adj","separator":" ","edge_props":[{"type":"double","name":"cost"}],"node_props":[{"type":"integer","name":"prop"}],"format":"adj_list"}}
10:24:25,088 [main] DEBUG RemoteUtils - received HTTP status 201
10:24:25,089 [main] DEBUG RemoteUtils - {"futureId":"87d54bed-bdf9-4601-98b7-ef632ce31463"}
10:24:25,091 [pool-1-thread-3] DEBUG PgxRemoteFuture$1 - Requesting GET http://scott:<password>@localhost:8080/pgx/future/session/session-shell-6nqg5dd/result/87d54bed-bdf9-4601-98b7-ef632ce31463 HTTP/1.1
10:24:25,300 [pool-1-thread-3] DEBUG RemoteUtils - received HTTP status 200
10:24:25,301 [pool-1-thread-3] DEBUG RemoteUtils - {"stats":{"loadingTimeMillis":0,"estimatedMemoryMegabytes":0,"numEdges":4,"numNodes":4},"graphName":"sample","nodeProperties":{"prop":"integer"},"edgeProperties":{"cost":"double"}}

この例のグラフURIは、インメモリー・アナリスト・サーバーがHTTPまたはHDFSを使用してアクセスできるファイルを指し示している必要があります。

3.13.2 Javaによる接続

Javaを使用してインメモリー・アナリストを初期化する場合、ベースURLを指定できます。この例は次のようになります。インメモリー・アナリスト・サーバーへのURLがgetInMemAnalyst APIコールに表示されます。

import oracle.pg.rdbms.*;
import oracle.pgx.api.*;
 
PgRdbmsGraphConfigcfg = GraphConfigBuilder.forPropertyGraphRdbms().setJdbcUrl("jdbc:oracle:thin:@127.0.0.1:1521:orcl") 
   .setUsername("scott").setPassword("<password>")  .setName("mygraph") 
   .setMaxNumConnections(2) .setLoadEdgeLabel(false) 
   .addVertexProperty("name", PropertyType.STRING, "default_name")  
   .addEdgeProperty("weight", PropertyType.DOUBLE, "1000000")  
   .build();OraclePropertyGraph opg = OraclePropertyGraph.getInstance(cfg);
ServerInstance remoteInstance = Pgx.getInstance("http://scott:<password>@hostname:port/pgx");
PgxSession session = remoteInstance.createSession("my-session");
 
PgxGraph graph = session.readGraphWithProperties(opg.getConfig());

3.13.3 PGX REST APIによる接続

REST API PGXエンドポイントを使用して、インメモリー・アナリスト・インスタンスに接続できます。これにより、Java以外の言語でインメモリー・アナリストと対話して独自のクライアントを実装できます。

このトピックの例は、次を前提としています。

  • curlを含むLinuxがインストールされています。curlは、RESTエンドポイントと対話するための単純なコマンドライン・ユーティリティです。)
  • PGXサーバーは、http://localhost:7007で稼働中です。
  • PGXサーバーは認証/認可が無効にされています。つまり、$ORACLE_HOME/md/property_graph/pgx/conf/server.conf"enable_tls": falseが含まれています。(これはデフォルト以外の設定であり、本番では推奨されません)。
  • PGXでは、ローカル・ファイル・システムからグラフを読み取ることができます。つまり、$ORACLE_HOME/md/property_graph/pgx/conf/pgx.conf"allow_local_filesystem": trueが含まれています。(これはデフォルト以外の設定であり、本番では推奨されません)。

Swagger仕様では、ブラウザでhttp://localhost:7007/swagger.jsonを開くことで、JSONでサポートされているエンドポイントの完全なリストを表示できます。

ステップ1: CSRFトークンの取得

CSRFトークンをリクエストします。

curl -v http://localhost:7007/token

レスポンスは次のようになります。

*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 7007 (#0)
> GET /token HTTP/1.1
> Host: localhost:7007
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 201
< SET-COOKIE: _csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0;Version=1; HttpOnly
< Content-Length: 0

レスポンスに表示されるように、Cookie _csrf_tokenにトークン値が設定されます。9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0は、次のリクエストのサンプル・トークンとして使用されます。どの書込みリクエストでも、PGXサーバーでは、Cookieとペイロードの両方に同じトークンが存在している必要があります。

ステップ2: セッションの作成

新しいセッションを作成するには、JSONペイロードを送信します。

curl -v --cookie '_csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0' -H 'content-type: application/json' -X POST http://localhost:7007/core/v1/sessions -d '{"source":"my-application", "idleTimeout":0, "taskTimeout":0, "timeUnitName":"MILLISECONDS", "_csrf_token":"9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0"}'

my-applicationを、実行しているアプリケーションを記述する値に置き換えます。この値は、サーバー管理者がセッションをアプリケーションにマップするために使用できます。アイドル・タイムアウトとタスク・タイムアウトを0に設定すると、セッションおよび送信されたタスクのタイムアウトがサーバーにより決定されることを意味します。CookieヘッダーとJSONペイロードの両方に同じCSRFトークンを指定する必要があります。

レスポンスは次のようになります。

*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 7007 (#0)
> POST /core/v1/sessions HTTP/1.1
> Host: localhost:7007
> User-Agent: curl/7.47.0
> Accept: */*
> Cookie: _csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0
> content-type: application/json
> Content-Length: 159
> 
* upload completely sent off: 159 out of 159 bytes
< HTTP/1.1 201
< SET-COOKIE: SID=abae2811-6dd2-48b0-93a8-8436e078907d;Version=1; HttpOnly
< Content-Length: 0

レスポンスは、作成されたセッションID値をCookieに設定します。セッションID abae2811-6dd2-48b0-93a8-8436e078907dは、後続のリクエストのサンプルとして使用されます。

ステップ3: グラフの読取り

ノート:

事前にロードされたグラフまたはすでに別のセッションで公開されたグラフを分析する場合は、このステップをスキップできます。事前にロードまたは公開されたグラフにアクセスするために必要なものはグラフの名前のみです。

グラフを読み取るには、次の例に示すように、グラフ構成をJSONとしてサーバーに送信します(<graph-config>を実際のPGXグラフ構成のJSON表現に置き換えます)。

curl -v -X POST --cookie '_csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0;SID=abae2811-6dd2-48b0-93a8-8436e078907d'  http://localhost:7007/core/v1/loadGraph -H 'content-type: application/json'  -d  '{"graphConfig":<graph-config>,"graphName":null,"csrf_token":"9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0"}'

次に、Oracleデータベースからプロパティ・グラフを読み取るグラフ構成の例を示します。

{
  "format": "pg",
  "db_engine": "RDBMS",
  "jdbc_url":"jdbc:oracle:thin:@127.0.0.1:1521:orcl122",
  "username":"scott",
  "password":"tiger",
  "max_num_connections": 8,
  "name": "connections",
  "vertex_props": [
    {"name":"name", "type":"string"},
    {"name":"role", "type":"string"},
    {"name":"occupation", "type":"string"},
    {"name":"country", "type":"string"},
    {"name":"political party", "type":"string"},
    {"name":"religion", "type":"string"}
  ],
  "edge_props": [
    {"name":"weight", "type":"double", "default":"1"}
  ],
  "edge_label": true,
  "loading": {
    "load_edge_label": true
  }
}

"graphName": nullを渡すと、サーバーに名前の生成が指示されます。

サーバーは次のように応答します。

* upload completely sent off: 315 out of 315 bytes
< HTTP/1.1 202
< Location: http://localhost:7007/core/v1/futures/8a46ef65-01a9-4bd0-87d3-ffe9dfd2ce3c/status
< Content-Type: application/json;charset=utf-8
< Content-Length: 51
< Date: Mon, 05 Nov 2018 17:22:22 GMT
<
* Connection #0 to host localhost left intact
{"futureId":"8a46ef65-01a9-4bd0-87d3-ffe9dfd2ce3c"}

非同期リクエストについて

PGX RESTエンドポイントのほとんどは非同期です。結果が準備できるまで接続をオープンしたままにしておくかわりに、PGXサーバーはタスクとして送信し、ステータス・コードが200のfuture IDを返します。このIDは、クライアントがタスクのステータスを定期的にリクエストしたり、完了後の結果値を要求するために使用できます。

前述のレスポンスから、次のようにfutureのステータスを要求できます。

curl -v --cookie 'SID=abae2811-6dd2-48b0-93a8-8436e078907d'  http://localhost:7007/core/v1/futures/8a46ef65-01a9-4bd0-87d3-ffe9dfd2ce3c/status

次のような内容が返されます。

< HTTP/1.1 200
< Content-Type: application/json;charset=utf-8
< Content-Length: 730
< Date: Mon, 05 Nov 2018 17:35:19 GMT
< 
* Connection #0 to host localhost left intact
{"id":"eb17f75b-e4c1-4a66-81a0-4ff0f8b4cb92","links":[{"href":"http://localhost:7007/core/v1/futures/eb17f75b-e4c1-4a66-81a0-4ff0f8b4cb92/status","rel":"self","method":"GET","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/futures/eb17f75b-e4c1-4a66-81a0-4ff0f8b4cb92","rel":"abort","method":"DELETE","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/futures/eb17f75b-e4c1-4a66-81a0-4ff0f8b4cb92/status","rel":"canonical","method":"GET","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/futures/eb17f75b-e4c1-4a66-81a0-4ff0f8b4cb92/value","rel":"related","method":"GET","interaction":["async-polling"]}],"progress":"succeeded","completed":true,"intervalToPoll":1}

この出力には、ステータス(この場合はsucceeded)の他に、タスクを取り消すリンク(DELETE)および完了後にタスクの結果を取得するリンク(GET <future-id>/value)も含まれます。

curl -X GET --cookie 'SID=abae2811-6dd2-48b0-93a8-8436e078907d' http://localhost:7007/core/v1/futures/cdc15a38-3422-42a1-baf4-343c140cf95d/value

サーバーにより生成された名前(sample)を含め、ロードされたグラフに関する詳細が返されます。

{"id":"sample","links":[{"href":"http://localhost:7007/core/v1/graphs/sample","rel":"self","method":"GET","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/graphs/sample","rel":"canonical","method":"GET","interaction":["async-polling"]}],"nodeProperties":{"prop1":{"id":"prop1","links":[{"href":"http://localhost:7007/core/v1/graphs/sample/properties/prop1","rel":"self","method":"GET","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/graphs/sample/properties/prop1","rel":"canonical","method":"GET","interaction":["async-polling"]}],"dimension":0,"name":"prop1","entityType":"vertex","type":"integer","transient":false}},"vertexLabels":null,"edgeLabel":null,"metaData":{"id":null,"links":null,"numVertices":4,"numEdges":4,"memoryMb":0,"dataSourceVersion":"1536029578000","config":{"format":"adj_list","separator":" ","edge_props":[{"type":"double","name":"cost"}],"error_handling":{},"vertex_props":[{"type":"integer","name":"prop1"}],"vertex_uris":["PATH_TO_FILE"],"vertex_id_type":"integer","loading":{}},"creationRequestTimestamp":1541242100335,"creationTimestamp":1541242100774,"vertexIdType":"integer","edgeIdType":"long","directed":true},"graphName":"sample","edgeProperties":{"cost":{"id":"cost","links":[{"href":"http://localhost:7007/core/v1/graphs/sample/properties/cost","rel":"self","method":"GET","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/graphs/sample/properties/cost","rel":"canonical","method":"GET","interaction":["async-polling"]}],"dimension":0,"name":"cost","entityType":"edge","type":"double","transient":false}},"ageMs":0,"transient":false}

わかりやすくするために、残りのステップでは、非同期タスクのステータスまたは値を要求する追加のリクエストを省略します。

ステップ4: プロパティの作成

ロードされたグラフでPageRankアルゴリズムを実行する前に、グラフ上にDOUBLE型の頂点プロパティを作成して、計算されたランキング値を保持できるようにする必要があります。

curl -v -X POST --cookie '_csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0;SID=abae2811-6dd2-48b0-93a8-8436e078907d'  http://localhost:7007/core/v1/graphs/sample/properties -H 'content-type: application/json'  -d '{"entityType":"vertex","type":"double","name":"pagerank", "hardName":false,"dimension":0,"_csrf_token":"9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0"}'

返されたfutureの結果をリクエストすると、次のような結果が返されます。

{"id":"pagerank","links":[{"href":"http://localhost:7007/core/v1/graphs/sample/properties/pagerank","rel":"self","method":"GET","interaction":["async-polling"]},{"href":"http://localhost:7007/core/v1/graphs/sample/properties/pagerank","rel":"canonical","method":"GET","interaction":["async-polling"]}],"dimension":0,"name":"pagerank","entityType":"vertex","type":"double","transient":true}

ステップ5: ロードされたグラフでのPageRankアルゴリズムの実行

次の例に、アルゴリズム(この場合はPageRank)を実行する方法を示します。アルゴリズムIDはURLの一部であり、アルゴリズムに渡されるパラメータはJSONペイロードの一部です。

curl -v -X POST --cookie '_csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0;SID=abae2811-6dd2-48b0-93a8-8436e078907d' http://localhost:7007/core/v1/analyses/pgx_builtin_k1a_pagerank/run -H  'content-type: application/json' -d  '{"args":[{"type":"GRAPH","value":"sample"},{"type":"DOUBLE_IN","value":0.001},{"type":"DOUBLE_IN","value":0.85},{"type":"INT_IN","value":100},{"type":"BOOL_IN","value":true},{"type":"NODE_PROPERTY","value":"pagerank"}],"expectedReturnType":"void","workloadCharacteristics":["PARALLELISM.PARALLEL"],"_csrf_token":"9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0"}'

futureが完了すると、結果は次のようになります。

{"success":true,"canceled":false,"exception":null,"returnValue":null,"executionTimeMs":50}

ステップ6: PGQL問合せの実行

PageRankアルゴリズムの結果を問い合せるには、次の例に示すようにPGQL問合せを実行できます。

curl -v -X POST --cookie '_csrf_token=9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0;SID=abae2811-6dd2-48b0-93a8-8436e078907d' http://localhost:7007/core/v1/pgql/run -H 'content-type: application/json'  -d '{"pgqlQuery":"SELECT x.pagerank MATCH (x) WHERE x.pagerank > 0","semantic":"HOMOMORPHISM", "schemaStrictnessMode":true, "graphName" : "sample", "_csrf_token":"9bf51c8f-1c75-455e-9b57-ec3ca1c63cc0"}'

結果は、問合せの結果セットとの対話に使用できる一連のリンクです。

{"id":"pgql_1","links":[{"href":"http://localhost:7007/core/v1/pgqlProxies/pgql_1","rel":"self","method":"GET","interaction":["sync"]},{"href":"http://localhost:7007/core/v1/pgqlResultProxies/pgql_1/elements","rel":"related","method":"GET","interaction":["sync"]},{"href":"http://localhost:7007/core/v1/pgqlResultProxies/pgql_1/results","rel":"related","method":"GET","interaction":["sync"]},{"href":"http://localhost:7007/core/v1/pgqlProxies/pgql_1","rel":"canonical","method":"GET","interaction":["async-polling"]}],"exists":true,"graphName":"sample","resultSetId":"pgql_1","numResults":4}

結果セットの最初の2048要素をリクエストするには、次を送信します。

curl -X GET  --cookie 'SID=abae2811-6dd2-48b0-93a8-8436e078907d' http://localhost:7007/core/v1/pgqlProxies/pgql_1/results?size=2048

レスポンスは次のようになります。

{"id":"/pgx/core/v1/pgqlProxies/pgql_1/results","links":[{"href":"http://localhost:7007/core/v1/pgqlProxies/pgql_1/results","rel":"self","method":"GET","interaction":["sync"]},{"href":"http://localhost:7007/core/v1/pgqlProxies/pgql_1/results","rel":"canonical","method":"GET","interaction":["async-polling"]}],"count":4,"totalItems":4,"items":[[0.3081206521195582],[0.21367103988538017],[0.21367103988538017],[0.2645372681096815]],"hasMore":false,"offset":0,"limit":4,"showTotalResults":true}

3.14 プロパティ・グラフ・スナップショットの管理

プロパティ・グラフ・スナップショットを管理できます。

ノート:

プロパティ・グラフ・スナップショットの管理は上級ユーザーを対象としています。

異なるバージョンのプロパティ・グラフをバイナリ・スナップショットとしてデータベースに永続化できます。バイナリ・スナップショットは、実行時に計算されるグラフ・データのサブグラフを表し、これは将来の使用時に必要になる可能性があります。スナップショットは、インメモリー・アナリストの入力として、またはパラレル・プロパティ・グラフ・データ・ローダーが使用できる出力ストリームとして、後で読み戻すことができます。

Java API OraclePropertyGraphUtils.storeBinaryInMemoryGraphSnapshotを使用して、プロパティ・グラフの<graph_name>SS$表にバイナリ・スナップショットを格納できます。この操作には、プロパティ・グラフ・インスタンス、グラフの名前および所有者、スナップショットのID、およびバイナリ・スナップショットが読み取られる元の入力ストリームを保持しているOracleデータベースへの接続が必要です。スナップショットのタイムスタンプと、スナップショットを表に格納するときに使用する並列度も指定できます。

oraclePropertyGraphUtils.readBinaryInMemGraphSnapshotを使用して、格納されたバイナリ・スナップショットを読み取ることができます。この操作には、プロパティ・グラフ・インスタンス、グラフの名前および所有者、読み取るスナップショットのID、およびバイナリ・ファイル・スナップショットを書き込む先の出力ストリームを保持しているOracleデータベースへの接続が必要です。スナップショット・バイナリ・ファイルを表から読み取るときに使用する並列度も指定できます。

次のコード・スニペットは、Oracleフラットファイル形式のデータ・ファイルからプロパティ・グラフを作成し、新しい頂点を追加し、GraphML形式を使用した出力ストリームにグラフをエクスポートします。この出力ストリームはバイナリ・ファイル・スナップショットを表し、プロパティ・グラフ・スナップショット表に格納されます。最後に、この例はスナップショット表からファイルを読み戻し、そのコンテンツから2番目のグラフを作成します。

String szOPVFile = "../../data/connections.opv"; 
String szOPEFile = "../../data/connections.ope"; 
OraclePropertyGraph opg = OraclePropertyGraph.getInstance(args, szGraphName); 
opgdl = OraclePropertyGraphDataLoader.getInstance(); 
opgdl.loadData(opg, szOPVFile, szOPEFile, 2 /* dop */, 1000, true, 
               "PDML=T,PDDL=T,NO_DUP=T,"); 

// Add a new vertex
Vertex v = opg.addVertex(Long.valueOf("1000"));
v.setProperty("name", "Alice");
opg.commit();

System.out.pritnln("Graph " + szGraphName + " total vertices: " + 
                   opg.countVertices(dop));
System.out.pritnln("Graph " + szGraphName + " total edges: " + 
                   opg.countEdges(dop));


// Get a snapshot of the current graph as a file in graphML format. 
OutputStream os = new ByteArrayOutputStream();
OraclePropertyGraphUtils.exportGraphML(opg, 
                                       os /* output stream */, 
                                       System.out /* stream to show progress */);

// Save the snapshot into the SS$ table
InputStream is = new ByteArrayInputStream(os.toByteArray());
OraclePropertyGraphUtils.storeBinaryInMemGraphSnapshot(szGraphName, 
                                                     szGraphOwner /* owner of the 
                                                                   property graph */,
                                                     conn /* database connection */, 
                                                     is,
                                                     (long) 1 /* snapshot ID */,
                                                     1 /* dop */);
os.close();
is.close();

// Read the snapshot back from the SS$ table
OutputStream snapshotOS = new ByteArrayOutputStream();
OraclePropertyGraphUtils.readBinaryInMemGraphSnapshot(szGraphName, 
                                                    szGraphOwner /* owner of the 
                                                                   property graph */,
                                                    conn /* database connection */, 
                                                    new OutputStream[] {snapshotOS},
                                                    (long) 1 /* snapshot ID */,
                                                    1 /* dop */);

InputStream snapshotIS = new ByteArrayInputStream(snapshotOS.toByteArray());
String szGraphNameSnapshot = szGraphName + "_snap";
OraclePropertyGraph opg = OraclePropertyGraph.getInstance(args,szGraphNameSnapshot); 

OraclePropertyGraphUtils.importGraphML(opg, 
                                       snapshotIS /* input stream */, 
                                       System.out /* stream to show progress */);

snapshotOS.close();
snapshotIS.close();


System.out.pritnln("Graph " + szGraphNameSnapshot + " total vertices: " + 
                   opg.countVertices(dop));
System.out.pritnln("Graph " + szGraphNameSnapshot + " total edges: " + 
                   opg.countEdges(dop));

前述の例では、次のような出力が生成されます。

Graph test total vertices: 79
Graph test total edges: 164
Graph test_snap total vertices: 79
Graph test_snap total edges: 164

3.15 PGXでのユーザー定義関数(UDF)

ユーザー定義関数(UDF)を使用すると、PGXのユーザーはPGQL問合せまたはカスタム・グラフ・アルゴリズムにカスタム・ロジックを追加して、組込み関数をカスタム要件で補完できます。

注意:

UDFはPGXサーバーで任意のコードを実行できるようにするため、機密データにアクセスする可能性があります。さらに、いずれのPGXセッションでも、PGXサーバーで有効になっている任意のUDFを呼び出すことができます。UDFを有効にするアプリケーション管理者は、次の点を確認する必要があります。

  • すべてのUDFコードが信頼できること。
  • UDFが改ざんできない安全な場所に保管されること。

UDFの使用方法

次の簡単な例は、PGXサーバーでUDFを登録して呼び出す方法を示しています。

  1. public staticメソッドのあるクラスを作成します。次に例を示します。
    package my.udfs;
     
    public class MyUdfs {
      public static String concat(String a, String b) {
        return a + b;
      }
    }
    
  2. クラスをコンパイルしてJARファイルに圧縮します。次に例を示します。
    mkdir ./target
    javac -d ./target *.java
    cd target
    jar cvf MyUdfs.jar *
    
  3. JARファイルを/opt/oracle/graph/pgx/server/libにコピーします。
  4. UDF JSON構成ファイルを作成します。たとえば、/path/to/my/udfs/dir/my_udfs.jsonに次のものが含まれているとします。
    {
      "user_defined_functions": [
        {
          "namespace": "my",
          "language": "java",
          "implementation_reference": "my.package.MyUdfs",
          "function_name": "concat",
          "return_type": "string",
          "arguments": [
             {
               "name": "a",
               "type": "string"
             },
             {
               "name": "b",
               "type": "string"
             }
           ]
        }
      ]
    }
  5. /etc/oracle/graph/pgx.confに、UDF構成ファイルが含まれているディレクトリを示します。次に例を示します。
    "udf_config_directory": "/path/to/my/udfs/dir/"
  6. PGXサーバーを再起動します。次に例を示します。
    sudo systemctl restart pgx
  7. PGQL問合せ内からUDFの起動を試行します。次に例を示します。
    graph.queryPgql("SELECT my.concat(my.concat(n.firstName, ' '), n.lastName) FROM MATCH (n:Person)")
  8. PGXアルゴリズム内からUDFの起動を試行します。次に例を示します。
    import oracle.pgx.algorithm.annotations.Udf;
    ....
     
    @GraphAlgorithm
    public class MyAlogrithm {
      public void bomAlgorithm(PgxGraph g, VertexProperty<String> firstName, VertexProperty<String> lastName, @Out VertexProperty<String> fullName) {
     
     
      ... fullName.set(v, concat(firstName.get(v), lastName.get(v))); ...
     
      }
     
      @Udf(namespace = "my")
      abstract String concat(String a, String b);
    }

UDF構成ファイル情報

UDF構成ファイルは、user_defined_functionsの配列を含むJSONファイルです。(このようなファイルの例は、前述の「UDFの使用方法」サブセクションの「UDF JSON構成ファイルの作成」のステップを参照してください)。

各ユーザー定義関数は、次の表に示すフィールドをサポートしています。

表3-5 各UDFのフィールド

フィールド データ・タイプ 説明 必須かどうか
function_name string PGXで識別子として使用される関数の名前 必須
言語 enum[java, javascript] 関数のソース言語(javaまたはjavascript) 必須
return_type enum[boolean, integer, long, float, double, string] 関数の戻り型 必須
引数 オブジェクトの配列 引数の配列。引数ごとに、型、引数名、必須かどうか []
implementation_reference string クラスパス上の関数名への参照 null
ネームスペース string PGXでの関数のネームスペース null
source_function_name string ソース言語での関数の名前 null
source_location string 関数のソース・コードへのローカル・ファイル・パス null

構成されたすべてのUDFは、次に示すフィールドの組合せに関して一意である必要があります。

  • ネームスペース
  • function_name
  • 引数