18 グラフ・サーバー(PGX)に対するPGQL問合せの実行
このセクションでは、グラフ・サーバー(PGX)でPGQL問合せを実行するために使用されるJava APIについて説明します。
- PGQLの開始
 グラフ・サーバー(PGX)でPGQLを開始します。
- オプションを使用したプロパティ・グラフの作成
 グラフ最適化、および頂点が欠落しているエッジの処理の様々なオプションについて学習します。
- グラフ・サーバー(PGX)でサポートされているPGQL機能および制限事項
 グラフ・サーバー(PGX)でサポート対象のPGQL機能、およびサポート対象外のPGQL機能について学習します。
- CREATE PROPERTY GRAPH文を実行するためのJava API
 CREATE PROPERTY GRAPH文を実行する最も簡単な方法は、PgxSession.executePgql(String statement)メソッドを使用することです。
- CREATE PROPERTY GRAPH文を実行するためのPython API
 Python APIを介してCREATE PROPERTY GRAPH文を実行すると、プロパティ・グラフを作成できます。
- SELECT問合せを実行するためのJava API
 このセクションでは、グラフ・サーバー(PGX)でSELECT問合せを実行するためのAPIについて説明します。
- UPDATE問合せを実行するためのJava APIUPDATE問合せは、PGQL 1.3仕様のグラフの変更のセクションで説明されているように、INSERT、UPDATEおよびDELETE操作を使用して既存のグラフに変更を加えます。
- パーティション化されたIDを含むPGQL問合せ
 PGQLのid()関数を使用して、パーティション化されたIDを取得できます。
- PGQL問合せを実行するためのセキュリティ・ツール
 問合せインジェクションから保護するために、リテラルのかわりにバインド変数を使用し、グラフ名、ラベル、プロパティ名などの識別子のかわりにprintIdentifier(String identifier)を使用できます。
- PGQL問合せをチューニングするためのベスト・プラクティス
 このセクションでは、メモリー割当て、並列度および問合せ計画に関するベスト・プラクティスについて説明します。
親トピック: グラフ・サーバー(PGX)の使用
18.1 PGQLスタート・ガイド
グラフ・サーバー(PGX)でPGQLを開始します。
このセクションでは、PGQLの開始方法の例を示します。事前に設定されているデータベース・レルムを想定しています(データベース認証のためのグラフ・サーバーの準備のステップに従ってください)。また、ユーザーがHRスキーマへのreadアクセス権を持っていることも想定しています。
                  
最初に、CREATE PROPERTY GRAPH文を実行して、従業員、部門およびemployee works at departmentを含むグラフを作成します。 
                  
例18-1 グラフ・サーバー(PGX)でのグラフの作成
次の文は、グラフ・サーバー(PGX)にグラフを作成します
String statement =
      "CREATE PROPERTY GRAPH hr_simplified "
    + "  VERTEX TABLES ( "
    + "    hr.employees LABEL employee "
    + "      PROPERTIES ARE ALL COLUMNS EXCEPT ( job_id, manager_id, department_id ), "
    + "    hr.departments LABEL department "
    + "      PROPERTIES ( department_id, department_name ) "
    + "  ) "
    + "  EDGE TABLES ( "
    + "    hr.employees AS works_at "
    + "      SOURCE KEY ( employee_id ) REFERENCES employees (employee_id) "
    + "      DESTINATION departments "
    + "      PROPERTIES ( employee_id ) "
    + "  )";
session.executePgql(statement);
/**
 * To get a handle to the graph, execute:
 */
PgxGraph g = session.getGraph("HR_SIMPLIFIED");
/**
 * You can use this handle to run PGQL queries on this graph.
 * For example, to find the department that “Nandita Sarchand” works for, execute:
 */
String query =
    "SELECT dep.department_name "
  + "FROM MATCH (emp:Employee) -[:works_at]-> (dep:Department) "
  + "WHERE emp.first_name = 'Nandita' AND emp.last_name = 'Sarchand' "
  + "ORDER BY 1";
PgqlResultSet resultSet = g.queryPgql(query);
resultSet.print();
+-----------------+
| department_name |
+-----------------+
| Shipping        |
+-----------------+
/**
 * To get an overview of the types of vertices and their frequencies, execute:
 */
String query =
      "SELECT label(n), COUNT(*) "
    + "FROM MATCH (n) "
    + "GROUP BY label(n) "
    + "ORDER BY COUNT(*) DESC";
PgqlResultSet resultSet = g.queryPgql(query);
resultSet.print();
+-----------------------+
| label(n)   | COUNT(*) |
+-----------------------+
| EMPLOYEE   | 107      |
| DEPARTMENT | 27       |
+-----------------------+
/**
  *To get an overview of the types of edges and their frequencies, execute:
  */
 String query =
    "SELECT label(n) AS srcLbl, label(e) AS edgeLbl, label(m) AS dstLbl, COUNT(*) "
  + "FROM MATCH (n) -[e]-> (m) "
  + "GROUP BY srcLbl, edgeLbl, dstLbl "
  + "ORDER BY COUNT(*) DESC";
PgqlResultSet resultSet = g.queryPgql(query);
resultSet.print();
+---------------------------------------------+
| srcLbl   | edgeLbl  | dstLbl     | COUNT(*) |
+---------------------------------------------+
| EMPLOYEE | WORKS_AT | DEPARTMENT | 106      |
+---------------------------------------------+
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.2 オプションを使用したプロパティ・グラフの作成
グラフ最適化、および頂点が欠落しているエッジの処理の様々なオプションについて学習します。
CREATE PROPERTY GRAPH文でOPTIONS句を使用すると、次の各項で説明するオプションを指定できます。
                  
グラフ最適化オプションの使用
問合せや分析、または更新操作を実行するためのグラフをロードできます。要件に応じて、CREATE PROPERTY GRAPH文のOPTIONS句を使用して、読取りまたは更新のパフォーマンスを最適化できます。
                     
次の表に、OPTIONS句でサポートされる有効なオプションを示します。
                     
表18-1 グラフ最適化オプション
| オプション | 説明 | 
|---|---|
| OPTIMIZED_FOR_READ | これは、読取り集中型のシナリオに使用できます。 | 
| OPTIMIZED_FOR_UPDATES | これはデフォルトのオプションで、高速更新に使用できます。 | 
| SYNCHRONIZABLE | これにより、フラッシュバック・テクノロジを介してグラフを同期できるようになります。ただし、エッジ・キーのいずれかがコンポジット・キーであるか、または数値以外のキーである場合は、例外がスローされます。このような場合、通常はグラフのロードはできますが、PGXによって新しい(コンポジット以外および数値の)エッジ・キーが生成されます。そのため、このようなエッジをデータベースと同期することはできません。 | 
たとえば、次のグラフは、OPTIMIZED_FOR_UPDATESおよびSYNCHRONIZABLEオプションを使用して設定されます。
                     
CREATE PROPERTY GRAPH hr 
VERTEX TABLES ( 
employees LABEL employee, departments LABEL department 
) 
EDGE TABLES ( 
departments AS managed_by 
SOURCE KEY ( department_id ) REFERENCES departments (department_id)
DESTINATION employees 
NO PROPERTIES 
) OPTIONS (OPTIMIZED_FOR_UPDATES, SYNCHRONIZABLE)ノート:
- SYNCHRONIZABLEオプションは、- OPTIMIZED_FOR_UPDATESおよび- OPTIMIZED_FOR_READと組み合せて使用できます。ただし、- OPTIMIZED_FOR_UPDATESと- OPTIMIZED_FOR_READは一緒には使用できず、一緒に使用すると例外がスローされます。
- 同期可能なグラフを作成する場合は、頂点キーとエッジ・キーが数値であり、コンポジットではないことを確認します。
オプションを使用した頂点が欠落しているエッジの処理
エッジのソース頂点または宛先頂点のいずれか、あるいはその両方が欠落している場合は、CREATE PROPERTY GRAPH文のOPTIONS句で次のいずれかの値を構成できます。
                     
