12 プロパティ・グラフ問合せ言語(PGQL)

PGQLは、頂点からなり、エッジにより別の頂点に接続され、それぞれが関連付けられたキー値ペア(プロパティ)を持つことができる、プロパティ・グラフ・データ構造のためのSQLライクな問合せ言語です。

この言語はグラフ・パターン一致の概念に基づき、データ・グラフの頂点およびエッジに対し一致するパターンを指定することができます。

ノート:

グラフ・サーバー(PGX) 23.2.0では、PGQL 1.5以前のバージョンがサポートされます。

プロパティ・グラフ・サポートでは、Java APIを介してプロパティ・グラフ問合せ言語(PGQL)の問合せを実行するための2つの方法が提供されています。

PGQLの詳細は、PGQL Specificationを参照してください。

12.1 PGQLを使用したプロパティ・グラフの作成

CREATE PROPERTY GRAPHは、データベース表からプロパティ・グラフ・ビュー(PGビュー)を作成するPGQL DDL文です。

CREATE PROPERTY GRAPH文は、グラフに付ける名前で始まり、その後に頂点表とエッジ表のセットが続きます。グラフには、頂点表またはエッジ表がない(空のグラフ)か、頂点表がありエッジ表はない(頂点のみでエッジなしのグラフ)か、頂点表とエッジ表の両方がある(頂点とエッジのあるグラフ)場合があります。ただし、エッジ表のみで頂点表のないグラフは指定できません。

opt/oracle/graph/dataディレクトリのサンプル・グラフ・データを使用して作成されたbank_accountsおよびbank_txnsデータベース表について考えてみます。詳細は、「CSVファイルからのデータのインポート」を参照してください。

  • BANK_ACCOUNTSは、列idnameが含まれる表です。新しい口座ごとに、この表に行が追加されます。
  • BANK_TXNSは、列txn_idfrom_acct_idto_acct_iddescriptionおよびamountが含まれる表です。from_acct_idからto_acct_idへの新しいトランザクションごとに、この表に行が追加されます。

次のようにデータベース表を使用してPGビューを作成できます:

CREATE PROPERTY GRAPH bank_graph
     VERTEX TABLES(
       bank_accounts AS accounts
         KEY(id)
         LABEL accounts
         PROPERTIES (id, name)
     )
     EDGE TABLES(
       bank_txns AS transfers
         KEY (txn_id)
         SOURCE KEY (from_acct_id) REFERENCES accounts (id)
         DESTINATION KEY (to_acct_id) REFERENCES accounts (id)
         PROPERTIES (description, amount)
     ) OPTIONS (PG_VIEW)

次のグラフの概念については、データベース表をグラフにマップし、前述のPGQL DDL文を使用して説明します:

  • 頂点表: データ・エンティティが含まれる表は頂点表です(bank_accountsなど)。
    • 頂点表の各行は頂点です。
    • 頂点表の列は、頂点のプロパティです。
    • 頂点表の名前は、この頂点セットのデフォルト・ラベルです。あるいは、CREATE PROPERTY GRAPH文の一部としてラベル名を指定できます。
  • エッジ表: エッジ表は、2つの頂点表をリンクする任意の表、またはソース・エンティティからターゲット・エンティティへのアクションを示すデータを含む表です。たとえば、FROM_ACCOUNT_IDからTO_ACCOUNT_IDへの送金は自然なエッジです。
    • 外部キー関係により、データ内で関連しているリンクを知ることができます。CREATE PROPERTY GRAPHは、デフォルトで外部キー関係を使用してエッジを識別します。
    • エッジ表のプロパティの一部は、エッジのプロパティにできます。たとえば、from_acct_idからto_acct_idへのエッジには、プロパティdescriptionおよびamountを指定できます。
    • エッジ表の名前は、エッジ・セットのデフォルト・ラベルです。あるいは、CREATE PROPERTY GRAPH文の一部としてラベル名を指定できます。
  • キー:
    • 頂点表のキー: 頂点表のキーは、グラフ内の一意の頂点を識別します。キーはCREATE PROPERTY GRAPH文で指定できます。指定しない場合、デフォルトで表の主キーになります。表に重複した行がある場合は、CREATE PROPERTY GRAPH文によってエラーが返されます。
    • エッジ表のキー: エッジ表のキーは、グラフ内のエッジを一意に識別します。ソースと宛先の頂点を指定すると、KEY句はソースと宛先の頂点キーを一意に識別します。
  • 表の別名: 頂点表とエッジ表には一意の名前が付いている必要があります。同じリレーショナル表から複数の頂点表を識別したり、同じリレーショナル表から複数のエッジ表を識別する必要がある場合は、別名を使用する必要があります。たとえば、次のように、1つの表bank_accountsから2つの頂点表bank_accountsおよびaccountsを作成できます:
    CREATE PROPERTY GRAPH bank_transfers
         VERTEX TABLES (bank_accounts KEY(id)
                        bank_accounts AS accounts KEY(id))
    

    頂点表とエッジ表のいずれかが同じ名前を共有する場合は、表の別名を再度使用する必要があります。次の例では、同じ名前で参照されている頂点表があるため、エッジ表DEPARTMENTSに表の別名が使用されます。

    CREATE PROPERTY GRAPH hr
    VERTEX TABLES (
      employees KEY(employee_id)
        PROPERTIES ARE ALL COLUMNS,
      departments KEY(department_id)
        PROPERTIES ARE ALL COLUMNS
     )
    EDGE TABLES (   
      departments AS managed_by
        SOURCE KEY ( department_id ) REFERENCES departments ( department_id )
        DESTINATION employees
        PROPERTIES ARE ALL COLUMNS
     )OPTIONS (PG_VIEW)
  • プロパティ: グラフの頂点プロパティおよびエッジ・プロパティはそれぞれ、頂点表およびエッジ表の列から導出され、デフォルトでは基礎となる表の列と同じ名前になります。ただし、列ごとに異なるプロパティ名を選択できます。これは、2つの表の列名が同じでデータ型が異なる場合に競合を回避するのに役立ちます。

    次の例では、頂点プロパティidおよびnameの名前がそれぞれacct_noおよびacct_nameに変更されます。

    CREATE PROPERTY GRAPH bank_transfers
    VERTEX TABLES (
      bank_accounts AS accounts
        LABEL accounts  
        PROPERTIES (id AS acct_no, name AS acct_name)
    )
  • REFERENCES句: エッジのソースおよび宛先の頂点を、対応する頂点表に接続します。

CREATE PROPERTY GRAPH文の詳細は、PGQL Specificationを参照してください。

プロパティ・グラフの作成方法は、次の表を参照してください。

表12-1 CREATE PROPERTY GRAPH文のサポート

メソッド 詳細情報
oracle.pgx.api Javaパッケージを使用して、グラフ・サーバー(PGX)にプロパティ・グラフを作成する CREATE PROPERTY GRAPH文を実行するためのJava API
pypgx.api Pythonパッケージを使用して、グラフ・サーバー(PGX)にプロパティ・グラフを作成する CREATE PROPERTY GRAPH文を実行するためのPython API
Oracle Database表でプロパティ・グラフ・ビューを作成する プロパティ・グラフ・ビューの作成

12.2 PGQLによるパターン一致

パターン一致は、MATCH句で1つ以上のパス・パターンを指定することで行われます。単一パス・パターンは頂点およびエッジの線形パスと一致しますが、より複雑なパターンは、複数のパス・パターンをカンマで区切って組み合せることによって一致させることができます。(対応するSQL文に類似した)値式はWHERE句で指定され、通常、頂点およびエッジのプロパティに制約を指定することによって一致をフィルタ処理で除外できます

たとえば、コンピュータ・ネットワーク上のTCP/IP接続のグラフを想定し、誰かが1つのマシンにログインし、そこから別のマシンへ、そこからさらに別のマシンへという場合を検出するとします。次のようなパターンを問合せます。

SELECT id(host1) AS id1, id(host2) AS id2, id(host3) AS id3         /* choose what to return */
FROM MATCH
    (host1) -[connection1]-> (host2) -[connection2]-> (host3)       /* single linear path pattern to match */
