パスを操作する

このページでは、Spanner Graph でグラフパスを操作する方法について説明します。

グラフ データベースでは、グラフパス データ型はエッジと交互に配置された一連のノードを表し、これらのノードとエッジがどのように関連しているかを示します。パスデータ型の詳細については、グラフパスの種類をご覧ください。

Spanner Graph Language(GQL)を使用すると、グラフパスを構築してクエリを実行できます。このドキュメントの例では、Spanner Graph を設定してクエリを実行するページと同じ Spanner Graph スキーマを使用します。

グラフパスを作成する

グラフパスを作成するには、グラフパターンでパス変数を作成するか、PATH 関数を使用します。

パス変数を使用してグラフパスを作成することをおすすめします。パス変数を作成する形式は次のとおりです。

MATCH p = PATH_PATTERN

詳細については、グラフパターンをご覧ください。

次の例のクエリは、FinGraph 内の口座間の送金パターンを検索します。

GRAPH FinGraph
MATCH p = (src:Account {id: 16})-[t1:Transfers]->(mid:Account)-[t2:Transfers]->
  (dst:Account {id: 7})
RETURN TO_JSON(p) AS full_path;

結果

full_path
[{"identifier": ..., "properties": {"id": 16, ...}, ...}, {"identifier": ..., "properties": {"amount": 300.0, ...}, ...}, ...]

この結果は、クエリがデータベースで Account -> Transfers -> Account パターンを見つけたことを示しています。

グラフパスをクエリする

次のパス固有の関数を使用して、グラフパスをクエリできます。Spanner Graph クエリの一般的な情報については、クエリの概要をご覧ください。

EDGES

EDGES 関数は、グラフパス内のすべてのエッジを返します。セマンティクスの詳細については、EDGES をご覧ください。

このクエリは、中間口座を経由する 2 つのアカウント間のパスを検索します。パス内の 2 番目の Transfers エッジの量を返します。これは、srcmid の間、または middst の間のいずれかです。

GRAPH FinGraph
MATCH p = (src:Account {id: 7})-[t1:Transfers]->{1,3}(mid:Account)-[t2:Transfers]->
  {1,3}(dst:Account {id: 16})
LET second_edge = EDGES(p)[1]
RETURN DISTINCT src.id AS src, dst.id AS dst, second_edge.amount AS second_edge_amount;

結果

src dst second_edge_amount
7 16 300

NODES

NODES 関数は、グラフパス内のすべてのノードを返します。セマンティクスの詳細については、NODES をご覧ください。

このクエリは、2 つの転送のグラフパスを見つけて、パスを表す JSON リストを返します。