- IGNORE EDGE ON MISSING VERTEX: 欠落した頂点のエッジを無視する必要があることを指定します。
- IGNORE EDGE AND LOG ON MISSING VERTEX: 欠落した頂点のエッジを無視し、無視されたすべてのエッジをログに記録する必要があることを指定します。
- IGNORE EDGE AND LOG ONCE ON MISSING VERTEX: 欠落した頂点のエッジを無視し、最初の無視されたエッジのみをログに記録する必要があることを指定します。
- ERROR ON MISSING VERTEX(デフォルト): 頂点が欠落しているエッジに対してエラーをスローする必要があることを指定します。
たとえば、次のグラフはERROR ON MISSING VERTEXオプションを使用して設定されます。
                     
CREATE PROPERTY GRAPH region_graph 
VERTEX TABLES ( 
regions KEY (region_id), 
countries KEY (country_id)
) 
EDGE TABLES ( 
countries AS countries_regions 
SOURCE KEY ( country_id ) REFERENCES countries(country_id) 
DESTINATION KEY (region_id) REFERENCES regions(region_id) 
NO PROPERTIES 
) OPTIONS ( ERROR ON MISSING VERTEX)実行すると、次のエラー・レスポンスが表示されます。
unknown vertex ID received in destination 4 of edge 5IGNORE EDGE AND LOG ON MISSING VERTEXまたはIGNORE EDGE AND LOG ONCE ON MISSING VERTEXオプションを使用する場合は、/etc/oracle/graph/logback.xmlのデフォルトのLogback構成ファイルと、/etc/oracle/graph/logback-server.xmlのグラフ・サーバー(PGX)ロガー構成ファイルを更新して、DEBUGログを記録する必要があります。そのようにすることでのみ、無視されたエッジを/var/opt/log/pgx-server.logファイルで確認できます。
                     
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.3 グラフ・サーバー(PGX)でサポートされているPGQL機能および制限事項
グラフ・サーバー(PGX)でサポート対象のPGQL機能、およびサポート対象外のPGQL機能について学習します。
表18-2 グラフ・サーバー(PGX)でサポートされているPGQL機能および制限事項
| 機能 | グラフ・サーバー上のPGQL (PGX) | 
|---|---|
| CREATE PROPERTY GRAPH | サポート対象 制限事項:
                                        
 | 
| DROP PROPERTY GRAPH | サポート対象外 | 
| 固定長パターン一致 | サポート対象 | 
| 可変長パターン一致の目標 | サポート対象: 
 | 
| 可変長パターン一致の数量詞 | サポート対象: 
 制限事項: 
 | 
| 可変長パスのネスト解除 | サポート対象: 
 制限事項: 
 | 
| GROUP BY | サポート対象 | 
| HAVING | サポート対象 | 
| 集計 | サポート対象: 
 制限事項: 
 | 
| DISTINCT
 | サポート対象 | 
| SELECT v.* | サポート対象 | 
| ORDER BY (+ASC/DESC)、LIMIT、OFFSET | サポート対象 | 
| データ型 | サポート対象: 
 | 
| JSON | 組込みのJSONサポートはありません。ただし、JSON値は STRINGとして格納でき、JavaまたはJavaScriptで記述されたユーザー定義関数(UDF)を使用して操作または問合せできます。 | 
| 演算子 | サポート対象: 
 | 
| 関数および述語 | サポート対象: 
 | 
| ユーザー定義関数 | サポート対象: 
 | 
| 副問合せ: 
 | サポート対象 制限事項 
 | 
| GRAPH_TABLE演算子 | サポート対象 制限事項 
 | 
| INSERT/UPDATE/DELETE | サポート対象 | 
| INTERVALリテラルおよび操作 | サポートされているリテラル: 
 サポートされている操作: 
 | 
また、次では、特定のサポートされているPGQL機能とサポートされていないPGQL機能についても説明します:
- すべてのプロパティの選択のサポート
- 可変長パス問合せのネスト解除
- PGQL問合せでのINTERVALリテラルの使用
- PGQLでのパス・モードの使用
- PGQL Lateral副問合せのサポート
- PGQL GRAPH_TABLE演算子のサポート
- 数量詞の制限
- 定量化されたパターンでのWHERE句およびCOST句の制限事項
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.3.1 すべてのプロパティの選択のサポート
SELECT v.*を使用して、変数vにバインドする頂点またはエッジのすべてのプロパティを選択できます。たとえば:
                     
SELECT label(n), n.* FROM MATCH (n) ORDER BY "number", "name"実行時に、問合せ出力は次のようになります。
+-----------------------------+
| label(n) | number | name    |
+-----------------------------+
| Account  | 1001   | <null>  |
| Account  | 2090   | <null>  |
| Account  | 8021   | <null>  |
| Account  | 10039  | <null>  |
| Person   | <null> | Camille |
| Person   | <null> | Liam    |
| Person   | <null> | Nikita  |
| Company  | <null> | Oracle  |
+-----------------------------+ラベル式を使用して、指定した頂点ラベルまたはエッジ・ラベルに属するプロパティを選択できます。たとえば:
SELECT label(n), n.* FROM MATCH (n:Person) ORDER BY "name"前述の問合せは、指定されたPersonラベルのすべてのプロパティを取得します。
                     
+--------------------+
| label(n) | name    |
+--------------------+
| Person   | Camille |
| Person   | Liam    |
| Person   | Nikita  |
+--------------------+複数の変数を使用してすべてのプロパティを選択した場合、列名が重複しないようにPREFIXを指定することもできます。たとえば:
                     
SELECT n.* PREFIX 'n_', e.* PREFIX 'e_', m.* PREFIX 'm_'
FROM MATCH (n:Account) -[e:transaction]-> (m:Account)
ORDER BY "e_amount"問合せの出力は次のようになります。
+--------------------------------+
| n_number | e_amount | m_number |
+--------------------------------+
| 10039    | 1000.0   | 8021     |
| 8021     | 1500.3   | 1001     |
| 8021     | 3000.7   | 1001     |
| 2090     | 9900.0   | 10039    |
| 1001     | 9999.5   | 2090     |
+--------------------------------+18.3.2 可変長パス問合せのネスト解除
可変長パス問合せ(SHORTESTパスやCHEAPESTパスなど)のネストを解除して、パスに沿った頂点またはエッジごとに個別の行を取得できます。
                     
- ONE ROW PER MATCH(デフォルト・オプション)
- ONE ROW PER VERTEX(vertex_variable)
- ONE ROW PER STEP(edge_source_variable,edge_variable,edge_destination_variable)
たとえば、次のPGQL問合せではONE ROW PER STEPオプションを使用します。
                     
SELECT  v1.ACCT_ID AS src_no, k.TXN_AMOUNT, v2.ACCT_ID AS dest_no 
FROM MATCH ALL SHORTEST (a:Accounts) -[e:transfers]->+ (b:Accounts)
ONE ROW PER STEP( v1,k,v2 )
WHERE a.ACCT_ID = 284 AND b.ACCT_ID = 616ONE ROW PER STEPオプションは、最小ホップが0より大きいパスのみをサポートするため、*修飾子はこのオプションでサポートされていないことに注意してください。
                     