WHERE
    connection1.toPort = 22 AND connection1.opened = true AND
    connection2.toPort = 22 AND connection2.opened = true AND
    connection1.bytes > 300 AND                                     /* meaningful amount of data was exchanged */
    connection2.bytes > 300 AND
    connection1.start < connection2.start AND                       /* second connection within time-frame of first */
    connection2.start + connection2.duration < connection1.start + connection1.duration
GROUP BY id1, id2, id3                                              /* aggregate multiple matching connections */ 

パターン一致のその他の例については、PGQL仕様のWriting simple queriesを参照してください。

12.3 エッジ・パターンはPGQLによって方向を持つ

エッジ・パターンは、グラフのエッジと同様に方向を持ちます。そのため、(a) <-[]- (b)は、bにはaに向けられたエッジがある場合を指定するのに対し、(a) -[]-> (b)は、逆の方向のエッジを探します。

次の例は、AprilとChrisの共通の友人で両者よりも年上の人を探します。

SELECT friend.name, friend.dob
FROM MATCH                  /* note the arrow directions below */
  (p1:person) -[:likes]-> (friend) <-[:likes]- (p2:person)
WHERE
  p1.name = 'April' AND p2.name ='Chris' AND
  friend.dob > p1.dob AND friend.dob > p2.dob
ORDER BY friend.dob DESC 

エッジ・パターンのその他の例については、PGQL仕様のEdge Patternsを参照してください。

12.4 PGQLによる頂点およびエッジ・ラベル

ラベルは、グラフのエッジおよびノードにタイプ情報を添付する方法で、すべてのノードが同じものを表しているわけではないグラフの制約に使用することができます。たとえば:

SELECT p.name
FROM MATCH (p:person) -[e1:likes]-> (m1:movie),
     MATCH (p) -[e2:likes]-> (m2:movie)
WHERE m1.title = 'Star Wars'
  AND m2.title = 'Avatar'

この例では、ラベルpersonの頂点セット、ラベルmovieの頂点セットおよびラベルlikesのエッジ・セットが含まれるグラフを問い合せます。ラベル述語は、コロン(:)またはキーワードISのいずれかで始め、その後に1つ以上のラベルを続けることができます。複数のラベルを使用する場合、ラベルは縦棒(|)で区切られます。

次の問合せは、ラベル述語にキーワードISを使用した前述の問合せの例を示しています。

SELECT p.name
FROM MATCH (p IS person) -[e1 IS likes]-> (m1 IS movie),
     MATCH (p IS person) -[e2 IS likes]-> (m2 IS movie)
WHERE m1.title = 'Star Wars'
  AND m2.title = 'Avatar'

関連項目:

12.5 PGQLによる可変長のパス

可変長パス・パターンは、頂点およびエッジの変数と一致するように*のような数量詞を持ちます。PATHマクロを使用すると、名前を参照することにより、MATCH句に任意の回数だけ埋め込むことのできる問合せの開始時に、名前付きパス・パターンを指定できます。次の例は、MarioとLuigiの共通の祖先をすべて検索します。

PATH has_parent AS () -[:has_father|has_mother]-> ()
SELECT ancestor.name
FROM MATCH (p1:Person) -/:has_parent*/-> (ancestor:Person)
   , MATCH (p2:Person) -/:has_parent*/-> (ancestor)
WHERE
  p1.name = 'Mario' AND
  p2.name = 'Luigi'

追加の制約または問合せ結果では使用されない、中間エッジまたはノードの名前は定義する必要がないため、前のパス仕様では、匿名制約の使用も示しています。匿名要素は、[:has_father|has_mother]などの制約を持つことができます。エッジは変数名を取得できません(他の場所で参照されないため)が、制約はされます。

可変長パス・パターン一致のその他の例については、PGQL仕様のVariable-Length Pathsを参照してください。

12.6 PGQLによる集計およびソート

SQLのように、PGQLは次のサポートがあります。

  • ソリューションのグループを作成するGROUP BY

  • MIN、MAX、SUMおよびAVG集計

  • 結果をソートするORDER BY

および、その他の多くのなじみのあるSQL構造がサポートされます。

関連項目:

12.7 プロパティ・グラフ・ビューに対するPGQL問合せの実行

このトピックでは、Oracle Database表のプロパティ・グラフ・ビューに対して直接PGQL問合せを実行する方法について説明します。

PGQL問合せの実行フローを次の図に示します。

図12-1 Oracle Databaseのプロパティ・グラフ・ビューでのPGQL

図12-1の説明が続きます。
「図12-1 Oracle Databaseのプロパティ・グラフ・ビューでのPGQL」の説明

基本的な実行フローは次のとおりです。

  1. PGQL問合せはJava APIを介してRDBMS上のPGQLに送信されます。
  2. プロパティ・グラフ・ビューの内部メタデータ表を使用してPGQL問合せがSQL文に変換されます。
  3. 変換されたSQLは、JDBCによってOracle Databaseに送信されます。
  4. SQL結果セットは、PGQL結果セットとしてラップされてコール元に返されます。

12.7.1 PGビューでサポートされているPGQL機能および制限事項

プロパティ・グラフ・ビュー(PGビュー)でサポートされているPGQL機能および制限事項について学習します。

次の表では、PGビューでサポートされているPGQL機能およびサポートされていないPGQL機能の完全なリストについて説明します:

表12-2 PGビューでサポートされているPGQL機能および制限事項

機能 PGビュー上のPGQL
CREATE PROPERTY GRAPH サポート対象
DROP PROPERTY GRAPH サポート対象
固定長パターン一致 サポート対象
可変長パターン一致の目標 サポート対象:
  • 到達可能性
  • パス検索接頭辞:
    • ANY
    • ANY SHORTEST
    • SHORTEST k
    • ALL
  • パス・モード:
    • WALK

制限事項:

  • パス検索接頭辞:
    • ALL SHORTEST
    • ANY CHEAPEST
    • CHEAPEST k
  • パス・モード:
    • TRAIL
    • SIMPLE
    • ACYCLIC
可変長パターン一致の数量詞 サポート対象:
  • *
  • +
  • ?
  • { n }
  • { n, }
  • { n, m }
  • { , m }
可変長パスのネスト解除 サポート対象外
GROUP BY サポート対象
HAVING サポート対象
集計 サポート対象:
  • COUNT
  • MINMAXAVGSUM
  • LISTAGG

制限事項:

  • ARRAY_AGG
DISTINCT
  • SELECT DISTINCT
  • DISTINCTを使用した集計(COUNT(DISTINCT e.prop)など)
サポート対象
SELECT v.* サポート対象
ORDER BY (+ASC/DESC)、LIMIT、OFFSET サポート対象
データ型 使用可能なすべてのOracle RDBMSデータ型がサポートされています
JSON サポート対象:
  • JSONストレージ:
    • JSON文字列(VARCHAR2)
    • JSONオブジェクト
  • JSON関数:

    構文json_function_name(arg1, arg2,...)に従うJSON関数コール。たとえば:

    json_value(department_data, '$.department')

制限事項:
  • シンプルなドット表記法
  • JSON関数コールのオプション句(RETURNINGERRORなど)はサポートされません。たとえば:

    json_value(department_data, '$.employees[1].hireDate' RETURNING DATE)

演算子 サポート対象:
  • 関係: +-*/%- (単項マイナス)
  • 算術: =<><><=>=
  • 論理: ANDORNOT
  • 文字列: || (concat)
関数および述語

サポート対象は、オプションのスキーマおよびパッケージ修飾子が指定されたfunction_name(arg1, arg2, ...)という形式をとる、Oracle RDBMSで使用可能なすべての関数です。

サポートされているPGQL関数/述語:

  • IS NULLIS NOT NULL
  • JAVA_REGEXP_LIKE (CONTAINSに基づいています)
  • LOWERUPPER
  • SUBSTRING
  • ABSCEIL/CEILINGFLOORROUND
  • EXTRACT
  • ID, VERTEX_ID, EDGE_ID
  • LABELIS [NOT] LABELED
  • ALL_DIFFERENT
  • CAST
  • CASE
  • INNOT IN
  • IS [NOT] SOURCE [OF]IS [NOT] DESTINATION [OF]
  • VERTEX_EQUALEDGE_EQUAL

