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

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

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

図13-4 Oracle DatabaseのPGQLプロパティ・グラフのPGQL

図13-4の説明
「図13-4 Oracle DatabaseのPGQLプロパティ・グラフのPGQL」の説明

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

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

13.7.1 PGQLプロパティ・グラフでサポートされているPGQL機能および制限事項

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

次の表に、PGQLプロパティ・グラフでサポートされているPGQL機能およびサポートされていないPGQL機能の完全なリストを示します。

表13-2 PGQLプロパティ・グラフでサポートされているPGQLの機能および制限事項

機能 PGQLプロパティ・グラフ上のPGQL
CREATE PROPERTY GRAPH サポート対象
DROP PROPERTY GRAPH サポート対象
固定長パターン一致 サポート対象
可変長パターン一致の目標 サポート対象:
  • 到達可能性
  • パス検索接頭辞:
    • ANY
    • ANY SHORTEST
    • SHORTEST k
    • ALL
  • パス・モード:
    • WALK
    • TRAIL
    • SIMPLE
    • ACYCLIC

制限事項:

  • パス検索接頭辞:
    • ALL SHORTEST
    • ANY CHEAPEST
    • CHEAPEST k
可変長パターン一致の数量詞 サポート対象:
  • *
  • +
  • ?
  • { n }
  • { n, }
  • { n, m }
  • { , m }
可変長パスのネスト解除 サポート対象:
  • ONE ROW PER STEP

    制限: 数量詞*はサポートされていません

サポート対象外:

  • ONE ROW PER VERTEX
GROUP BY サポート対象
HAVING サポート対象
集計 サポート対象:
  • COUNT
  • MINMAXAVGSUM
  • LISTAGG
  • JSON_ARRAYAGG

制限事項:

  • 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] (Oracle Database 23aiでのみサポート)
  • VERTEX_EQUALEDGE_EQUAL

制限事項:

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

拡張機能:

  • 他のグラフのメタデータに基づいてグラフを作成するためのCREATE PROPERTY GRAPHBASE GRAPHS
INSERT/UPDATE/DELETE Oracle Database 19c以降でサポート
INTERVALリテラルおよび操作 サポート対象外

13.7.1.1 例でサポートされているPGQL機能に関する追加情報