実行すると、前述の問合せでは、対応するソース頂点および宛先頂点によってバインドされているパス上のエッジごとに1行を取得します。
+-------------------------------+
| src_no | TXN_AMOUNT | dest_no |
+-------------------------------+
| 744    | 1000.0     | 616     |
| 772    | 1000.0     | 744     |
| 284    | 1000.0     | 772     |
| 744    | 1000.0     | 616     |
| 772    | 1500.0     | 744     |
| 284    | 1000.0     | 772     |
+-------------------------------+グラフ・ビジュアライゼーション・ツールを使用して、パスに沿ってONE ROW PER STEPを使用してエッジを視覚化することもできます。
                     
ONE ROW PER VERTEXオプションを指定した問合せの例を次に示します。
                     
SELECT k.acct_id AS id, k.acct_name AS name
FROM MATCH ANY SHORTEST (a:Accounts) ((src:Accounts)-[e:transfers]->){1,3}(b:Accounts)
ONE ROW PER VERTEX(k)
WHERE a.acct_id=284 AND b.acct_id=616実行時には、前述の問合せはパスに沿って頂点ごとに1行を取得します。
+----------------+
| id  | name     |
+----------------+
| 616 | Account4 |
| 744 | Account3 |
| 772 | Account2 |
| 284 | Account1 |
+---------------+
再帰的なパスのネスト解除問合せ用の組込み関数のサポート
PGQLは、次の2つの組込み関数をサポートしており、これらはパスのネスト解除オプション(ONE ROW PER VERTEX、ONE ROW PER STEPまたはONE ROW PER MATCH)のいずれかと組み合せて使用できます。
                        
- MATCH_NUMBER(k): ネスト解除されたパスごとに一意のパス単位の識別子を戻します(つまり、2つの行の元が同じパスである場合、それらの- MATCH_NUMBER(k)は同じです)。
- ELEMENT_NUMBER(k): パスに沿って頂点またはエッジの要素番号を戻します。左端の頂点には- 1、2番目には- 3、その次には- 5のように、頂点には奇数の番号が付けられます。左端のエッジには- 2、次のエッジには- 4のように、エッジには偶数の番号が割り当てられます。
たとえば、次のPGQL問合せでは、ONE ROW PER VERTEXオプションを指定してMATCH_NUMBER(k)およびELEMENT_NUMBER(k)関数を使用します。
                        
SELECT k.*, match_number(k), element_number(k)
FROM MATCH ANY SHORTEST (a:Accounts) -[e:transfers]->* (b:Accounts) ONE ROW PER VERTEX ( k )
WHERE a.acct_id = 284 AND b.acct_id = 616前述の問合せを実行すると、次の出力が生成されます。頂点に対して戻されたelement_number(k)は奇数の値であることに注意してください。前述の問合せではANYパス・パターンを使用しているため、出力には任意のパスが1つのみ表示されます。このため、match_number(k)はパス内のすべての行で同じです。
                        
+-----------------------------------------------------------+
| ACCT_ID | ACCT_NAME | match_number(k) | element_number(k) |
+-----------------------------------------------------------+
| 616     | Account   | 0               | 7                 |
| 744     | Account   | 0               | 5                 |
| 772     | Account   | 0               | 3                 |
| 284     | Account   | 0               | 1                 |
+-----------------------------------------------------------+
次の例に、ONE ROW PER STEPオプションを指定してMATCH_NUMBER(k)およびELEMENT_NUMBER(k)関数を使用したPGQL問合せを示します。 
                        
SELECT v1.acct_id AS src_no,k.txn_amount,v2.acct_id AS dest_no, match_number(k), element_number(k)
FROM MATCH ALL SHORTEST (a:Accounts) -[e:transfers]->+ (b:Accounts)
ONE ROW PER STEP( v1,k,v2 )
WHERE a.acct_id = 284 AND b.acct_id = 616前述の問合せ出力は次のようになります。match_number(k)で識別されたパスが2つあり、エッジは偶数のelement_number(k)値とともに表示されています。
                        
+---------------------------------------------------------------------+
| src_no | txn_amount | dest_no | match_number(k) | element_number(k) |
+---------------------------------------------------------------------+
| 744    | 1000.0     | 616     | 0               | 6                 |
| 772    | 1000.0     | 744     | 0               | 4                 |
| 284    | 1000.0     | 772     | 0               | 2                 |
| 744    | 1000.0     | 616     | 1               | 6                 |
| 772    | 1500.0     | 744     | 1               | 4                 |
| 284    | 1000.0     | 772     | 1               | 2                 |
+---------------------------------------------------------------------+18.3.3 PGQL問合せでのINTERVALリテラルの使用
PGQL問合せでINTERVALリテラルを使用すると、PGQL Temporalデータ型に対する間隔の加算または減算をそれぞれ実行できます。
                        
INTERVAL型は期間で、キーワード"INTERVAL"とそれに続く数値および時間単位で構成されます。たとえば、INTERVAL '1' DAYとなります。
                        
次の表に、INTERVAL値でサポートされる有効な時間単位を示します。
                        
表18-3 INTERVAL値のフィールドの有効な値
                           
| キーワード | サポートされる有効な値 | 
|---|---|
| YEAR | <間隔先行フィールド精度>以外は制約なし | 
| MONTH | 月(年単位) (0-11) | 
| DAY | <間隔先行フィールド精度>以外は制約なし | 
| HOUR | 時間(日単位) (0-23) | 
| MINUTE | 分(時間単位) (0-59) | 
| SECOND | 秒(分単位) (0-59.999...) | 
次のINTERVAL操作がTemporalデータ型でサポートされています。
                        
- TEMPORAL TYPE + INTERVAL
- INTERVAL + TEMPORAL TYPE
- TEMPORAL TYPE - INTERVAL
たとえば、次のPGQL問合せでは、n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'である個人を取得します。
                        
opg4j> graph.queryPgql("SELECT n.name, n.birthdate FROM MATCH (n:Person) WHERE n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'").print()graph.queryPgql("SELECT n.name, n.birthdate FROM MATCH (n:Person) WHERE n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'").print();graph.query_pgql("SELECT n.name, n.birthdate FROM MATCH (n:Person) WHERE n.birthdate + INTERVAL '20' YEAR > TIMESTAMP '2000-01-01 00:00:00'").print()実行時に、問合せ出力は次のようになります。
+--------------------------+
| name  | birthdate        |
+--------------------------+
| Mary  | 1982-09-25T00:00 |
| Alice | 1987-02-01T00:00 |
+--------------------------+18.3.4 PGQLでのパス・モードの使用
次のパス・モードは、ANY、ALL、ANY SHORTEST、SHORTEST k、およびALL SHORTESTと組み合せて使用できます。
                     
- WALK(デフォルト・パス・モード):ウォークは、一連の頂点とエッジを介してグラフをトラバースします。ウォークでアクセスした頂点とエッジを繰り返すことができます。したがって、このデフォルトのパス・モードではパスのフィルタリングは行われません。
- TRAIL:トレイルはエッジを繰り返すことなくグラフをトラバースしています。したがって、エッジを繰り返すパス・バインディングは返されません。
 前述の出力では、両方のパスに頂点8021と1001が2回含まれていますが、エッジが繰り返されないかぎり、まだトレイルは有効です。- SELECT CAST(a.number AS STRING) || ' -> ' || LISTAGG(x.number, ' -> ') AS accounts_along_path FROM MATCH ALL TRAIL PATHS (a IS account) (-[IS transaction]-> (x)){2,} (b IS Account) WHERE a.number = 8021 AND b.number = 1001 +-----------------------------------------------+ | accounts_along_path | +-----------------------------------------------+ | 8021 -> 1001 -> 2090 -> 10039 -> 8021 -> 1001 | | 8021 -> 1001 -> 2090 -> 10039 -> 8021 -> 1001 | +-----------------------------------------------+