制限事項:

  • LABELS
  • IN_DEGREEOUT_DEGREE
ユーザー定義関数 サポート対象:
  • PL/SQL関数
  • Oracle Database Multilingual Engine (MLE)を介して作成された関数
副問合せ:
  • スカラー副問合せ
  • EXISTSおよびNOT EXISTS副問合せ
  • LATERAL副問合せ
  • GRAPH_TABLE副問合せ
サポート対象:
  • EXISTSおよびNOT EXISTS副問合せ
  • スカラー副問合せ

制限事項:

  • LATERAL副問合せ
  • GRAPH_TABLE副問合せ
INSERT/UPDATE/DELETE サポート対象
INTERVALリテラルおよび操作 サポート対象外
12.7.1.1 例でサポートされているPGQL機能に関する追加情報

プロパティ・グラフ・ビュー(PGビュー)では、次のPGQL機能がサポートされています:

  • 再帰的問合せは、次の可変長パス検索の目標でサポートされています。
    • 到達可能性
    • ANY
    • ANY SHORTEST
    • TOP k SHORTEST
  • 再帰的問合せは、次の水平集計でサポートされています。
    • LISTAGG
      SELECT LISTAGG(src.first_name || ' ' || src.last_name, ',')
      FROM MATCH TOP 2 SHORTEST ( (n:Person) ((src)-[e:knows]->)* (m:Person) )
      WHERE n.id = 1234
    • SUM
      SELECT SUM(e.weight + 3)
      FROM MATCH TOP 2 SHORTEST ( (n:Person) -[e:knows]->* (m:Person) )
      WHERE n.id = 1234
    • COUNT
      SELECT COUNT(e)
      FROM MATCH TOP 2 SHORTEST ( (n:Person) -[e:knows]->* (m:Person) )
      WHERE n.id = 1234
    • AVG
      SELECT AVG(dst.age)
      FROM MATCH TOP 2 SHORTEST ( (n:Person) (-[e:knows]->(dst))* (m:Person) )
      WHERE n.id = 1234
    • MIN (プロパティ値またはCAST式の場合のみ)
      SELECT MIN(CAST(dst.age + 5 AS INTEGER))
      FROM MATCH TOP 2 SHORTEST ( (n:Person) (-[e:knows]->(dst))* (m:Person) )
      WHERE n.id = 1234
    • MAX (プロパティ値またはCAST式の場合のみ)
      SELECT MAX(dst.birthday)
      FROM MATCH TOP 2 SHORTEST ( (n:Person) (-[e:knows]->(dst))* (m:Person) )
      WHERE n.id = 1234
  • 再帰的問合せでは、次の数量詞がサポートされています。

    表12-3 PGQL SELECT問合せでサポートされる数量詞

    構文 説明
    * 0 (ゼロ)以上
    + 1以上
    ? 0 (ゼロ)または1
    {n} nに等しい
    {n,} n以上
    {n,m} nmの間(両端を含む)
    {,m} 0からmの間(両端を含む)
  • 精度とスケールによるデータ型キャストがサポートされています。
    SELECT CAST(v.id AS VARCHAR2(10)) || '→' || CAST(w.id AS VARCHAR2(10)) AS friendOf
    FROM MATCH (v) -[:friendOf]->(w)
    SELECT CAST(e.mval AS NUMBER(5,2)) AS mval
    FROM MATCH () -[e:knows]->()
    WHERE e.mval = '342.5'
  • Oracle Databaseの組込み関数とユーザー定義関数(UDF)が両方ともサポートされています。

    たとえば:

    • 表に、{"name":"John", "age": 43}などの値が含まれているJSON列があると仮定します。
      SELECT JSON_VALUE(p.attributes, '$.name') AS name
      FROM MATCH (p:Person)
      WHERE JSON_VALUE(p.attributes, '$.age') > 35
    • Oracle Text索引が表内のテキスト列に存在すると仮定します。
      SELECT n.text
      FROM MATCH (n)
      WHERE CONTAINS(n.text, 'cat', 1) > 0
    • UDFのupdated_idがグラフ・サーバー(PGX)に登録されていると仮定します。
      SELECT my.updated_id(n.ID) FROM MATCH(n) LIMIT 10
  • SELECT v.*句では、頂点またはエッジのすべてのプロパティの選択がサポートされています。vは、プロパティが選択されている変数です。次の例では、グラフのすべてのエッジ・プロパティを取得します。
    SELECT label(e), e.* FROM MATCH (n)-[e]->(m) ON bank_graph_view LIMIT 3

    実行時に、前述の問合せが、次のように変数eにバインドされているすべてのプロパティを取得します。

    +--------------------------------------------------------------+
    | label(e)  | AMOUNT | DESCRIPTION | FROM_ACCT_ID | TO_ACCT_ID |
    +--------------------------------------------------------------+
    | TRANSFERS | 1000   | transfer    | 178          | 921        |
    | TRANSFERS | 1000   | transfer    | 178          | 462        |
    | TRANSFERS | 1000   | transfer    | 179          | 688        |
    +--------------------------------------------------------------+
    

    複数の変数を使用してすべてのプロパティを選択した場合に、列名が重複しないようにPREFIXを指定できます。たとえば:

    SELECT n.* PREFIX 'n_', e.* PREFIX 'e_', m.* PREFIX 'm_' 
    FROM MATCH (n:Accounts) -[e:transfers]-> (m:Accounts) 
    ON bank_graph_view LIMIT 3

    問合せ出力は次のようになります。

    +--------------------------------------------------------------------------------------------+
    | n_ID | n_NAME  | e_AMOUNT | e_DESCRIPTION | e_FROM_ACCT_ID | e_TO_ACCT_ID | m_ID | m_NAME  |
    +--------------------------------------------------------------------------------------------+
    | 178  | Account | 1000     | transfer      | 178            | 921          | 921  | Account |
    | 178  | Account | 1000     | transfer      | 178            | 462          | 462  | Account |
    | 179  | Account | 1000     | transfer      | 179            | 688          | 688  | Account |
    +--------------------------------------------------------------------------------------------+
    

    ラベル式を使用すると、指定した頂点またはエッジ・ラベルに属するプロパティのみを選択できます。

    SELECT LABEL(n), n.* FROM MATCH (n:Accounts) ON bank_graph_view LIMIT 3

    前述の問合せ出力は次のようになります。

    +-----------------------+
    | LABEL(n) | ID | NAME  |
    +-----------------------+
    | ACCOUNTS | 1  | User1 |
    | ACCOUNTS | 2  | User2 |
    | ACCOUNTS | 3  | User3 |
    +-----------------------+
    
  • 頂点ペア間のすべてのパスを戻すALLパス検索の目標がサポートされています。ただし、無限サイクルを回避するために、次の数量詞のみがサポートされています。
    • ?
    • {n}
    • {n.m}
    • {,m}

    たとえば、次のPGQL問合せでは、アカウント284からアカウント616へのすべてのトランザクション・パスを検索します。

    SELECT LISTAGG(e.amount, ' + ') || ' = ', SUM(e.amount) AS total_amount
    FROM MATCH ALL (a:Accounts) -[e:Transfers]->{1,4}(b:Accounts)
    WHERE a.id = 284 AND b.id = 616
    ORDER BY total_amount

    実行すると、問合せによって次の結果が生成されます。

    +--------------------------------------------------+
    | LISTAGG(e.amount, ' + ') || ' = ' | TOTAL_AMOUNT |
    +--------------------------------------------------+
    | 1000 + 1000 + 1000 =              | 3000         |
    | 1000 + 1500 + 1000 =              | 3500         |
    | 1000 + 1000 + 1000 + 1000 =       | 4000         |
    +--------------------------------------------------+
    $16 ==> oracle.pg.rdbms.pgql.pgview.PgViewResultSet@4f38acf
    
  • 1列1行のみを戻すスカラー副問合せがサポートされています。

    たとえば:

    SELECT p.name AS name , (
      SELECT SUM(t.amount)      
      FROM MATCH (a) <-[t:transaction]- (:Account)  
    ) AS sum_incoming , (
      SELECT SUM(t.amount)      
      FROM MATCH (a) -[t:transaction]-> (:Account)  
    ) AS sum_outgoing , (
      SELECT COUNT(DISTINCT p2)      
      FROM MATCH (a) -[t:transaction]- (:Account) -[:owner]-> (p2:Person)    
      WHERE p2 <> p  
    ) AS num_persons_transacted_with , (
      SELECT COUNT(DISTINCT c)      
      FROM MATCH (a) -[t:transaction]- (:Account) -[:owner]-> (c:Company)  
    ) AS num_companies_transacted_with   
    FROM MATCH (p:Person) <-[:owner]- (a:Account)
    ORDER BY sum_outgoing + sum_incoming DESC
  • EXISTSおよびNOT EXISTS副問合せがサポートされています。このような問合せでは、外部問合せのバインディングが指定された場合に問合せで1つ以上の結果が生成されるかどうかに応じて、TRUEまたはFALSEになります。

    たとえば:

    SELECT fof.name, COUNT(friend) AS num_common_friends 
    FROM MATCH (p:Person) -[:knows]-> (friend:Person) -[:knows]-> (fof:Person)
    WHERE NOT EXISTS (   
      SELECT * FROM MATCH (p) -[:knows]-> (fof) 
    )