GRAPH FinGraph
MATCH p = (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
RETURN TO_JSON(NODES(p)) AS nodes;

結果

ノード
[{"identifier": "...", "properties": {"id": 16}, ...}, {"identifier": "...", "properties": {"id": 20, ...}, ...]
...

PATH_FIRST

PATH_FIRST 関数は、グラフパス内の最初のノードを検索します。セマンティクスの詳細については、PATH_FIRST をご覧ください。

このクエリは、2 つの転送のグラフパス内の最初のノードを検索します。Account ノードのラベルとアカウントのニックネームを返します。

GRAPH FinGraph
MATCH p = -[:Transfers]->{1,3}(dst:Account{id: 7})
RETURN DISTINCT PATH_FIRST(p).id AS can_reach_target;

結果

can_reach_target
7
16
20

PATH_LAST

PATH_LAST 関数は、グラフパスの最後のノードを検索します。セマンティクスの詳細については、PATH_LAST をご覧ください。

このクエリは、2 つの転送のグラフパス内の最後のノードを検索します。Account ノードのラベルとアカウントのニックネームを返します。

GRAPH FinGraph
MATCH p =(start:Account{id: 7})-[:Transfers]->{1,3}
RETURN DISTINCT PATH_LAST(p).id as can_reach_target;

結果

can_reach_target
7
16
20

PATH_LENGTH

PATH_LENGTH 関数は、グラフパス内のエッジの数を検索します。セマンティクスの詳細については、PATH_LENGTH をご覧ください。

このクエリは、1 ~ 3 個の転送を含むグラフパス内のエッジの数を検索します。

GRAPH FinGraph
MATCH p = (src:Account)-[e:Transfers]->{1,3}(dst:Account)
RETURN PATH_LENGTH(p) AS num_transfers, COUNT(*) AS num_paths;

結果

num_transfers num_paths
1 5
2 7
3 11

IS_ACYCLIC

IS_ACYCLIC 関数は、グラフパスに繰り返しノードの有無を確認します。繰り返しが見つかった場合は TRUE を返し、それ以外の場合は FALSE を返します。セマンティクスの詳細については、IS_ACYCLIC をご覧ください。

このクエリは、このグラフパスに重複するノードがあるかどうかを確認します。

GRAPH FinGraph
MATCH p = (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
RETURN IS_ACYCLIC(p) AS is_acyclic_path, src.id AS source_account_id,
  mid.id AS mid_account_id, dst.id AS dst_account_id;

結果

is_acyclic_path source_account_id mid_account_id dst_account_id
TRUE 16 20 7
TRUE 20 7 16
TRUE 20 7 16
FALSE 16 20 16
TRUE 7 16 20
TRUE 7 16 20
FALSE 20 16 20

IS_TRAIL

IS_TRAIL 関数は、グラフパスに繰り返しエッジがあるかどうかを確認します。繰り返しが見つかった場合は TRUE を返し、それ以外の場合は FALSE を返します。セマンティクスの詳細については、IS_TRAIL をご覧ください。

このクエリは、このグラフパスに重複するエッジがあるかどうかを確認します。

GRAPH FinGraph
MATCH p = (src:Account)-[t1:Transfers]->(mid1:Account)-[t2:Transfers]->
  (mid2:Account)-[t3:Transfers]->(dst:Account)
WHERE src.id < dst.id
RETURN IS_TRAIL(p) AS is_trail_path, t1.id AS t1_id, t2.id AS t2_id, t3.id AS t3_id;

結果

is_trail_path t1_id t2_id t3_id
FALSE 16 20 16
TRUE 7 16 20
TRUE 7 16 20

パスモード

Spanner Graph では、重複するノードとエッジがデフォルトで返されます。次のパスモードを使用すると、指定されたモードに基づいて、ノードとエッジが繰り返されるパスを含めたり除外したりできます。セマンティクスの詳細については、パスモードをご覧ください。

WALK

デフォルトの WALK パスモードでは、ノードやエッジが繰り返されるパスも含め、すべてのパスが保持されます。

次のクエリは、定量化されていないパスパターンで WALK パスモードを使用する方法を示しています。結果の最初のパスでは、t1t3 に同じエッジが使用されています。

GRAPH FinGraph
MATCH p = WALK (src:Account)-[t1:Transfers]->(mid1:Account)-[t2:Transfers]->
  (mid2:Account)-[t3:Transfers]->(dst:Account)
WHERE src.id < dst.id
RETURN t1.id AS transfer1_id, t2.id AS transfer2_id, t3.id AS transfer3_id;

結果

transfer1_id transfer2_id transfer3_id
16 20 16
7 16 20
7 16 20

ACYCLIC

デフォルトの ACYCLIC パスモードでは、ノードが繰り返されるパスが除外されます。

次のクエリは、定量化されていないパスパターンACYCLIC パスモードを使用する方法を示しています。src ノードと dst ノードが同じパスは除外されます。

GRAPH FinGraph
MATCH p = ACYCLIC (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
RETURN src.id AS account1_id, mid.id AS account2_id, dst.id AS account3_id;

結果

account1_id account2_id account3_id
20 7 16
20 7 16
7 16 20
7 16 20
16 20 7

TRAIL

デフォルトの TRAIL パスモードでは、エッジが繰り返されるパスが除外されます。

次のクエリは、定量化されていないパスパターンで TRAIL パスモードを使用する方法を示しています。t1 エッジと t3 エッジが同じパスは除外されます。

GRAPH FinGraph
MATCH p = TRAIL (src:Account)-[t1:Transfers]->(mid1:Account)-[t2:Transfers]->
  (mid2:Account)-[t3:Transfers]->(dst:Account)
RETURN
  t1.id AS transfer1_id, t2.id AS transfer2_id, t3.id AS transfer3_id;

結果

transfer1_id transfer2_id transfer3_id
16 20 7
16 20 7
20 7 16
20 7 16
7 16 20
7 16 20
7 16 20
7 16 20
20 16 20

パス検索の接頭辞

パス検索接頭辞を使用すると、パスパターンを制限して、各データ パーティションから最短パスを返すことができます。セマンティクスの詳細については、パス検索接頭辞をご覧ください。

ANY SHORTEST

ANY SHORTEST パス検索接頭辞は、各データパーティションのパターンに一致する最短パス(エッジ数が最も少ないパス)を返します。パーティションごとに最短経路が複数ある場合は、いずれかを返します。

次のクエリは、各 [a, b] ペア間の任意のパスに一致します。

GRAPH FinGraph
MATCH p = ANY SHORTEST (a:Account)-[t:Transfers]->{1,4}(b:Account)
WHERE a.is_blocked
LET total_amount = SUM(t.amount)
RETURN a.id AS account1_id, total_amount, b.id AS account2_id;

結果

account1_id total_amount account2_id
16 500 16
16 800 7
16 300 20

変換規則

詳細については、GRAPH_PATH 変換ルールをご覧ください。

ユースケースの例

次のユースケースの例では、すべてのアカウントがアカウント ID 20 から 1 ~ 3 つのアカウントを経由して転送されています。

GRAPH FinGraph
MATCH p = (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN DISTINCT dst.id AS dst;

結果

dst
7
16
20

ただし、アカウント ID 20 を返すクエリは、アカウント ID 20 で始まるため、範囲が広すぎる可能性があります。より具体的な結果を表示するには、繰り返しノードのない非巡回グラフパスのみを表示するようにクエリを適用します。手順は次のとおりです。

  • MATCH p = ACYCLIC <path_pattern> を使用します。
  • クエリで IS_ACYCLIC(p) フィルタを適用する

次のクエリでは MATCH p = ACYCLIC PATH_PATTERN を使用しています。

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN DISTINCT dst.id AS dst;

結果

dst
7
16

送金の最初のアカウントを確認するには、次のクエリを実行します。

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})(-[:Transfers]->
  (nexts:Account)){1,3}(dst:Account)
RETURN dst.id AS dst, ARRAY_AGG(DISTINCT nexts[0].id) AS unique_starts;

このクエリは、nexts を使用して結果を取得する量化パス内に新しい変数を導入するため、従来型ではありません。パス変数を使用すると、クエリを簡素化できます。

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id AS dst, ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts;

NODES(p) を使用すると、パス上のすべてのノードが返されます。最初のノード アカウントが start として指定されているため、次のアカウント(最初のオフセット)が、資金が最初に移行されるアカウントになります。

結果

dst unique_starts
7 16、7

パスは、複数の量化パスがある場合に便利です。start から見つかったパスがアカウント ID 7 を通過する必要があるという制約を追加できます。

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id AS dst,
  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts;

MATCH ステートメントは変更されましたが、クエリの残りの部分は変更する必要はありません。パス変数を使用しないと、検査する量化パスを Spanner が静的に把握できない場合があります。

パス変数を使用すると、すべての転送の合計を取得できます。

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
LET all_transfers = EDGES(p)
LET transfer_amounts = SUM(all_transfers.amount)
RETURN dst.id AS dst,
  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS participating_neighbor_nodes, transfer_amounts;

結果

dst participating_neighbor_nodes transfer_amounts
16 7 600
16 7 800

次のステップ