- ACYCLIC:グラフ・トラバースの開始頂点と終了頂点が異なる場合、これはパスにサイクルがないことを意味します。この場合、頂点が繰り返されるパス・バインディングは返されません。
 前述の問合せで、10の最短パスがリクエストされました。ただし、他のすべてのパスが循環するため、2つのみが返されます。- SELECT CAST(a.number AS STRING) || ' -> ' || LISTAGG(x.number, ' -> ') AS accounts_along_path FROM MATCH SHORTEST 10 ACYCLIC PATHS (a IS account) (-[IS transaction]-> (x))+ (b) WHERE a.number = 10039 AND b.number = 1001 +-----------------------+ | accounts_along_path | +-----------------------+ | 10039 -> 8021 -> 1001 | | 10039 -> 8021 -> 1001 | +-----------------------+
- SIMPLE: 単純なウォークは、頂点を繰り返さずにグラフをトラバースします。したがって、頂点を繰り返すパス・バインディングは返されません。唯一の例外は、繰り返される頂点がパスの最初の頂点と最後の頂点であることです。
 前述の問合せは循環パスを返します。このパスは、同じ頂点で開始および終了し、パスに他のサイクルがないため、有効かつ単純なパスです。- SELECT CAST(a.number AS STRING) || ' -> ' || LISTAGG(x.number, ' -> ') AS accounts_along_path FROM MATCH ANY SIMPLE PATH (a IS account) (-[IS transaction]-> (x))+ (a) WHERE a.number = 10039 +----------------------------------------+ | accounts_along_path | +----------------------------------------+ | 10039 -> 8021 -> 1001 -> 2090 -> 10039 | +----------------------------------------+
パス・モードは、構文的にはANY、ALL、ANY SHORTEST、SHORTEST k、ALL SHORTEST、CHEAPESTおよびCHEAPEST kの後に配置されます。パス・モードの後には、PATHまたはPATHSキーワードが続くこともあります。
                     
すべての無限量数量詞に対してTRAIL、ACYCLICまたはSIMPLEの一致パス・モードを使用すると、グラフ・パターン一致の結果セットが有限であることが保証されます。
                     
18.3.5 PGQL Lateral副問合せのサポート
LATERAL副問合せを使用して、ある問合せの出力行を別の問合せに渡すことができます。サポートされているのは単一のLATERAL副問合せのみです。
                     
たとえば、別のORDER BY句またはGROUP BY句の上にORDER BY句またはGROUP BY句を使用できます。
                     
/* Find the top-5 largest transactions and return the account number
   that received the highest number of such large transactions */
SELECT recipient, COUNT(*) AS num_large_transactions
FROM LATERAL ( SELECT m.number AS recipient
               FROM MATCH (n:account) -[e:transaction]-> (m:account)
               ORDER BY e.amount DESC
               LIMIT 5 )
GROUP BY recipient
ORDER BY num_large_transactions DESC
LIMIT 1また、FROM句のLATERAL副問合せの後に1つ以上のMATCH句を指定できます。たとえば:
                     
SELECT path_num, elem_num, owner.name
FROM LATERAL ( SELECT v, MATCHNUM(v) AS path_num, ELEMENT_NUMBER(v) AS elem_num
               FROM MATCH SHORTEST 2 PATHS (a1:account) -[e:transaction]->* (a2:account)
                      ONE ROW PER VERTEX ( v )
               WHERE a1.number = 10039 AND a2.number = 2090 )
    , MATCH (v) -[:owner]-> (owner:Person|Company)
ORDER BY path_num, elem_num FROM句にLATERAL副問合せが含まれている場合、LATERAL副問合せはFROM句の最初の表式である必要があることに注意することが重要です。FROM句には追加のMATCH句を含めることができますが、追加のLATERAL副問合せを含めることはできません。
                     
18.3.6 PGQL GRAPH_TABLE演算子のサポート
 PGQLのGRAPH_TABLE演算子によって、グラフ・サーバー(PGX)にロードされるグラフとデータベース上のグラフの間の相互運用性が向上します。 
                     
- グラフ・パターンMATCH問合せのラベル述語では、ISキーワードを使用する必要があります。
- 出力行数を制限するには、LIMIT x句のかわりにFETCH [FIRST/NEXT] x [ROW/ROWS]句を使用します。
- エッジの方向を確認するには、[NOT] is_source_of(e, v)/[NOT] is_destination_of(e, v)のかわりにv IS [NOT] SOURCE [OF] e/v IS [NOT] DESTINATION [OF] eを標準フォームとして使用します。
- 頂点またはエッジに指定したラベルがあるかどうかを確認するには、has_label(x, <label_string>)の代替としてx IS [NOT] LABELED <label_string>述語を使用します。
- kの最短パスと一致させるには、MATCH TOP k SHORTEST (n) –[e]->* (m)の標準形式としてMATCH SHORTEST k (n) –[e]->* (m)を使用します。
- 固定長パス・パターンの前にはALLキーワード(オプション)があります。MATCH ALL (n) –[e]->{1,4} (m)の代替としてのMATCH (n) –[e]->{1,4} (m)。
たとえば:
SELECT *
FROM GRAPH_TABLE ( financial_transactions
       MATCH ALL TRAIL (a IS account) -[e IS transaction]->* (b IS account)
       /* optional ONE ROW PER VERTEX/STEP clause here */
       WHERE a.number = 8021 AND b.number = 1001
       COLUMNS ( LISTAGG(e.amount, ', ') AS amounts )
     )ORDER BY amounts+----------------------------------------+
| amounts                                |
+----------------------------------------+
| 1500.3                                 |
| 1500.3, 9999.5, 9900.0, 1000.0, 3000.7 |
| 3000.7                                 |
| 3000.7, 9999.5, 9900.0, 1000.0, 1500.3 |
+----------------------------------------+18.3.7 数量詞の制限
到達可能性パターンと最短パス・パターンでは、*、+、{1,4}などのすべての数量詞がサポートされていますが、最小コスト・パス・パターンでサポートされている数量詞は* (ゼロ以上)のみです。
                     
18.3.8 定量化されたパターンでのWHERE句およびCOST句の制限事項
到達可能性パターンや最短パス・パターンおよび最小コスト・パス・パターンなどの定量化されたパターンのWHERE句およびCOST句は、単一の変数の参照のみに制限されます。
                     
次に、WHERE句またはCOST句が、zeroまたはoneのかわりに2つの変数eおよびxを参照しているためにサポートされていない問合せの例を示します。 
                     
... PATH p AS (n) –[e]-> (m) WHERE e.prop > m.prop ...
... SHORTEST ( (n) (-[e]-> (x) WHERE e.prop + x.prop > 10)* (m) ) ...
... CHEAPEST ( (n) (-[e]-> (x) COST e.prop + x.prop )* (m) ) ...次の問合せがサポートされているのは、副問合せは外部スコープから単一の変数aのみを参照し、変数cは副問合せで新たに導入されたためにカウントされないためです。
                     