サポートされていないPGQL機能をいくつか次に示します。
  • PGQLの次のSELECT機能はサポートされていません。
    • パス式でのバインド変数の使用。

      バインド変数を使用しようとすると、次のようなエラーになります。

      opg4j> String s = "SELECT id(a) FROM MATCH ANY SHORTEST (a) -[e]->* (b) WHERE id(a) = ?";
      s ==> "SELECT id(a) FROM MATCH ANY SHORTEST (a) -[e]->* (b) WHERE id(a) = ?"
       
      opg4j> PgqlPreparedStatement ps = pgqlConn.prepareStatement(s);
      ps ==> oracle.pg.rdbms.pgql.PgqlExecution@7806db3f
       
      opg4j> ps.setString(1, "PERSON(3)");
       
      opg4j> ps.executeQuery();
      |  Exception java.lang.UnsupportedOperationException: Use of bind variables for path queries is not supported
    • in_degree関数とout_degree関数

ノート:

12.7.2 PGQL問合せのSQL変換

PgqlStatementおよびPgqlPreparedStatementtranslateQuery()メソッドとgetSqlTranslation()メソッドを介してPGQL問合せのSQL変換を取得できます。

PGQL問合せに未加工のSQLを使用すると、次のことができます。

  • 他のSQLベースのツールまたはインタフェース(たとえば、SQL*PlusやSQL Developer)を使用してデータベースに対してSQLを直接実行する。
  • 生成されたSQLをカスタマイズおよびチューニングして、パフォーマンスを最適化し、アプリケーションの特定要件を達成する。
  • PGQL副問合せを、Oracle Databaseに格納されている他のデータ(リレーショナル表、空間データ、およびJSONデータなど)と結合する大規模なSQL問合せを作成する。

PGQL問合せの変換および実行に影響を与える複数のオプションがあります。問合せオプションを設定する主な方法は次のとおりです。

  • executeQuerytranslateQuery、およびPgqlConnection.prepareStatementメソッドに対して明示的引数を使用する
  • executeQueryおよびtranslateQueryoptions文字列引数でフラグを使用する
  • Java JVM引数を使用する

次の表に、PGQLの変換と実行に使用できる問合せ引数をまとめています。

表12-4 PGQLの変換および実行のオプション

オプション デフォルト 明示的引数 オプション・フラグ JVM引数
並列度

0

parallel

なし

なし

タイムアウト

無制限

timeout

なし

なし

動的サンプリング

2

dynamicSampling

なし

なし

結果の最大数

無制限

maxResults

なし

なし

逆パス最適化 True なし REVERSE_PATH=F oracle.pg.rdbms.pgql.reversePath=false
ソース・フィルタ最適化のプッシュ True なし PUSH_SRC_HOPS=F oracle.pg.rdbms.pgql.pushSrcHops=false
宛先フィルタ最適化のプッシュ False なし PUSH_DST_HOPS=T oracle.pg.rdbms.pgql.pushDstHops=true
最短パス変換でのビューの作成 False なし SP_CREATE_VIEW=T oracle.pg.rdbms.pgql.spCreateView=true
最短パス変換での表の作成 True なし SP_CREATE_TABLE=F oracle.pg.rdbms.pgql.spCreateTable=false

12.7.3 PGQL問合せでのパフォーマンスの考慮事項

次の項では、問合せのパフォーマンスに関するいくつかの推奨プラクティスについて説明します。

12.7.3.1 再帰的問合せ

再帰的問合せの実行を高速化するために、次の索引をお薦めします。

  • 再帰パターンの基礎となるVERTEX表の場合、キー列の索引
  • 再帰パターンの基礎となるEDGE表の場合、ソース・キー列の索引

    ノート:

    (ソース・キー, 宛先キー)に索引を作成することもできます。

たとえば、次のCREATE PROPERTY GRAPH文について考えてみます。

CREATE PROPERTY GRAPH people
  VERTEX TABLES(
    person
      KEY ( id )
      LABEL person
      PROPERTIES( name, age )
  )
  EDGE TABLES(
    knows
      key (person1, person2)
      SOURCE KEY ( person1 ) REFERENCES person (id)
      DESTINATION KEY ( person2 ) REFERENCES person (id)
      NO PROPERTIES
  )
  OPTIONS ( PG_VIEW )

また、次の問合せも考えてみます。

SELECT COUNT(*)
FROM MATCH ANY SHORTEST ( (n:Person) -[e:knows]->* (m:Person) )
WHERE n.id = 1234

前述の問合せの再帰的部分のパフォーマンスを向上させるには、次の索引が存在する必要があります。

  • CREATE INDEX <INDEX_NAME> ON PERSON(ID)
  • CREATE INDEX <INDEX_NAME> ON KNOWS(PERSON1) または

    CREATE INDEX <INDEX_NAME> ON KNOWS(PERSON1, PERSON2)

コンポジット頂点キー

コンポジット頂点キーの場合、キー列にファンクション索引を作成することで、問合せの実行を最適化できます。

  • 再帰パターンの基礎となるVERTEX表の場合、カンマ区切りのキー列の連結に対するファンクション索引
  • 再帰パターンの基礎となるEDGE表の場合、カンマ区切りのソース・キー列の連結に対するファンクション索引

    ノート:

    (ソース・キー列, 宛先キー列)に索引を作成することもできます。

たとえば、次のCREATE PROPERTY GRAPH文について考えてみます。

CREATE PROPERTY GRAPH people
  VERTEX TABLES(
    person
      KEY ( id1, id2 )
      LABEL person
      PROPERTIES( name, age )
  )
  EDGE TABLES(
    knows
      key (id)
      SOURCE KEY ( id1person1, id2person1 ) REFERENCES person (id1,id2)
      DESTINATION KEY ( id1person2, id2person2 ) REFERENCES person (id1,id2)
      NO PROPERTIES
  )
  OPTIONS ( PG_VIEW )

また、次の問合せも考えてみます。

SELECT COUNT(*)
FROM MATCH ANY SHORTEST ( (n:Person) -[e:knows]->* (m:Person) )
WHERE n.id = 1234

前述の問合せの再帰的部分のパフォーマンスを向上させるには、次の索引が存在する必要があります。

  • CREATE INDEX <INDEX_NAME> ON PERSON (ID1 || ',' || ID2)
  • CREATE INDEX <INDEX_NAME> ON KNOWS (ID1PERSON1 || ',' || ID2PERSON1)または

    CREATE INDEX <INDEX_NAME> ON KNOWS (ID1PERSON1 || ',' || ID2PERSON1, ID1PERSON2 || ',' || ID2PERSON2)

コンポジット頂点キーの一部の列が文字列列である場合、その列は、ファンクション索引の作成でカンマをエスケープする必要があります。