PGQLプロパティ・グラフでは、次の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
  • 再帰的問合せでは、次の数量詞がサポートされています。

    表13-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 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 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 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 LATERAL副問合せがサポートされています。たとえば:
    SELECT recipient, COUNT(*) AS num_large_transactions
    FROM LATERAL ( SELECT m.id AS recipient
                   FROM MATCH (n IS accounts) -[e IS transfers]-> (m IS accounts)
                   WHERE n.id = 772
                   ORDER BY e.amount DESC )
    GROUP BY recipient
    ORDER BY num_large_transactions DESC
  • PGQL GRAPH_TABLE演算子がサポートされています。たとえば:
    SELECT * FROM GRAPH_TABLE ( bank_graph
      MATCH (a IS accounts) -[e IS transfers]-> (b IS accounts)
      COLUMNS ( a.id as from_ac, e.amount as amount, b.id as to_ac  )
    ) FETCH FIRST FIVE ROWS ONLY
  • 頂点がエッジのソースまたは宛先かどうかを検証するソース(IS [NOT] SOURCE OF)述語および宛先(IS [NOT] DESTINATION OF)述語がサポートされます。これは、エッジが任意の方向エッジ・パターン(-[e]-)を介して一致する場合に便利です。このPGQL機能はOracle Database 23aiでのみサポートされていることに注意してください。たとえば:
    SELECT e.amount, CASE WHEN n IS SOURCE OF e THEN 'Outgoing transaction' ELSE 'Incoming transaction' END AS type
    FROM MATCH (n:Accounts) -[e:transfers]- (m:Accounts)
    WHERE n.id = 284
    ORDER BY type, e.amount
    この問合せの結果は次のようになります:
    +-------------------------------+
    | AMOUNT | TYPE                 |
    +-------------------------------+
    | 1000   | Incoming transaction |
    | 1200   | Outgoing transaction |
    | 1300   | Outgoing transaction |
    +-------------------------------+
  • 値をJSON配列に集計するためのJSON_ARRAYAGG関数(『Oracle Database SQL言語リファレンス』JSON_ARRAYAGGを参照)がサポートされています。
    SELECT JSON_ARRAY_AGG(n.id) AS txn_from
    FROM MATCH (n:Accounts) -[e:transfers]- (m:Accounts)
    WHERE m.id = 616

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

    +-------------------------------------------+
    | TXN_FROM                                  |
    +-------------------------------------------+
    | [202,582,650,108,744,756,801,674,710,764] |
    +-------------------------------------------+
  • 頂点キーとエッジ・キーが一意かどうか、およびエッジのソースと宛先が存在するかどうかを確認する組込みグラフ検証関数pg.validate()
    pgqlStmt.execute("CALL pg.validate('BANK_TXN_GRAPH')")
    $1 ==> false
    次のように、頂点がない無効なキーまたはエッジの場合、例外が発生します:
    pgqlStmt.execute("CALL pg.validate('COUNTRIES')")
    opg4j> pgqlStmt.execute("CALL pg.validate('COUNTRIES')")
    |  Exception oracle.pg.rdbms.pgql.PgqlToSqlException: Invalid vertex key 60 for edge NO in edge table CTY_REG with destination key column(s) "REGION_ID" referencing REGIONS ( "REGION_ID" )
  • ONE ROW PER STEP句を使用したパスのネスト解除は、PGQL GRAPH_TABLE演算子問合せでサポートされています。
    SELECT *
    FROM GRAPH_TABLE ( financial_transactions
           MATCH
             (a IS account) -[IS transaction]->+ (a) 
           KEEP SHORTEST 5 SIMPLE PATHS
           WHERE a.number = 10039
           ONE ROW PER STEP ( v1, e, v2 )  
           COLUMNS( MATCHNUM() AS matchnum,
                    ELEMENT_NUMBER(e) AS elemnum,
                    v1.number AS account1,
                    v2.number AS account2, e.amount))
    ORDER BY matchnum, elemnum

    前述の例に示すように、ONE ROW PER STEP句は、イテレータの頂点変数、イテレータのエッジ変数および別のイテレータの頂点変数を宣言します。問合せでは、次に示すように、ステップごとに1行(ステップは頂点-エッジ-頂点トリプル)が生成されます:

    +---------------------------------------------------+
    | matchnum | elemnum | account1 | account2 | amount |
    +---------------------------------------------------+
    | 0        | 2       | 10039    | 8021     | 1000.0 |
    | 0        | 4       | 8021     | 1001     | 1500.3 |
    | 0        | 6       | 1001     | 2090     | 9999.5 |
    | 0        | 8       | 2090     | 10039    | 9900.0 |
    | 1        | 2       | 10039    | 8021     | 1000.0 |
    | 1        | 4       | 8021     | 1001     | 3000.7 |
    | 1        | 6       | 1001     | 2090     | 9999.5 |
    | 1        | 8       | 2090     | 10039    | 9900.0 |
    +---------------------------------------------------+

    前述の出力には、それぞれ4つのエッジを持つ2つのパスが示されています。

サポートされていない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関数

ノート:

13.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の変換と実行に使用できる問合せ引数をまとめています。

表13-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

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

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

13.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_PGQL )

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

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_PGQL )

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

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)

13.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"')),
...
...

13.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)関数をコールして、キャッシュのリフレッシュにかかる時間をミリ秒単位で増やすこともできます。

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

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

13.7.4.1 PGQLプロパティ・グラフの作成

CREATE PROPERTY GRAPH文を使用してPGQLプロパティ・グラフを作成できます。

例13-1 PGQLプロパティ・グラフの作成

次の例では、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> 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_PGQL) "
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 PGQL property graph.
 */
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_PGQL) ";

      // 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_PGQL)
... """
>>> pgql_statement.execute(pgql)
False

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

13.7.4.2 PGQL SELECT問合せの実行

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

例13-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 PGQL property graph.
 */
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パッケージを手動でインストールします。

例13-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 |
+---------------+

例13-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       |
+---------------+

例13-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 |
+-----+

13.7.4.3 PGQLプロパティ・グラフを変更するためのPGQL問合せの実行

PGQL INSERTUPDATEおよびDELETEの各問合せをPGQLプロパティ・グラフに対して実行するには、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

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

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

例13-6 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> var pgqlStmt = pgqlConn.createStatement() //create a PGQL Statement
opg4j> pgqlStmt.execute("DROP PROPERTY GRAPH <graph>")
$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 PGQL property graph.
 */
public class DropPgqlGraph
{

  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 " +graph;
      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 <graph>"
>>> pgql_statement.execute(pgql)
False