... PATH p AS (a) -> (b)
      WHERE EXISTS ( SELECT * FROM MATCH (a) -> (c) ) ...18.4 CREATE PROPERTY GRAPH文を実行するためのJava API
CREATE PROPERTY GRAPH文を実行する最も簡単な方法は、PgxSession.executePgql(String statement)メソッドを使用することです。
                  
例18-2 CREATE PROPERTY GRAPH文の実行
String statement =
      "CREATE PROPERTY GRAPH hr_simplified "
    + "  VERTEX TABLES ( "
    + "    hr.employees LABEL employee "
    + "      PROPERTIES ARE ALL COLUMNS EXCEPT ( job_id, manager_id, department_id ), "
    + "    hr.departments LABEL department "
    + "      PROPERTIES ( department_id, department_name ) "
    + "  ) "
    + "  EDGE TABLES ( "
    + "    hr.employees AS works_at "
    + "      SOURCE KEY ( employee_id ) REFERENCES employees (employee_id) "
    + "      DESTINATION departments "
    + "      PROPERTIES ( employee_id ) "
    + "  )";
session.executePgql(statement);
PgxGraph g = session.getGraph("HR_SIMPLIFIED");
/**
 * Alternatively, one can use the prepared statement API, for example:
 */
PgxPreparedStatement stmnt = session.preparePgql(statement);
stmnt.execute();
stmnt.close();
PgxGraph g = session.getGraph("HR_SIMPLIFIED");
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.5 CREATE PROPERTY GRAPH文を実行するためのPython API
Python APIを介してCREATE PROPERTY GRAPH文を実行してプロパティ・グラフを作成できます。
Pythonクライアントを使用したプロパティ・グラフの作成
- Pythonクライアントを起動します。./bin/opg4py --base_url https://localhost:7007 --user customer_360
- 次のようにCREATE PROPERTY GRAPH文を定義して実行します。statement = ( "CREATE PROPERTY GRAPH "+ "<graph_name>" + " " + "VERTEX TABLES ( " + "bank_accounts " + "KEY(acct_id) " + "LABEL Account PROPERTIES (acct_id) " + ")" + "EDGE TABLES ( " + "bank_txns " + "KEY (txn_id) " + "SOURCE KEY (from_acct_id) REFERENCES bank_accounts (acct_id) " + "DESTINATION KEY (to_acct_id) REFERENCES bank_accounts (acct_id) " + "LABEL Transfer PROPERTIES(amount) " + ")") >>> session.prepare_pgql(statement).execute()<graph_name>は、グラフの名前です。 グラフが作成され、get_graphメソッドで確認できます。>>> graph = session.get_graph("<graph_name>") >>> graph PgxGraph(name:<graph_variable>, v: 1000, e: 5001, directed: True, memory(Mb): 0)
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.6 SELECT問合せを実行するためのJava API
このセクションでは、グラフ・サーバー(PGX)でSELECT問合せを実行するためのAPIについて説明します。
                  
- グラフ・サーバー(PGX)のグラフに対するSELECT問合せの実行PgxGraph.queryPgql(String query)メソッドは、現在のセッションで問合せを実行します。このメソッドは、PgqlResultSetを返します。
- PGXセッションに対するSELECT問合せの実行PgxSession.queryPgql(String query)メソッドは、セッションで指定された問合せを実行し、PgqlResultSetを返します。
- 結果セットの反復処理
 結果セットを反復処理するには、JDBCに似た方法またはJava Iteratorインタフェースを使用する方法があります。
- 結果セットの出力PgqlResultSet (パッケージoracle.pgx.api)の次のメソッドは、結果セットを出力するために使用されます。
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.6.1 グラフ・サーバー(PGX)のグラフに対するSELECT問合せの実行
PgxGraph.queryPgql(String query)メソッドは、現在のセッションで問合せを実行します。このメソッドは、PgqlResultSetを返します。
                     
問合せはPGXグラフに対して直接実行されるため、MATCH句内のON句は省略できます。同じ理由で、INSERT句内のINTO句は省略できます。ただし、ON句およびINTO句でグラフ名を明示的に指定する場合、これらのグラフ名がグラフの実際の名前(PgxGraph.getName())と一致している必要があります。
                     
親トピック: SELECT問合せを実行するためのJava API
18.6.2 PGXセッションに対するSELECT問合せの実行
PgxSession.queryPgql(String query)メソッドは、セッションで指定された問合せを実行し、PgqlResultSetを返します。
                     
MATCH句内のON句、およびINSERT句内のINTO句は、指定する必要があり、省略できません。この時点では、単一の問合せで複数のグラフのデータを結合することはまだサポートされていないため、問合せのすべてのON句およびINTO句で同じグラフを参照する必要があります。
                     
親トピック: SELECT問合せを実行するためのJava API
18.6.3 結果セットの反復処理
結果セットを反復処理するには、JDBCに似た方法またはJava Iteratorインタフェースを使用する方法があります。
JDBCのような反復の場合、PgqlResultSet (パッケージoracle.pgx.api)のメソッドはjava.sql.ResultSetのメソッドと似ています。主な違いは、PGQLの結果セット・インタフェースはJava 8で導入された新しい日時ライブラリに基づいているのに対し、java.sql.ResultSetはレガシーjava.util.Dateに基づいていることです。ギャップを埋めるために、PGQLの結果セットは、java.util.Dateをまだ使用しているアプリケーションにgetLegacyDate(..)を提供します。
                     
PgqlResultSetには、最初の行の前に初期設定されるcursorがあります。その後、次のメソッドを使用してカーソルを再配置できます。
                        - next() : boolean
- previous() : boolean
- beforeFirst()
- afterLast()
- first() : boolean
- last() : boolean
- absolute(long row) : boolean
- relative(long rows) : boolean
- getObject(int columnIdx) : Object
- getObject(String columnName) : Object
- getString(int columnIdx) : String
- getString(String columnName) : String
- getInteger(int columnIdx) : Integer
- getInteger(String columnName) : Integer
- getLong(int columnIdx) : Long
- getLong(String columnName) : Long
- getFloat(int columnIdx) : Float
- getFloat(String columnName) : Float
- getDouble(int columnIdx) : Double
- getDouble(String columnName) : Double
- getBoolean(int columnIdx) : Boolean
- getBoolean(String columnName) : Boolean
- getVertexLabels(int columnIdx) : Set<String>
- getVertexLabels(String columnName) : Set<String>
- getDate(int columnIdx) : LocalDate
- getDate(String columnName) : LocalDate
- getTime(int columnIdx) : LocalTime
- getTime(String columnName) : LocalTime
- getTimestamp(int columnIdx) : LocalDateTime
- getTimestamp(String columnName) : LocalDateTime
- getTimeWithTimezone(int columnIdx) : OffsetTime
- getTimeWithTimezone(String columnName) : OffsetTime
- getTimestampWithTimezone(int columnIdx) : OffsetDateTime
- getTimestampWithTimezone(String columnName) : OffsetDateTime
- getLegacyDate(int columnIdx) : java.util.Date
- getLegacyDate(String columnName) : java.util.Date
- getVertex(int columnIdx) : PgxVertex<ID>
- getVertex(String columnName) : PgxVertex<ID>
- getEdge(int columnIdx) : PgxEdge
- getEdge(String columnName) : PgxEdge
詳細は、Java ドキュメントを参照してください。
最後に、結果セットのリソースを解放するPgqlResultSet.close()があり、PgqlResultSet.getMetaData()を使用して列名と列数を取得できます。
                     
