パスを操作する

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

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

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

グラフパスを作成する

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

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

MATCH p = PATH_PATTERN

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

次の例のクエリは、FinGraph 内のアカウント間の送金パターンを見つけます。

GRAPH FinGraph
MATCH p = (src:Account {id: 16})-[t:Transfers]->{2}(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)-[t:Transfers]->{2}(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)-[t:Transfers]->{2}(dst:Account)
RETURN IS_ACYCLIC(p) AS is_acyclic_path,
       ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids;

結果

is_acyclic_path account_ids
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)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN IS_TRAIL(p) AS is_trail_path,
       ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

結果

is_trail_path transfer_ids
FALSE 16,20,16
TRUE 7,16,20
TRUE 7,16,20

パスモード

Spanner Graph でのデフォルトの動作では、ノードやエッジが繰り返されるパスを含むすべてのパスが返されます。次のパスモードを使用して、ノードやエッジが繰り返されるパスとエッジのパスを含めるか除外します。詳細なセマンティクスについては、パスモードのドキュメントをご覧ください。

WALK

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

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

GRAPH FinGraph
MATCH p = WALK (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

結果

transfer_ids
16,20,16
7,16,20
7,16,20

ACYCLIC

ACYCLIC パスモードでは、ノードの繰り返しがあるパスが除外されます。

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

GRAPH FinGraph
MATCH p = ACYCLIC (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids

結果

account_ids
16,20,7
20,7,16
20,7,16
7,16,20
7,16,20

TRAIL

TRAIL パスモードでは、エッジの繰り返しがあるパスが除外されます。

次のクエリは、定量化されたパスパターンTRAIL パスモードを使用する方法を示しています。エッジの繰り返しがあるパスは除外されます。

GRAPH FinGraph
MATCH p = TRAIL (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

結果

transfer_ids
7,16,20
7,16,20

パス検索の接頭辞

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

ANY SHORTEST

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

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

GRAPH FinGraph
MATCH p = ANY SHORTEST (a:Account {is_blocked:true})-[t:Transfers]->{1,4}(b:Account)
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

次のステップ