17 グラフ・サーバー(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 API
UPDATE
問合せは、PGQL 1.3仕様のグラフの変更のセクションで説明されているように、INSERT
、UPDATE
およびDELETE
操作を使用して既存のグラフに変更を加えます。 - パーティション化されたIDを含むPGQL問合せ
PGQLのid()関数を使用して、パーティション化されたIDを取得できます。 - PGQL問合せを実行するためのセキュリティ・ツール
問合せインジェクションから保護するために、リテラルのかわりにバインド変数を使用し、グラフ名、ラベル、プロパティ名などの識別子のかわりにprintIdentifier(String identifier)
を使用できます。 - PGQL問合せをチューニングするためのベスト・プラクティス
このセクションでは、メモリー割当て、並列度および問合せ計画に関するベスト・プラクティスについて説明します。
親トピック: グラフ・サーバー(PGX)の使用
17.1 PGQLスタート・ガイド
グラフ・サーバー(PGX)でPGQLを開始します。
このセクションでは、PGQLの開始方法の例を示します。事前に設定されているデータベース・レルムを想定しています(データベース認証のためのグラフ・サーバーの準備のステップに従ってください)。また、ユーザーがHRスキーマへのread
アクセス権を持っていることも想定しています。
最初に、CREATE PROPERTY GRAPH
文を実行して、従業員、部門およびemployee works at department
を含むグラフを作成します。
例17-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問合せの実行
17.2 オプションを使用したプロパティ・グラフの作成
グラフ最適化、および頂点が欠落しているエッジの処理の様々なオプションについて学習します。
CREATE PROPERTY GRAPH
文でOPTIONS句を使用すると、次の各項で説明するオプションを指定できます。
グラフ最適化オプションの使用
問合せや分析、または更新操作を実行するためのグラフをロードできます。要件に応じて、CREATE PROPERTY GRAPH
文のOPTIONS句を使用して、読取りまたは更新のパフォーマンスを最適化できます。
次の表に、OPTIONS
句でサポートされる有効なオプションを示します。
表17-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 5
IGNORE 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問合せの実行
17.3 グラフ・サーバー(PGX)でサポートされているPGQL機能および制限事項
グラフ・サーバー(PGX)でサポート対象のPGQL機能、およびサポート対象外のPGQL機能について学習します。
表17-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)を使用して操作または問合せできます。
|
演算子 | サポート対象:
|
関数および述語 | サポート対象:
|
ユーザー定義関数 | サポート対象:
|
副問合せ:
|
サポート対象
制限事項
|
INSERT/UPDATE/DELETE |
サポート対象 |
INTERVAL リテラルおよび操作
|
サポートされているリテラル:
サポートされている操作:
|
また、次では、特定のサポートされているPGQL機能とサポートされていないPGQL機能についても説明します:
- すべてのプロパティの選択のサポート
- 可変長パス問合せのネスト解除
- PGQL問合せでのINTERVALリテラルの使用
- PGQLでのパス・モードの使用
- PGQL Lateral副問合せのサポート
- PGQL GRAPH_TABLE副問合せのサポート
- 数量詞の制限
- 定量化されたパターンでのWHERE句およびCOST句の制限事項
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
17.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 |
+--------------------------------+
17.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 = 616
ONE 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 |
+---------------------------------------------------------------------+
17.3.3 PGQL問合せでのINTERVALリテラルの使用
PGQL問合せでINTERVAL
リテラルを使用すると、PGQL Temporalデータ型に対する間隔の加算または減算をそれぞれ実行できます。
INTERVAL
型は期間で、キーワード"INTERVAL
"とそれに続く数値および時間単位で構成されます。たとえば、INTERVAL '1' DAY
となります。
次の表に、INTERVAL
値でサポートされる有効な時間単位を示します。
表17-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 |
+--------------------------+
17.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
の後に配置されます。パス・モードの後には、PATH
またはPATHS
キーワードが続くこともあります。
すべての無限量数量詞に対してTRAIL
、ACYCLIC
またはSIMPLE
の一致パス・モードを使用すると、グラフ・パターン一致の結果セットが有限であることが保証されます。
17.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
17.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 |
+----------------------------------------+
17.3.7 数量詞の制限
到達可能性パターンと最短パス・パターンでは、*
、+
、{1,4}
などのすべての数量詞がサポートされていますが、最小コスト・パス・パターンでサポートされている数量詞は*
(ゼロ以上)のみです。
17.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) ) ...
17.4 CREATE PROPERTY GRAPH文を実行するためのJava API
CREATE PROPERTY GRAPH文を実行する最も簡単な方法は、PgxSession.executePgql(String statement)
メソッドを使用することです。
例17-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問合せの実行
17.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問合せの実行
17.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問合せの実行
17.6.1 グラフ・サーバー(PGX)のグラフに対するSELECT問合せの実行
PgxGraph.queryPgql(String query)
メソッドは、現在のセッションで問合せを実行します。このメソッドは、PgqlResultSet
を返します。
問合せはPGXグラフに対して直接実行されるため、MATCH
句内のON
句は省略できます。同じ理由で、INSERT
句内のINTO
句は省略できます。ただし、ON
句およびINTO
句でグラフ名を明示的に指定する場合、これらのグラフ名がグラフの実際の名前(PgxGraph.getName()
)と一致している必要があります。
親トピック: SELECT問合せを実行するためのJava API
17.6.2 PGXセッションに対するSELECT問合せの実行
PgxSession.queryPgql(String query)
メソッドは、セッションで指定された問合せを実行し、PgqlResultSet
を返します。
MATCH
句内のON
句、およびINSERT
句内のINTO
句は、指定する必要があり、省略できません。この時点では、単一の問合せで複数のグラフのデータを結合することはまだサポートされていないため、問合せのすべてのON
句およびINTO
句で同じグラフを参照する必要があります。
親トピック: SELECT問合せを実行するためのJava API
17.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: 1000
PgqlResultSet
で使用可能なものと同じgetterをPgxResult
でも使用できることに注意してください。
親トピック: SELECT問合せを実行するためのJava API
17.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
17.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問合せの実行
17.7.1 PGQLによるグラフの更新可能性
Oracle RDBMSまたはCSVファイルからPGXにロードされたグラフ・データは、PGQLを介してすぐに更新することはできません。
最初に、PgxGraph.clone()
メソッドを使用してデータのコピーを作成する必要があります。結果のグラフは完全に更新可能です。
次のケースについて検討します。
// load a graph from the RDBMS or from CSV
PgxGraph g1 = session.readGraphWithProperties("path/to/graph_config.json");
// 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
17.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
17.7.3 PGXセッションに対するUPDATE問合せの実行
現時点では、PgxSession
に対するUPDATE
問合せの実行はサポートされていないため、更新は常にPgxGraphに対して実行する必要があります。セッションからグラフを取得するには、PgxSession.getGraph(String graphName)
メソッドを使用します。
親トピック: UPDATE問合せを実行するためのJava API
17.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.readGraphWithProperties("path/to/graph_config.json");
// 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
17.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問合せの実行
17.9 PGQL問合せを実行するためのセキュリティ・ツール
問合せインジェクションから保護するために、リテラルのかわりにバインド変数を使用し、グラフ名、ラベル、プロパティ名などの識別子のかわりにprintIdentifier(String identifier)
を使用できます。
- バインド変数の使用
バインド変数を使用する理由は2つあります。 - 安全な方法での識別子の使用
文字列の連結を介して問合せを作成すると、問合せのリテラルのみでなく、グラフ名、ラベル、プロパティ名などの識別子でもセキュリティ上のリスクが生じます。唯一の問題は、バインド変数がそのような識別子でサポートされていないことです。したがって、これらの識別子がアプリケーションの観点から可変である場合は、oracle.pgql.lang.ir.PgqlUtils.printIdentifier(String identifier)
メソッドを介して識別子を渡すことにより、問合せインジェクションから保護することをお薦めします。
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
17.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問合せを実行するためのセキュリティ・ツール
17.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問合せを実行するためのセキュリティ・ツール
17.10 PGQL問合せをチューニングするためのベスト・プラクティス
このセクションでは、メモリー割当て、並列度および問合せ計画に関するベスト・プラクティスについて説明します。
- メモリー割当て
グラフ・サーバー(PGX)にはon-heap
およびoff-heap
メモリーがあり、前者は標準JVMヒープですが、後者はPGXによって管理される別のヒープです。グラフ・データと同様に、PGQL問合せの中間結果および最終結果は、一部がオンヒープおよび一部がオフヒープで格納されます。したがって、両方のヒープが必要です。 - 並列度
デフォルトでは、使用可能なすべてのプロセッサ・スレッドがPGQL問合せの処理に使用されます。ただし、必要に応じてグラフ・サーバー(PGX)のparallelism
オプションを設定することにより、スレッドの数を制限できます。 - 問合せ計画の説明
PgxGraph.explainPgql(String query)
メソッドは、問合せの問合せ計画を把握するために使用されます。このメソッドは、次のメソッドを持つOperation (package oracle.pgx.api)
のインスタンスを返します。
親トピック: グラフ・サーバー(PGX)に対するPGQL問合せの実行
17.10.1 メモリー割当て
グラフ・サーバー(PGX)にはon-heap
およびoff-heap
メモリーがあり、前者は標準JVMヒープですが、後者はPGXによって管理される別のヒープです。グラフ・データと同様に、PGQL問合せの中間結果および最終結果は、一部がオンヒープおよび一部がオフヒープで格納されます。したがって、両方のヒープが必要です。
オンヒープ・メモリーの場合、JVMの起動時にデフォルトの最大値が選択されますが、-Xmx
オプションを介して上書きできます。
オフヒープの場合、デフォルトでは最大値は設定されず、オフヒープのメモリー使用量はシステム・リソースを使い果たすまで自動的に増加し続けます。システム・リソースがなくなると、操作が取り消され、メモリーが解放され、適切な例外がユーザーに渡されます。必要に応じて、グラフ・サーバー(PGX)のmax_off_heap_size
オプションを介して最大オフヒープ・サイズを構成できます。
可能なかぎり最大のグラフをロードして問合せを実行できるようにするための適切な開始点として、オンヒープ・とオフヒープの比率を1対1にすることをお薦めします。オンヒープ・メモリー・サイズを構成するステップは、「オンヒープ制限の構成」を参照してください。
17.10.2 パラレル化
デフォルトでは、使用可能なすべてのプロセッサ・スレッドがPGQL問合せの処理に使用されます。ただし、必要に応じてグラフ・サーバー(PGX)のparallelism
オプションを設定することにより、スレッドの数を制限できます。
グラフ・サーバーの構成パラメータの詳細は、グラフ・サーバー(PGX)エンジンの構成パラメータを参照してください。
17.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)