結果セットの反復の例は次のとおりです。
PgqlResultSet resultSet = g.queryPgql(
    "   SELECT owner.name AS account_holder, SUM(t.amount) AS total_transacted_with_Nikita "
  + "     FROM MATCH (p:Person) -[:ownerOf]-> (account1:Account) " 
  + "        , MATCH (account1) -[t:transaction]- (account2) "
  + "        , MATCH (account2:Account) <-[:ownerOf]- (owner:Person|Company) "
  + "    WHERE p.name = 'Nikita' "
  + " GROUP BY owner");
while (resultSet.next()) {
  String accountHolder = resultSet.getString(1);
  long totalTransacted = resultSet.getLong(2);
  System.out.println(accountHolder + ": " + totalTransacted);
}
resultSet.close();
前述の例の出力は次のようになります。
Oracle: 4501
Camille: 1000
さらに、PgqlResultSetもJava Iteratorインタフェースを介して反復可能です。結果セットに対する"for each loop"の例は、次のとおりです。
                        
for (PgxResult result : resultSet) {
  String accountHolder = result.getString(1);
  long totalTransacted = result.getLong(2);
  System.out.println(accountHolder + ": " + totalTransacted);
}
前述の例の出力は次のようになります。
Oracle: 4501
Camille: 1000PgqlResultSetで使用可能なものと同じgetterをPgxResultでも使用できることに注意してください。
                        
親トピック: SELECT問合せを実行するためのJava API
18.6.4 結果セットの出力
PgqlResultSet (パッケージoracle.pgx.api)の次のメソッドは、結果セットを出力するために使用されます。
                     
- print() : PgqlResultSet
- print(long numResults) : PgqlResultSet
- print(long numResults, int from) : PgqlResultSet
- print(PrintStream printStream, long numResults, int from) : PgqlResultSet
たとえば:
g.queryPgql("SELECT COUNT(*) AS numPersons FROM MATCH (n:Person)").print().close()
+------------+
| numPersons |
+------------+
| 3          |
+------------+もう1つ例を示します:
PgqlResultSet resultSet = g.queryPgql(
    "   SELECT owner.name AS account_holder, SUM(t.amount) AS total_transacted_with_Nikita "
  + "     FROM MATCH (p:Person) -[:ownerOf]-> (account1:Account) " 
  + "        , MATCH (account1) -[t:transaction]- (account2) "
  + "        , MATCH (account2:Account) <-[:ownerOf]- (owner:Person|Company) "
  + "    WHERE p.name = 'Nikita' "
  + " GROUP BY owner")
resultSet.print().close()
+-----------------------------------------------+
| account_holder | total_transacted_with_Nikita |
+-----------------------------------------------+
| Camille        | 1000.0                       |
| Oracle         | 4501.0                       |
+-----------------------------------------------+親トピック: SELECT問合せを実行するためのJava API
18.7 UPDATE問合せを実行するためのJava API
UPDATE問合せは、PGQL 1.3仕様のグラフの変更のセクションで説明されているように、INSERT、UPDATEおよびDELETE操作を使用して既存のグラフに変更を加えます。
                  
INSERTを使用するとグラフに新しい頂点およびエッジを挿入でき、UPDATEを使用するとプロパティを新しい値に設定して既存の頂点およびエッジを更新でき、DELETEを使用するとグラフから頂点およびエッジを削除できます。
                  
- PGQLによるグラフの更新可能性
 Oracle RDBMSまたはCSVファイルからPGXにロードされたグラフ・データは、PGQLを介してすぐに更新することはできません。
- グラフ・サーバー(PGX)のグラフに対するUPDATE問合せの実行
 グラフに対してUPDATE問合せを実行するには、PgxGraph.executePgql(String query)メソッドを使用します。
- PGXセッションに対するUPDATE問合せの実行
 現時点では、PgxSessionに対するUPDATE問合せの実行はサポートされていないため、更新は常にPgxGraphに対して実行する必要があります。セッションからグラフを取得するには、PgxSession.getGraph(String graphName)メソッドを使用します。
- グラフの基礎となるスキーマの変更INSERT操作では、既知のラベルとプロパティを持つ頂点とエッジのみを挿入できます。同様に、UPDATE操作では、既知のプロパティの値のみを設定できます。したがって、新しいデータは常にグラフの既存のスキーマに準拠する必要があります。
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.7.1 PGQLによるグラフの更新可能性
Oracle RDBMSまたはCSVファイルからPGXにロードされたグラフ・データは、PGQLを介してすぐに更新することはできません。
最初に、PgxGraph.clone()メソッドを使用してデータのコピーを作成する必要があります。結果のグラフは完全に更新可能です。
                     
次のケースについて検討します。
// load a graph from the RDBMS or from CSV
PgxGraph g1 = session.readGraphByName("<graph>",GraphSource.PG_VIEW);
// create an updatable copy of the graph
PgxGraph g2 = g1.clone("new_graph_name");
// insert an additional vertex into the graph
g2.executePgql("INSERT VERTEX v " +
               "         LABELS ( Person ) " +
               "         PROPERTIES ( v.firstName = 'Camille', " +
               "                      v.lastName = ' Mullins')"); 
さらに、前述の例の最後の2つのステップを組み合せて単一のステップにするPgxGraph.cloneAndExecutePgql(String query, String graphName)メソッドもあります。
                        
// create an updatable copy of the graph while inserting a new vertex
PgxGraph g2_copy = g1.cloneAndExecutePgql(
                     "INSERT VERTEX v " +
                     "         LABELS ( Person ) " +
                     "         PROPERTIES ( v.firstName = 'Camille', " +
                     "                      v.lastName = ' Mullins') "
                   , "new_graph_name");PgxGraph.clone()を介して作成されるグラフは、セッションに対してローカルであることに注意してください。ただし、PgxGraph.publish(..)メソッドを介して他のセッションと共有することはできますが、PGQLを使用して更新することはできなくなりました。セッションローカル・グラフのみが更新可能ですが、永続グラフは更新できません。
                        
親トピック: UPDATE問合せを実行するためのJava API
18.7.2 グラフ・サーバー(PGX)のグラフに対するUPDATE問合せの実行
グラフに対してUPDATE問合せを実行するには、PgxGraph.executePgql(String query)メソッドを使用します。
                     
次に、INSERT問合せの例を示します。
                        
g.executePgql("INSERT VERTEX v " +
              "         LABELS ( Person ) " +
              "         PROPERTIES ( v.firstName = 'Camille', " +
              "                      v.lastName = ' Mullins' ) "); 
INSERTのINTO句は省略できることに注意してください。INTO句を使用する場合、INTO句のグラフ名は、問合せが実行されるPGXグラフ(PgxGraph.getName())の名前に対応している必要があります。 
                        
次に、UPDATE問合せの例を示します。
                        
// set the date of birth of Camille to 2014-11-15
g.executePgql("UPDATE v SET ( v.dob = DATE '2014-11-14' ) " +
              "FROM MATCH (v:Person) " +
              "WHERE v.firstName = 'Camille' AND v.lastName = ' Mullins' "); 
次に、DELETE問合せの例を示します。
                        
// delete Camille from the graph
g.executePgql("DELETE v " +
              "FROM MATCH (v:Person) " +
              "WHERE v.firstName = 'Camille' AND v.lastName = 'Mullins' "); 親トピック: UPDATE問合せを実行するためのJava API
18.7.3 PGXセッションに対するUPDATE問合せの実行
現時点では、PgxSessionに対するUPDATE問合せの実行はサポートされていないため、更新は常にPgxGraphに対して実行する必要があります。セッションからグラフを取得するには、PgxSession.getGraph(String graphName)メソッドを使用します。
                     