たとえば、前述の例の表PERSONの列ID1の型がVARCHAR2(10)の場合、次のように列のカンマをエスケープする必要があります。

replace(ID1, ',', '\,')

そのため、パフォーマンスを向上させるための索引は次のようになります。

  • CREATE INDEX <INDEX_NAME> ON PERSON (replace(ID1, ',', '\,') || ',' || ID2)
  • CREATE INDEX <INDEX_NAME> ON KNOWS (replace(ID1PERSON1, ',', '\,') || ',' || ID2PERSON1)
12.7.3.2 問合せオプティマイザ・ヒントの使用

次のヒントを使用すると、PGQL可変長パス・パターンのSQLへの変換に影響を与えることができます。

  • REVERSE_PATH: 逆パス最適化のオンとオフを切り替えます(デフォルトではON)。ONの場合、指定されたフィルタ述語に基づいて、パターンを最適に評価できるは、ソースから宛先なのか宛先からソースなのかを自動的に判断します。
  • PUSH_SRC_HOPS: ソース・フィルタ最適化の強要のオンとオフを切り替えます(デフォルトではON)。ONの場合、フィルタ述語を使用して、ソース頂点(パス評価が逆の場合は宛先頂点)の数を制限し、それによって可変長パス・パターン評価の検索領域を制限します。
  • PUSH_DST_HOPS: 宛先フィルタ最適化の強要のオンとオフを切り替えます(デフォルトではOFF)。ONの場合、フィルタ述語を使用して、宛先頂点(パス評価が逆の場合はソース頂点)の数を制限し、それによって可変長パス・パターン評価の検索領域を制限します。

前述のヒントは、次のJava APIメソッドでoptionsパラメータとして構成できます。

  • executeQuery(String pgql, String options)
  • translateQuery(String pgql, String options)
  • execute(String pgql, String matchOptions, String options)

たとえば、次のPGQL問合せについて考えます。

SELECT v1.name AS v1, v2.name AS v2, v3.name As v3 
FROM MATCH (v1:Person)-[e1:friendOf]->(v2:Person), 
MATCH ANY (v2:Person)-[e2:friendOf]->*(v3:Person) 
WHERE v1.name= 'Bob'

PUSH_SRC_HOPSのデフォルト・オプションを使用して前述の問合せを実行すると、次のようにstart_nodes_translationの出力にフィルタ式が表示されます。