親トピック: UPDATE問合せを実行するためのJava API
18.7.4 グラフの基礎となるスキーマの変更
INSERT操作では、既知のラベルとプロパティを持つ頂点とエッジのみを挿入できます。同様に、UPDATE操作では、既知のプロパティの値のみを設定できます。したがって、新しいデータは常にグラフの既存のスキーマに準拠する必要があります。
                     
ただし、グラフのスキーマを更新するためのPGX APIはいくつか存在します。新しいラベルを追加するためのAPIは存在しませんが、PgxGraph.createVertexProperty(PropertyType type, String name)およびPgxGraph.createEdgeProperty(PropertyType type, String name)メソッドを介して新しいプロパティを追加できます。新しいプロパティは、ラベルに関係なく、グラフ内の各頂点/エッジにアタッチされます。最初はプロパティにデフォルト値が割り当てられますが、その後UPDATE文を介して値を更新できます。
                     
次のケースについて検討します。
// load a graph from the RDBMS or from CSV
PgxGraph g = session.readGraphByName("<graph>",GraphSource.PG_VIEW);
// add a new property to the graph
g.createVertexProperty(PropertyType.LOCAL_DATE, "dob");
// set the date of birth of Camille to 2014-11-15
g.executePgql("UPDATE v SET ( v.dob = DATE '2014-11-14' ) " +
              "FROM MATCH (v:Person) " +
              "WHERE v.firstName = 'Camille' AND v.lastName = ' Mullins' ");親トピック: UPDATE問合せを実行するためのJava API
18.8 パーティション化されたIDを含むPGQL問合せ
PGQLのid()関数を使用して、パーティション化されたIDを取得できます。
PGQLのSELECT問合せ
次に、パーティション化されたIDをPGQLのSELECT問合せを使用して取得する例をいくつか示します。
                     
g.queryPgql("SELECT id(n) FROM MATCH(n)").print().close()次のように出力されます。
+-------------+
| id(n)       |
+-------------+
| Accounts(2) |
| Accounts(4) |
| Accounts(6) |
+-------------+
g.queryPgql("SELECT n.name FROM MATCH(n) WHERE id(n) = 'Accounts(1)'").print().close()次のように出力されます。
+-------+
| name  |
+-------+
| User1 |
+-------+g.queryPgql("SELECT LABEL(n), n.name from MATCH(n) WHERE n.id = 1").print().close()次のように出力されます。
+------------------+
| label(n) | name  |
+------------------+
| Accounts | User1 |
+------------------+PGXでは、一定時間で頂点を取得することでWHERE id(n) = 'Accounts(1)'やWHERE n.id = 1などの述語を含む問合せを効率的に処理できるように、キーの一意索引が自動的に作成されます。
                     
バインド変数の使用
パーティション化されたIDは、バインド値としてPgxPreparedStatementに渡すこともできます。
                     
たとえば:
PgxPreparedStatement statement = g.preparePgql("SELECT n.name FROM MATCH (n) WHERE id(n)= ?")
statement.setString(1, "Accounts(1)")
statement.executeQuery().print().close()次のように出力されます。
+-------+
| name  |
+-------+
| User1 |
+-------+PGQLのINSERT問合せ
INSERT問合せでは、キー・プロパティが存在する場合はキー・プロパティの値を指定する必要があります。この値は、その後、頂点キーまたはエッジ・キーに使用されます。
                     
たとえば、次のようにINSERTを実行できます。
                     
g.executePgql("INSERT VERTEX v LABELS (Accounts) PROPERTIES (v.id = 1001, v.name = 'User1001')")挿入された値は、次のように確認できます。
g.queryPgql("SELECT id(n), n.name FROM MATCH(n) WHERE n.id = 1001").print().close()次のように出力されます。
+---------------------------+
| id(n)          | name     |
+---------------------------+
| Accounts(1001) | User1001 |
+---------------------------+親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.9 PGQL問合せを実行するためのセキュリティ・ツール
問合せインジェクションから保護するために、リテラルのかわりにバインド変数を使用し、グラフ名、ラベル、プロパティ名などの識別子のかわりにprintIdentifier(String identifier)を使用できます。
                  
- バインド変数の使用
 バインド変数を使用する理由は2つあります。
- 安全な方法での識別子の使用
 文字列の連結を介して問合せを作成すると、問合せのリテラルのみでなく、グラフ名、ラベル、プロパティ名などの識別子でもセキュリティ上のリスクが生じます。唯一の問題は、バインド変数がそのような識別子でサポートされていないことです。したがって、これらの識別子がアプリケーションの観点から可変である場合は、oracle.pgql.lang.ir.PgqlUtils.printIdentifier(String identifier)メソッドを介して識別子を渡すことにより、問合せインジェクションから保護することをお薦めします。
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.9.1 バインド変数の使用
バインド変数を使用する理由は2つあります。
- 問合せインジェクションから保護します。
- 問合せの再コンパイルを必要とせずに同じバインド変数を複数回設定できるため、問合せの実行が高速化されます。
プリペアド文を作成するには、次の2つの方法のいずれかを使用します。
- PgxGraph.preparePgql(String query) : PgxPreparedStatement
- PgxSession.preparePgql(String query) : PgxPreparedStatement
これらのメソッドから返されるPgxPreparedStatement (package oracle.pgx.api)には、バインド変数を指定されたデータ型の値にバインドするためのsetterメソッドがあります。
                     
PreparedStatement stmnt = g.preparePgql(
  "SELECT v.id, v.dob " +
  "FROM MATCH (v) " +
  "WHERE v.firstName = ? AND v.lastName = ?");
stmnt.setString(1, "Camille");
stmnt.setString(2, "Mullins");
ResultSet rs = stmnt.executeQuery();
問合せ内の各バインド変数は、PgxPreparedStatementの次のセッターのいずれかを使用して値に設定する必要があります。
                        
- setBoolean(int parameterIndex, boolean x)
- setDouble(int parameterIndex, double x)
- setFloat(int parameterIndex, float x)
- setInt(int parameterIndex, int x)
- setLong(int parameterIndex, long x)
- setDate(int parameterIndex, LocalDate x)
- setTime(int parameterIndex, LocalTime x)
- setTimestamp(int parameterIndex, LocalDateTime x)
- setTimeWithTimezone(int parameterIndex, OffsetTime x)
- setTimestampWithTimezone(int parameterIndex, OffsetDateTime x)
- setArray(int parameterIndex, List<?> x)
すべてのバインド変数が設定されると、文は次の方法で実行できます。
- PgxPreparedStatement.executeQuery()- SELECT問合せの場合のみ
- ResultSetを返します
 
- PgxPreparedStatement.execute()- あらゆるタイプの文の場合
- 結果の形式を示すブール値を返します。SELECT問合せの場合はtrue、それ以外の場合はfalseです。
- SELECTの場合、ResultSetには後で- PgxPreparedStatement.getResultSet()を使用してアクセスできます。
 
PGQLでは、配列リテラルを含む任意のデータ型のリテラルのかわりにバインド変数を使用できます。文字列配列のインスタンスにバインド変数が設定されている問合せの例は次のとおりです。
List<String> countryNames = new ArrayList<String>();
countryNames.add("Scotland");
countryNames.add("Tanzania");
countryNames.add("Serbia");
PreparedStatement stmnt = g.preparePgql(
  "SELECT n.name, n.population " +
  "FROM MATCH (c:Country) " +
  "WHERE c.name IN ?");
ResultSet rs = stmnt.executeQuery();
最後に、プリペアド文が不要になった場合は、PgxPreparedStatement.close()を介してクローズされ、リソースが解放されます。
                        
親トピック: PGQL問合せを実行するためのセキュリティ・ツール
18.9.2 安全な方法での識別子の使用
文字列の連結を介して問合せを作成すると、問合せのリテラルのみでなく、グラフ名、ラベル、プロパティ名などの識別子でもセキュリティ上のリスクが生じます。唯一の問題は、バインド変数がそのような識別子でサポートされていないことです。したがって、これらの識別子がアプリケーションの観点から可変である場合は、oracle.pgql.lang.ir.PgqlUtils.printIdentifier(String identifier)メソッドを介して識別子を渡すことにより、問合せインジェクションから保護することをお薦めします。
                     
識別子文字列を指定すると、メソッドは識別子の先頭と末尾に二重引用符を自動的に追加し、識別子内の文字を適切にエスケープします。
次のケースについて検討します。
String graphNamePrinted = printIdentifier("my graph name with \" special % characters ");
PreparedStatement stmnt = g.preparePgql(
  "SELECT COUNT(*) AS numVertices FROM MATCH (v) ON " + graphNamePrinted);親トピック: PGQL問合せを実行するためのセキュリティ・ツール
18.10 PGQL問合せをチューニングするためのベスト・プラクティス
このセクションでは、メモリー割当て、並列度および問合せ計画に関するベスト・プラクティスについて説明します。
- メモリー割当て
 グラフ・サーバー(PGX)にはon-heapおよびoff-heapメモリーがあり、前者は標準JVMヒープですが、後者はPGXによって管理される別のヒープです。グラフ・データと同様に、PGQL問合せの中間結果および最終結果は、一部がオンヒープおよび一部がオフヒープで格納されます。したがって、両方のヒープが必要です。
- 並列度
 デフォルトでは、使用可能なすべてのプロセッサ・スレッドがPGQL問合せの処理に使用されます。ただし、必要に応じてグラフ・サーバー(PGX)のparallelismオプションを設定することにより、スレッドの数を制限できます。
- 問合せ計画の説明PgxGraph.explainPgql(String query)メソッドは、問合せの問合せ計画を把握するために使用されます。このメソッドは、次のメソッドを持つOperation (package oracle.pgx.api)のインスタンスを返します。
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
18.10.1 メモリー割当て
グラフ・サーバー(PGX)にはon-heapおよびoff-heapメモリーがあり、前者は標準JVMヒープですが、後者はPGXによって管理される別のヒープです。グラフ・データと同様に、PGQL問合せの中間結果および最終結果は、一部がオンヒープおよび一部がオフヒープで格納されます。したがって、両方のヒープが必要です。
                     
オンヒープ・メモリーの場合、JVMの起動時にデフォルトの最大値が選択されますが、-Xmxオプションを介して上書きできます。 
                     
オフヒープの場合、デフォルトでは最大値は設定されず、オフヒープのメモリー使用量はシステム・リソースを使い果たすまで自動的に増加し続けます。システム・リソースがなくなると、操作が取り消され、メモリーが解放され、適切な例外がユーザーに渡されます。必要に応じて、グラフ・サーバー(PGX)のmax_off_heap_sizeオプションを介して最大オフヒープ・サイズを構成できます。
                     
可能なかぎり最大のグラフをロードして問合せを実行できるようにするための適切な開始点として、オンヒープ・とオフヒープの比率を1対1にすることをお薦めします。オンヒープ・メモリー・サイズを構成するステップは、「オンヒープ制限の構成」を参照してください。
18.10.2 パラレル化
デフォルトでは、使用可能なすべてのプロセッサ・スレッドがPGQL問合せの処理に使用されます。ただし、必要に応じてグラフ・サーバー(PGX)のparallelismオプションを設定することにより、スレッドの数を制限できます。
                     
グラフ・サーバーの構成パラメータの詳細は、グラフ・サーバー(PGX)エンジンの構成パラメータを参照してください。
18.10.3 問合せ計画の説明
PgxGraph.explainPgql(String query)メソッドは、問合せの問合せ計画を把握するために使用されます。このメソッドは、次のメソッドを持つOperation (package oracle.pgx.api)のインスタンスを返します。
                     
- print(): 操作とその子操作を出力するため
- getOperationType(): 操作のタイプを取得するため
- getPatternInfo(): 操作の文字列表現を取得するため
- getCostEstimate(): 操作のコストを取得するため
- getTotalCostEstimate(): 操作とその子操作のコストを取得するため
- getCardinatlityEstimate(): 予想される結果行数を取得するため
- getChildren(): 子操作にアクセスするため
次のケースについて検討します。
g.explainPgql("SELECT COUNT(*) FROM MATCH (n) -[e1]-> (m) -[e2]-> (o)").print()
\--- GROUP BY  GroupBy {"cardinality":"42", "cost":"42", "accumulatedCost":"58.1"}
     \--- (m) -[e2]-> (o) NeighborMatch {"cardinality":"3.12", "cost":"3.12", "accumulatedCost":"16.1"}
          \--- (n) -[e1]-> (m) NeighborMatch {"cardinality":"5", "cost":"5", "accumulatedCost":"13"}
               \--- (n) RootVertexMatch {"cardinality":"8", "cost":"8", "accumulatedCost":"8"}
前述の例では、print()メソッドを使用して問合せ計画を出力しています。
                     
問合せ計画が最適でない場合、パフォーマンスを向上させるために問合せを書き直せることがよくあります。たとえば、合計実行時間を改善する方法として、SELECT問合せをUPDATEおよびSELECT問合せに分割できます。
                     
グラフ・サーバー(PGX)はヒント・メカニズムを提供していないことに注意してください。
また、問合せ計画を出力すると、問合せで使用されているフィルタが表示されます。たとえば:
g.explainPgql("SELECT id(n) FROM MATCH (n)-[e]->(m) WHERE " +
...> "id(n) > 500 " +
...> "AND id(n) < 510 " +
...> "AND id(n) <> 509 " +
...> "AND id(n) <> 507 ").print()
\--- Projection {"cardinality":"146", "cost":"0", "accumulatedCost":"175"}
     \--- (n) -[e]-> (m) NeighborMatch {"cardinality":"146", "cost":"146", "accumulatedCost":"175"}
          \--- (n) RootVertexMatch {"cardinality":"29.2", "cost":"29.2", "accumulatedCost":"29.2"}
                WHERE $filter1
filter1: (id(n) <> 509) AND
         (id(n) <> 507) AND
         (id(n) > 500) AND
         (id(n) < 510)
前述の例では、問合せに3行を超えるフィルタがあるため、フィルタは問合せ計画の下に表示されます。フィルタが3行以下の場合、次のようにフィルタは問合せ計画ツリー内に直接表示されます。
g.explainPgql("SELECT id(n) FROM MATCH (n)-[e]->(m) WHERE " +
...> "id(n) > 500 " +
...> "AND id(n) < 510 ").print()
\--- Projection {"cardinality":"162", "cost":"0", "accumulatedCost":"194"}
     \--- (n) -[e]-> (m) NeighborMatch {"cardinality":"162", "cost":"162", "accumulatedCost":"194"}
          \--- (n) RootVertexMatch {"cardinality":"32.4", "cost":"32.4", "accumulatedCost":"32.4"}
                WHERE (id(n) > 500) AND
                      (id(n) < 510)