System.out.println(pgqlStatement.translateQuery(pgql).getSqlTranslation())
...
...
start_nodes_translation => (to_clob('SELECT ''PERSONS'' AS "src_table", e1.person_b AS "src_key"
FROM "GRAPHUSER"."PERSONS" "V1", "GRAPHUSER"."FRIENDSHIPS" "E1"
WHERE (((e1.person_a = v1.person_id) AND NOT(e1.person_b IS NULL)) AND (v1.name = ''Bob''))')),
     end_nodes_translation => (to_clob('SELECT ''PERSONS'' AS "dst_table", v3.person_id AS "dst_key"
FROM "GRAPHUSER"."PERSONS" "V3"')),
...
...

ヒントPUSH_SRC_HOPS=Fを指定して前述の問合せを実行すると、次のように問合せはSQLに変換されます。

System.out.println(pgqlStatement.translateQuery(pgql,"PUSH_SRC_HOPS=F").getSqlTranslation())

...
...start_nodes_translation => (to_clob('SELECT ''PERSONS'' AS "src_table", v2.person_id AS "src_key"
FROM "GRAPHUSER"."PERSONS" "V2"')),
     end_nodes_translation => (to_clob('SELECT ''PERSONS'' AS "dst_table", v3.person_id AS "dst_key"
FROM "GRAPHUSER"."PERSONS" "V3"')),
...
...
12.7.3.3 グラフ・メタデータ・キャッシュおよび変換キャッシュを使用した問合せ変換の高速化

次のグローバル・キャッシュは、PGQL問合せ変換の高速化に役立ちます。

  • グラフ・メタデータ・キャッシュ: 表、ラベル、プロパティなどのグラフ・メタデータを格納します。
  • 変換キャッシュ: PGQLからSQLへの変換を格納します。

次のJava APIを使用してキャッシュを構成できます。

  • clearTranslationCache()
  • disableTranslationCache()
  • enableTranslationCache()
  • setTranslationCacheMaxCapacity(int maxCapacity)
  • clearGraphMetadataCache()
  • disableGraphMetadataCache()
  • enableGraphMetadataCache()
  • setGraphMetadataCacheMaxCapacity(int maxCapacity)
  • setGraphMetadataRefreshInterval(long interval)

前述のこれらのメソッドは、PgqlConnectionクラスに属します。個々のキャッシュはデータベース・ユーザーごとに保持されるため、キャッシュされたオブジェクトは、接続URLとその下のユーザーが同じである場合に、異なるPgqlConnectionオブジェクト間で共有されます。

デフォルトでは、メタデータ・キャッシュと変換キャッシュの両方が有効になっている場合は、1000ms (デフォルト値)ごとにリフレッシュされます。これにより、複数のJVMを介して1つのグラフを変更する場合に、メタデータ・キャッシュを簡単に同期できます。また、setGraphMetadataRefreshInterval (long interval)関数をコールして、キャッシュのリフレッシュにかかる時間をミリ秒単位で増やすこともできます。

12.7.4 JavaおよびPython APIを使用したPGQL問合せの実行

oracle.pg.rdbms.pgqlパッケージのJava APIを使用して、PGQL問合せを実行できます。また、Python OPG4Pyパッケージを使用して、Oracle Databaseのグラフ・データに対してPGQL問合せを実行できます。このパッケージには、oracle.pg.rdbms.pgqlパッケージのJava APIをラップする1つ以上のモジュールを含むサブパッケージPgqlが含まれています。

12.7.4.1 プロパティ・グラフ・ビューの作成

CREATE PROPERTY GRAPH文を使用してプロパティ・グラフ・ビュー(PGビュー)を作成できます。

例12-1 プロパティ・グラフ・ビューの作成

次の例では、PGビューの作成について説明します。

opg4j> var jdbcUrl="jdbc:oracle:thin:@<host_name>:<port>/<db_service>"
opg4j> var conn = DriverManager.getConnection(jdbcUrl,"<username>","<password>");
opg4j> var pgqlConn = PgqlConnection.getConnection(conn)
opg4j> var pgqlStmt = pgqlConn.createStatement() //create a PGQL Statement
opg4j> conn.setAutoCommit(false)
opg4j> var pgql = 
...> "CREATE PROPERTY GRAPH bank_graph "
...> + "VERTEX TABLES ( bank_accounts AS Accounts "
...> + "KEY (id) "
...> + "LABEL Accounts "
...> + "PROPERTIES (id, name) "
...> + ") "
...> + "EDGE TABLES ( bank_txns AS Transfers "
...> + "KEY (txn_id) "
...> + "SOURCE KEY (from_acct_id) REFERENCES Accounts (id) "
...> + "DESTINATION KEY (to_acct_id) REFERENCES Accounts (id) "
...> + "LABEL Transfers "
...> + "PROPERTIES (from_acct_id, to_acct_id, amount, description) "
...> + ") OPTIONS (PG_VIEW) "
opg4j> pgqlStmt.execute(pgql)
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;
import oracle.pg.rdbms.pgql.jdbc.PgqlJdbcRdbmsDriver;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlStatement;


/*
 * This example shows how to create a property graph view.
 */
public class PgqlCreate
{

  public static void main(String[] args) throws Exception
  {
    int idx=0;
    String jdbcUrl            = args[idx++];
    String username           = args[idx++];
    String password           = args[idx++];
    String graph              = args[idx++];


    Connection conn = null;
    PgqlStatement pgqlStmt = null;

    try {
      //Get a jdbc connection
      DriverManager.registerDriver(new PgqlJdbcRdbmsDriver());
      conn = DriverManager.getConnection(jdbcUrl, username, password);
      conn.setAutoCommit(false);


      // Get a PGQL connection
      PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);

      // Create a PGQL Statement
      pgqlStmt = pgqlConn.createStatement();
      // Execute PGQL Query
      String pgql =
        "CREATE PROPERTY GRAPH " + graph + " " +
        "VERTEX TABLES ( bank_accounts as Accounts " +
        "KEY (id) " +
        "LABEL \"Accounts\"" +
        "PROPERTIES (id, name)" +
        ") " +
        "EDGE TABLES ( bank_txns as Transfers " +
        "KEY (txn_id) " +
        "SOURCE KEY (from_acct_id) REFERENCES Accounts (id) " +
        "DESTINATION KEY (to_acct_id) REFERENCES Accounts (id) " +
        "LABEL \"Transfers\"" +
        "PROPERTIES (from_acct_id, to_acct_id, amount, description)" +
        ") OPTIONS (PG_VIEW) ";

      // Print the results
      pgqlStmt.execute(pgql);
    }
    finally {
      // close the statement
      if (pgqlStmt != null) {
         pgqlStmt.close();
         }
      // close the connection
      if (conn != null) {
         conn.close();
         }
      }
  }
}
>>> pgql_conn = opg4py.pgql.get_connection("<username>","<password>", "jdbc:oracle:thin:@localhost:1521/orclpdb")
>>> pgql_statement = pgql_conn.create_statement()
>>> pgql = """
...         CREATE PROPERTY GRAPH bank_graph
...         VERTEX TABLES (
...           bank_accounts as Accounts
...             LABEL Accounts
...             PROPERTIES (id, name)
...         )
...         EDGE TABLES (
...           bank_txns as Transfers
...             KEY (txn_id)
...             SOURCE KEY (from_acct_id) REFERENCES Accounts(id)
...             DESTINATION KEY (to_acct_id) REFERENCES Accounts (id)
...             LABEL TRANSFERS
...             PROPERTIES (from_acct_id, to_acct_id, amount, description)
...         ) OPTIONS(PG_VIEW)
... """
>>> pgql_statement.execute(pgql)
False

プロパティ・グラフ・ビューの作成を確認するには、データベースで作成されるメタデータ表を確認します。

12.7.4.2 PGQL SELECT問合せの実行

次の例で説明するように、PGQL SELECT問合せを実行できます。

例12-2 PgqlStatementおよびPgqlResultSetを使用した単純なSELECT問合せの実行

次の例では、PgqlConnectionを使用してPgqlStatementを取得します。次に、PgqlStatementexecuteQueryメソッドをコールし、これにより、PgqlResultSetオブジェクトが返されます。PgqlResultSetには、表形式モードで結果を表示するprint()メソッドがあります。

opg4j> var jdbcUrl="jdbc:oracle:thin:@<host_name>:<port>/<db_service>"
opg4j> var conn = DriverManager.getConnection(jdbcUrl,"<username>","<password>");
opg4j> var pgqlConn = PgqlConnection.getConnection(conn)
opg4j> pgqlConn.setGraph("BANK_GRAPH")        
opg4j> var pgqlStmt = pgqlConn.createStatement() //create a PGQL Statement
opg4j> String s = "SELECT n.* FROM MATCH (n:Accounts) LIMIT 3"
opg4j> var resultSet = pgqlStmt.executeQuery(s)
opg4j> resultSet.print() //Prints the query result set
+---------------+
| ID | NAME     |
+---------------+
| 1  | Account1 |
| 2  | Account2 |
| 3  | Account3 |
+---------------+
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;
import oracle.pg.rdbms.pgql.jdbc.PgqlJdbcRdbmsDriver;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;

/*
 * This example shows how to execute a SELECT query on a property graph view.
 */
public class PgqlExample1
{

  public static void main(String[] args) throws Exception
  {
    int idx=0;
    String jdbcUrl            = args[idx++];
    String username           = args[idx++];
    String password           = args[idx++];
    String graph              = args[idx++];


    Connection conn = null;
    PgqlStatement pgqlStmt = null;
    PgqlResultSet rs = null;

    try {
      //Get a jdbc connection
      DriverManager.registerDriver(new PgqlJdbcRdbmsDriver());
      conn = DriverManager.getConnection(jdbcUrl, username, password);
      conn.setAutoCommit(false);


      // Get a PGQL connection
      PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
      pgqlConn.setGraph(graph);

      // Create a PGQL Statement
      pgqlStmt = pgqlConn.createStatement();

      // Execute PGQL Query
      String query = "SELECT n.* FROM MATCH (n:Accounts) LIMIT 5";
      rs = pgqlStmt.executeQuery(query);

      // Print the results
      rs.print();
    }
    finally {
      // close the result set
      if (rs != null) {
         rs.close();
         }
      // close the statement
      if (pgqlStmt != null) {
         pgqlStmt.close();
         }
      // close the connection
      if (conn != null) {
         conn.close();
         }
      }
  }
}
>>> pgql_conn = opg4py.pgql.get_connection("<username>","<password>", "<jdbcUrl>")
>>> pgql_statement = pgql_conn.create_statement()
>>> pgql_conn.set_graph("BANK_GRAPH")
>>> s = "SELECT n.* FROM MATCH (n:Accounts) LIMIT 3"
>>> pgql_statement.execute_query(s)
>>> pgql_result_set = pgql_statement.execute_query(s)
>>> pgql_result_set.print()
+---------------+
| ID | NAME     |
+---------------+
| 1  | Account1 |
| 2  | Account2 |
| 3  | Account3 |
+---------------+
>>> pgql_result_set
PgqlResultSet(java_pgql_result_set: oracle.pg.rdbms.pgql.PgqlResultSet, # of results: 3)

また、to_pandas()メソッドを使用して、前述のコードで取得したPGQL結果セットをPandasデータフレームに変換できます。

ノート:

to_pandas()のコールを正常に実行するには、システムにpandasパッケージをインストールする必要があります。このパッケージは、バージョンPython 3.8およびPython 3.9のPythonクライアントのインストール時に自動的にインストールされます。ただし、to_pandas()のコールに失敗した場合は、pandasモジュールがシステムにインストールされているかどうかを確認してください。モジュールが見つからないか、Pythonのバージョンが前述のバージョンと異なる場合は、pandasパッケージを手動でインストールします。

例12-3 PgqlPreparedStatementを使用したSELECT問合せの実行

opg4j> var jdbcUrl="jdbc:oracle:thin:@<host_name>:<port>/<db_service>"
opg4j> var conn = DriverManager.getConnection(jdbcUrl,"<username>","<password>");
opg4j> var pgqlConn = PgqlConnection.getConnection(conn)
opg4j> pgqlConn.setGraph("BANK_GRAPH");         
opg4j> String s = "SELECT n.* FROM MATCH (n:Accounts) LIMIT ?"
opg4j> var ps = pgqlConn.prepareStatement(s, 0 /* timeout */, 4 /* parallel */, 2 /* dynamic sampling */, -1 /* max results */, null /* match options */, null /* options */)
opg4j> ps.setInt(1, 3)
opg4j> var rs = ps.executeQuery()
opg4j> rs.print() //Prints the query result set
+---------------+
| ID | NAME     |
+---------------+
| 1  | Account1 |
| 2  | Account2 |
| 3  | Account3 |
+---------------+
import java.sql.Statement;
import java.sql.DriverManager;
import oracle.pg.rdbms.pgql.jdbc.PgqlJdbcRdbmsDriver;
import oracle.pg.rdbms.pgql.*; 

public class PgqlExample2
{
  public static void main(String[] args) throws Exception
  {
    int idx=0;
    String jdbcUrl            = args[idx++];
    String username           = args[idx++];
    String password           = args[idx++];
    String graph              = args[idx++];


    Connection conn = null;
    PgqlStatement pgqlStmt = null;
    PgqlResultSet rs = null;

    try {
      //Get a jdbc connection
      DriverManager.registerDriver(new PgqlJdbcRdbmsDriver());
      conn = DriverManager.getConnection(jdbcUrl, username, password);
      conn.setAutoCommit(false);


      // Get a PGQL connection
      PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
      pgqlConn.setGraph(graph);


      // Execute PGQL Query
      String s = "SELECT n.* FROM MATCH (n:Accounts) LIMIT ?";
      PgqlPreparedStatement pStmt = pgqlConn.prepareStatement(s, 0, 4 , 2 , -1 , null , null);
      pStmt.setInt(1,3);
      rs = pStmt.executeQuery();

      // Print the results
      rs.print();
    }
    finally {
      // close the result set
      if (rs != null) {
         rs.close();
         }
      // close the statement
      if (pgqlStmt != null) {
         pgqlStmt.close();
         }
      // close the connection
      if (conn != null) {
         conn.close();
         }
      }
  }
}
>>> pgql_conn = opg4py.pgql.get_connection("<username>","<password>", "<jdbcUrl>")
>>> pgql_statement = pgql_conn.create_statement()
>>> pgql_conn.set_graph("BANK_GRAPH")
>>> s = "SELECT n.* FROM MATCH (n:Accounts) LIMIT ?"
>>> ps = pgql_conn.prepare_statement(s, timeout=0, parallel=4, dynamicSampling=2, maxResults=-1, matchOptions=None, options=None)
>>> ps.set_int(1,3)
>>> ps.execute_query().print()
+---------------+
| ID | NAME     |
+---------------+
| 1  | Account1 |
| 2  | Account2 |
| 3  | Account3 |
+---------------+

例12-4 グループ化と集計を指定したSELECT問合せの実行

opg4j> var jdbcUrl="jdbc:oracle:thin:@<host_name>:<port>/<db_service>"
opg4j> var conn = DriverManager.getConnection(jdbcUrl,"<username>","<password>");
opg4j> var pgqlConn = PgqlConnection.getConnection(conn)
opg4j> pgqlConn.setGraph("BANK_GRAPH")        
opg4j> var pgqlStmt = pgqlConn.createStatement() //create a PGQL Statement
opg4j> String query = "SELECT v1.id, COUNT(v2) AS numTxns "+
...>         "FROM MATCH (v1)-[e IS Transfers]->(v2) "+
...>         "GROUP BY v1 "+
...>         "ORDER BY numTxns DESC "+
...>         "LIMIT 3"
opg4j> var resultSet = pgqlStmt.executeQuery(query)
opg4j> resultSet.print() //Prints the query result set
+---------------+
| ID  | NUMTXNS |
+---------------+
| 687 | 6       |
| 195 | 5       |
| 192 | 5       |
+---------------+
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;
import oracle.pg.rdbms.pgql.jdbc.PgqlJdbcRdbmsDriver;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;


/*
 * This example shows how to execute a SELECT query with aggregation .*/
public class PgqlExample3
{

  public static void main(String[] args) throws Exception
  {
    int idx=0;
    String jdbcUrl            = args[idx++];
    String username           = args[idx++];
    String password           = args[idx++];
    String graph              = args[idx++];


    Connection conn = null;
    PgqlStatement pgqlStmt = null;
    PgqlResultSet rs = null;

    try {
      //Get a jdbc connection
      DriverManager.registerDriver(new PgqlJdbcRdbmsDriver());
      conn = DriverManager.getConnection(jdbcUrl, username, password);
      conn.setAutoCommit(false);


      // Get a PGQL connection
      PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
      pgqlConn.setGraph(graph);

      // Create a PGQL Statement
      pgqlStmt = pgqlConn.createStatement();

      // Execute PGQL Query
      String query =
        "SELECT v1.id, COUNT(v2) AS numTxns "+
        "FROM MATCH (v1)-[e IS Transfers]->(v2) "+
        "GROUP BY v1 "+
        "ORDER BY numTxns DESC";

      rs = pgqlStmt.executeQuery(query);
      // Print the results
      rs.print();
    }
    finally {
      // close the result set
      if (rs != null) {
         rs.close();
         }
      // close the statement
      if (pgqlStmt != null) {
         pgqlStmt.close();
         }
      // close the connection
      if (conn != null) {
         conn.close();
         }
      }
  }
}
>>> pgql_conn = opg4py.pgql.get_connection("<username>","<password>", "<jdbcUrl>")
>>> pgql_statement = pgql_conn.create_statement()
>>> pgql_conn.set_graph("BANK_GRAPH")
>>> query = """
...          SELECT v1.id, COUNT(v2) AS numtxns
...          FROM MATCH (v1)-[e IS Transfers]->(v2)
...          GROUP BY v1
...          ORDER BY numtxns DESC
...          LIMIT 3
...          """
>>> pgql_statement.execute_query(query).print()
+---------------+
| ID  | NUMTXNS |
+---------------+
| 687 | 6       |
| 195 | 5       |
| 192 | 5       |
+---------------+

例12-5 PGQLパス問合せの表示

opg4j> var jdbcUrl="jdbc:oracle:thin:@<host_name>:<port>/<db_service>"
opg4j> var conn = DriverManager.getConnection(jdbcUrl,"<username>","<password>");
opg4j> var pgqlConn = PgqlConnection.getConnection(conn)
opg4j> pgqlConn.setGraph("BANK_GRAPH")        
opg4j> var pgqlStmt = pgqlConn.createStatement() //create a PGQL Statement
opg4j> String query = "PATH onehop AS ()-[IS transfers]->() "+
...>         "SELECT v1.id FROM MATCH (v1)-/:onehop/->(v2) "+
...>         "WHERE v2.id = 365"
opg4j> var resultSet = pgqlStmt.executeQuery(query)
opg4j> resultSet.print() //Prints the query result set
+-----+
| ID  |
+-----+
| 132 |
| 435 |
| 296 |
| 327 |
| 328 |
| 399 |
| 684 |
| 919 |
| 923 |
| 771 |
+-----+
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;
import oracle.pg.rdbms.pgql.jdbc.PgqlJdbcRdbmsDriver;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlResultSet;
import oracle.pg.rdbms.pgql.PgqlStatement;


/*
 * This example shows how to execute a PGQL PATH query.*/
public class PgqlExample4
{

  public static void main(String[] args) throws Exception
  {
    int idx=0;
    String jdbcUrl            = args[idx++];
    String username           = args[idx++];
    String password           = args[idx++];
    String graph              = args[idx++];


    Connection conn = null;
    PgqlStatement pgqlStmt = null;
    PgqlResultSet rs = null;

    try {
      //Get a jdbc connection
      DriverManager.registerDriver(new PgqlJdbcRdbmsDriver());
      conn = DriverManager.getConnection(jdbcUrl, username, password);
      conn.setAutoCommit(false);


      // Get a PGQL connection
      PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
      pgqlConn.setGraph(graph);

      // Create a PGQL Statement
      pgqlStmt = pgqlConn.createStatement();

     // Execute PGQL Query
      String query =
                 "PATH onehop AS ()-[IS transfers]->() "+
                 "SELECT v1.id FROM MATCH (v1)-/:onehop/->(v2) "+
                 "WHERE v2.id = 365";
      rs = pgqlStmt.executeQuery(query);

      // Print the results
      rs.print();
    }
    finally {
      // close the result set
      if (rs != null) {
         rs.close();
         }
      // close the statement
      if (pgqlStmt != null) {
         pgqlStmt.close();
         }
      // close the connection
      if (conn != null) {
         conn.close();
         }
      }
  }
}
>>> pgql_conn = opg4py.pgql.get_connection("<username>","<password>", "<jdbcUrl>")
>>> pgql_statement = pgql_conn.create_statement()
>>> pgql_conn.set_graph("BANK_GRAPH")
>>> query = """
...                  PATH onehop AS ()-[IS transfers]->()
...                  SELECT v1.id FROM MATCH (v1)-/:onehop/->(v2)
...                  WHERE v2.id = 365
...         """
>>> pgql_statement.execute_query(query).print()
+-----+
| ID  |
+-----+
| 132 |
| 435 |
| 296 |
| 327 |
| 328 |
| 399 |
| 684 |
| 919 |
| 923 |
| 771 |
+-----+
12.7.4.3 プロパティ・グラフ・ビューを変更するためのPGQL問合せの実行

PGQL INSERTUPDATEおよびDELETEの各問合せをプロパティ・グラフ・ビューに対して実行するには、OPG4J Javaシェル、OPG4Py Pythonシェルを使用するか、JavaまたはPythonアプリケーションを使用します。

グラフに頂点またはエッジを挿入する際に、一意のIDが自動生成されないことに注意してください。このため、キー列値がグラフ・プロパティに存在するか、データベースによって自動生成されるようにする(SEQUENCEおよびTRIGGERSを使用するか、IDENTITY列を使用して自動増分機能で実現する)必要があります。

次の例では、2つの新しい頂点を挿入し、2つの頂点間にエッジ関係も追加します。

opg4j> String pgql =
...>     "INSERT VERTEX v1 LABELS (Person) PROPERTIES (v1.name= 'ABC', v1.height=1.6, v1.birthdate = to_date('13/06/1963', 'DD/MM/YYYY')) "+
...>     "     , VERTEX v2 LABELS (Person) PROPERTIES (v2.name= 'XYZ', v2.height=1.75, v2.birthdate = to_date('19/06/1963', 'DD/MM/YYYY')) "+
...>     "     , EDGE e BETWEEN v1 AND v2 LABELS (friendof) PROPERTIES ( e.meeting_date = to_date('19/06/2021', 'DD/MM/YYYY')) "
pgql ==> "INSERT VERTEX v1 LABELS (Person) PROPERTIES (v1.name= 'ABC', v1.height=1.6, v1.birthdate = to_date('13/06/1963', 'DD/MM/YYYY'))      , VERTEX v2 LABELS (Person) PROPERTIES (v2.name= 'XYZ', v2.height=1.75, v2.birthdate = to_date('19/06/1963', 'DD/MM/YYYY'))      , EDGE e BETWEEN v1 AND v2 LABELS (friendof) PROPERTIES ( e.meeting_date = to_date('19/06/2021', 'DD/MM/YYYY')) "
opg4j> pgqlStmt.execute(pgql)
$14 ==> false
String pgql =
...>     "INSERT VERTEX v1 LABELS (Person) PROPERTIES (v1.name= 'ABC', v1.height=1.6, v1.birthdate = to_date('13/06/1963', 'DD/MM/YYYY')) "+
...>     "     , VERTEX v2 LABELS (Person) PROPERTIES (v2.name= 'XYZ', v2.height=1.75, v2.birthdate = to_date('19/06/1963', 'DD/MM/YYYY')) "+
...>     "     , EDGE e BETWEEN v1 AND v2 LABELS (friendof) PROPERTIES ( e.meeting_date = to_date('19/06/2021', 'DD/MM/YYYY')) ";
pgqlStmt.execute(pgql);
>>> pgql = """
...     INSERT VERTEX v1 LABELS (Person) PROPERTIES (v1.name= 'ABC', v1.height=1.6, v1.birthdate = to_date('13/06/1963', 'DD/MM/YYYY'))
...     , VERTEX v2 LABELS (Person) PROPERTIES (v2.name= 'XYZ', v2.height=1.75, v2.birthdate = to_date('19/06/1963', 'DD/MM/YYYY'))
...     , EDGE e BETWEEN v1 AND v2 LABELS (friendof) PROPERTIES ( e.meeting_date = to_date('19/06/2021', 'DD/MM/YYYY'))
... """
>>> pgql_statement.execute(pgql)
False

次の例では、UPDATE問合せを実行して前述の例で挿入されたエッジ・プロパティを変更してから、SELECT問合せを使用して更新操作を検証します。

opg4j> String pgql = "UPDATE e SET (e.meeting_date = to_date('12/02/2022', 'DD/MM/YYYY')) "+
...>     "FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) "+
...>     "WHERE v1.person_id = 27 AND v2.person_id = 28"
pgql ==> "UPDATE e SET (e.meeting_date = to_date('12/02/2022', 'DD/MM/YYYY')) FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) WHERE v1.person_id = 27 AND v2.person_id = 28"
opg4j> pgqlStmt.execute(pgql)
$40 ==> false
opg4j>pgqlStmt.executeQuery("SELECT e.meeting_date FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) WHERE v1.person_id = 27").print()
+-----------------------+
| MEETING_DATE          |
+-----------------------+
| 2022-02-12 00:00:00.0 |
+-----------------------+
String pgql ="UPDATE e SET (e.meeting_date = to_date('12/02/2022', 'DD/MM/YYYY')) "+
"FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) "+
"WHERE v1.person_id = 27 AND v2.person_id = 28";
pgqlStmt.execute(pgql);
>>> pgql = """
...     UPDATE e SET (e.meeting_date = to_date('12/02/2022', 'DD/MM/YYYY'))
...     FROM MATCH (v1:Person)-[e:friendof]->(v2:Person)
...     WHERE v1.person_id = 27 AND v2.person_id = 28
... """
>>> pgql_statement.execute(pgql)
False
>>> pgql_statement.execute_query("SELECT e.meeting_date FROM MATCH(v1:Person)-[e:friendof]->(v2:Person) WHERE v1.person_id = 27").print()
+-----------------------+
| MEETING_DATE          |
+-----------------------+
| 2022-02-12 00:00:00.0 |
+-----------------------+

DELETE問合せを使用すると、グラフ内の頂点およびエッジを削除できます。次の例では、DELETE問合せを実行してグラフのエッジを削除します。

opg4j> pgqlStmt.execute("DELETE e FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) WHERE v.person_id=27")
$14 ==> false
pgqlStmt.execute("DELETE e FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) WHERE v.person_id=27");
>>> pgql_statement.execute("DELETE e FROM MATCH (v1:Person)-[e:friendof]->(v2:Person) WHERE v1.person_id=27")
False
12.7.4.4 プロパティ・グラフ・ビューの削除

PGQL DROP PROPERTY GRAPH文を使用して、プロパティ・グラフ・ビュー(PGビュー)を削除できます。PGビューのメタデータ表はすべて削除されることに注意してください。

例12-6 プロパティ・グラフ・ビューの作成

次の例では、PGビューの作成について説明します。

opg4j> var jdbcUrl="jdbc:oracle:thin:@<host_name>:<port>/<db_service>"
opg4j> var conn = DriverManager.getConnection(jdbcUrl,"<username>","<password>")
opg4j> var pgqlConn = PgqlConnection.getConnection(conn)
opg4j> var pgqlStmt = pgqlConn.createStatement() //create a PGQL Statement
opg4j> pgqlStmt.execute("DROP PROPERTY GRAPH <pgview>")
$9 ==> false
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;
import oracle.pg.rdbms.pgql.jdbc.PgqlJdbcRdbmsDriver;
import oracle.pg.rdbms.pgql.PgqlConnection;
import oracle.pg.rdbms.pgql.PgqlStatement;
/**
 * This example shows how to drop a property graph view.
 */
public class DropPgView
{

  public static void main(String[] args) throws Exception
  {
    int idx=0;
    String jdbcUrl            = args[idx++];
    String username           = args[idx++];
    String password           = args[idx++];
    String graph              = args[idx++];  
    
    Connection conn = null;
    PgqlStatement pgqlStmt = null;
    
    try {
      //Get a jdbc connection
      DriverManager.registerDriver(new PgqlJdbcRdbmsDriver());
      conn = DriverManager.getConnection(jdbcUrl, username, password);
      conn.setAutoCommit(false);
                
      // Get a PGQL connection
      PgqlConnection pgqlConn = PgqlConnection.getConnection(conn);
	  
	  // Create PGQL Statement
      pgqlStmt = pgqlConn.createStatement();

      String query = "DROP PROPERTY GRAPH " +pgview;
      pgqlStmt.execute(query);
      
    }
    finally {
      // close the statement
      if (pgqlStmt != null) {
         pgqlStmt.close();
         }
      // close the connection
      if (conn != null) {
         conn.close();
         }
      }
  }
}
>>> pgql_conn = opg4py.pgql.get_connection("<username>","<password>", "jdbc:oracle:thin:@localhost:1521/orclpdb")
>>> pgql_statement = pgql_conn.create_statement()
>>> pgql = "DROP PROPERTY GRAPH <pgview>"
>>> pgql_statement.execute(pgql)
False