Spanner Graph クエリの概要

このドキュメントでは、Spanner Graph でプロパティ グラフにクエリを実行する方法について説明します。このセクションの例では、Spanner Graph を設定してクエリを実行するで作成したグラフスキーマを使用します。例を次の図に示します。

Spanner Graph スキーマの例。

Spanner Graph クエリを実行する

Spanner Graph クエリは次の方法で実行できます。

Spanner Graph クエリの結果を可視化する

クエリが完全なノードを JSON 形式で返す場合は、Spanner Studio で Spanner Graph クエリの結果を可視化できます。詳細については、Spanner Graph の可視化を操作するをご覧ください。

Spanner Graph クエリの構造

このセクションでは、各クエリ コンポーネントについて詳しく説明します。

次の例は、Spanner Graph クエリの基本構造を示しています。

Spanner Graph クエリ構造の例。

Spanner Graph では、データベース内に複数のグラフを作成できます。クエリは、GRAPH 句を使用してターゲット グラフ FinGraph を指定することから始まります。

グラフパターン マッチング

グラフパターン マッチングによって、グラフ内の特定のパターンが検出されます。最も基本的なパターンは要素パターン(ノードパターンとエッジパターン)で、グラフ要素(ノードとエッジ)と一致します。要素パターンは、パスパターンやより複雑なパターンに構成できます。

ノードパターン

ノードパターンは、グラフ内のノードと一致するパターンです。このパターンは、一致するかっこのペアで構成されます。必要に応じて、グラフパターン変数、ラベル式、プロパティ フィルタを含めることがあります。

すべてのノードを検索する

次のクエリは、グラフ内のすべてのノードを返します。グラフパターン変数と呼ばれる変数 n は、一致するノードにバインドされます。この場合、ノードパターンはグラフ内のすべてのノードと一致します。

GRAPH FinGraph
MATCH (n)
RETURN LABELS(n) AS label, n.id;

結果

このクエリは、次のように labelid を返します。

ラベル id
アカウント 7
アカウント 16
アカウント 20
人物 1
人物 2
人物 3

特定のラベルを持つすべてのノードを検索する

次のクエリは、Person ラベルを持つグラフ内のすべてのノードと一致します。このクエリは、一致したノードの labelidname の各プロパティを返します。

GRAPH FinGraph
MATCH (p:Person)
RETURN LABELS(p) AS label, p.id, p.name;

結果

ラベル id name
人物 1 Alex
人物 2 Dana
人物 3 Lee

ラベル式に一致するすべてのノードを検索する

1 つ以上の論理演算子を使用してラベル式を作成できます。

次のクエリは、Person ラベルまたは Account ラベルを持つグラフ内のすべてのノードと一致します。グラフパターン変数 n によって公開されるプロパティのセットは、Person または Account ラベルを持つノードによって公開されるプロパティのスーパーセットです。

GRAPH FinGraph
MATCH (n:Person|Account)
RETURN LABELS(n) AS label, n.id, n.birthday, n.create_time;
  • 結果では、すべてのノードに id プロパティがあります。
  • Account ラベルに一致するノードには create_time プロパティがありますが、birthday プロパティはありません。このようなノードの birthday プロパティには NULL が返されます。
  • Person ラベルに一致するノードには birthday プロパティがありますが、create_time プロパティはありません。このようなノードの create_time プロパティには NULL が返されます。

結果

ラベル id birthday create_time
アカウント 7 NULL 2020-01-10T14:22:20.222Z
アカウント 16 NULL 2020-01-28T01:55:09.206Z
アカウント 20 NULL 2020-02-18T13:44:20.655Z
人物 1 1991-12-21T08:00:00Z NULL
人物 2 1980-10-31T08:00:00Z NULL
人物 3 1986-12-07T08:00:00Z NULL

ラベル式のルールの詳細については、ラベル式をご覧ください。

ラベル式とプロパティ フィルタに一致するすべてのノードを検索する

次のクエリは、Person ラベルが付いていて、プロパティ id1 に等しいグラフ内のすべてのノードと一致します。

GRAPH FinGraph
MATCH (p:Person {id: 1})
RETURN LABELS(p) AS label, p.id, p.name, p.birthday;

結果

ラベル id name birthday
人物 1 Alex 1991-12-21T08:00:00Z

WHERE 句を使用すると、ラベルとプロパティに対してより複雑なフィルタ条件を作成できます。

次のクエリは、Person ラベルが付いていて、プロパティ birthday1990-01-10 の前に存在するグラフ内のすべてのノードと一致します。

GRAPH FinGraph
MATCH (p:Person WHERE p.birthday < '1990-01-10')
RETURN LABELS(p) AS label, p.name, p.birthday;

結果

ラベル name birthday
人物 Dana 1980-10-31T08:00:00Z
人物 Lee 1986-12-07T08:00:00Z

エッジパターン

エッジパターンは、ノード間のエッジまたは関係に一致します。エッジパターンは角かっこ [] で囲まれ、記号 --><- は方向を示します。

ノードパターンと同様に、グラフパターン変数は一致するエッジ要素にバインドするために使用されます。

一致するラベルを持つすべてのエッジを検索する

次のクエリは、Transfers ラベルを持つグラフ内のすべてのエッジを返します。グラフパターン変数 e は、一致するエッジにバインドされます。

GRAPH FinGraph
MATCH -[e:Transfers]->
RETURN e.Id as src_account, e.order_number

結果

src_account order_number
7 304330008004315
7 304120005529714
16 103650009791820
20 304120005529714
20 302290001255747

ラベル式とプロパティ フィルタに一致するすべてのエッジを検索する

次のクエリに示すように、エッジパターンでは、ノードパターンと同様に、ラベル式、プロパティ仕様、WHERE 句を使用できます。このクエリは、指定された order_number に一致する Transfers というラベルが付けられたすべてのエッジを検索します。

GRAPH FinGraph
MATCH -[e:Transfers {order_number: "304120005529714"}]->
RETURN e.Id AS src_account, e.order_number

結果

src_account order_number
7 304120005529714
20 304120005529714

任意の方向のエッジパターンを使用してすべてのエッジを検索する

Spanner Graph 内のすべてのエッジは有向ですが、クエリで any direction エッジパターン -[]- を使用すると、エッジをどちらの方向であっても照合できます。

次のクエリは、ブロックされた口座が関与するすべての送金を検索します。

GRAPH FinGraph
MATCH (account:Account)-[transfer:Transfers]-(:Account {is_blocked:true})
RETURN transfer.order_number, transfer.amount;

結果

order_number amount
304330008004315 300
304120005529714 100
103650009791820 300
302290001255747 200

パスパターン

パスパターンは、ノードパターンとエッジパターンを交互に組み合わせて作成されます。

パスパターンを使用して、指定されたラベルとプロパティのフィルタを持つノードからのすべてのパスを検索する

次のクエリは、id2 に等しい Person が所有する口座から開始された、ある口座へのすべての送金を見つけます。

一致した各結果は、Person {id: 2} から、Owns エッジを使用して接続された Account を経由し、Transfers エッジを使用して別の Account に至るパスを表します。

GRAPH FinGraph
MATCH
  (p:Person {id: 2})-[:Owns]->(account:Account)-[t:Transfers]->
  (to_account:Account)
RETURN
  p.id AS sender_id, account.id AS from_id, to_account.id AS to_id;

結果

sender_id from_id to_id
2 20 7
2 20 16

定量化されたパスパターン

定量化されたパターンを使用すると、指定した範囲内でパターンを繰り返すことができます。

定量化されたエッジパターンを照合する

次のクエリは、id7 に等しい送金元 Account から 1~3 回送金したすべての送金先口座を検索します(自身を除く)。

数量詞 {1, 3} を接尾辞として追加したエッジパターン。

GRAPH FinGraph
MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account)
WHERE src != dst
RETURN src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length, dst.id AS dst_account_id;

結果

src_account_id path_length dst_account_id
7 1 16
7 1 16
7 1 16
7 3 16
7 3 16
7 2 20
7 2 20

上の例では、ARRAY_LENGTH 関数を使用して group variable e にアクセスしています。詳細については、アクセス グループ変数をご覧ください。

サンプル結果の一部は重複しています。これは、同じ src 口座と dst 口座のペアの間に、パターンに一致するパスが複数存在する可能性があるためです。

定量化されたパスパターンを照合する

次のクエリは、ブロックされている中間口座を経由する 1~2 つの Transfers エッジを持つ Account ノード間のパスを見つけます。

かっこで囲まれたパスパターンは定量化され、かっこ内で WHERE 句を使用して繰り返しパターンの条件を指定します。

GRAPH FinGraph
MATCH
  (src:Account)
  ((a:Account)-[:Transfers]->(b:Account {is_blocked:true}) WHERE a != b){1,2}
    -[:Transfers]->(dst:Account)
RETURN src.id AS src_account_id, dst.id AS dst_account_id;

結果

src_account_id dst_account_id
7 20
7 20
20 20

変数をグループ化する

定量化されたパターンで宣言されたグラフパターン変数は、定量化されたパターンの外部でアクセスされるとグループ変数と見なされ、一致したグラフ要素の配列にバインドされます。

グループ変数は配列としてアクセスできます。配列には、グラフ要素が一致したパスに沿って出現する順番で保持されます。グループ変数は、水平集計を使用して集計できます。

グループ変数にアクセスする

次の例では、変数 e は次のようにアクセスされます。

  • WHEREe.amount > 100 の単一エッジにバインドされたグラフパターン変数(定量化されたパターン内)。
  • RETURN ステートメントの ARRAY_LENGTH(e) のエッジ要素配列にバインドされたグループ変数(定量化されたパターンの外部)。
  • エッジ要素の配列にバインドされたグループ変数。これは、定量化されたパターンの外部で SUM(e.amount) で集計されます。これは水平集計の例です。
GRAPH FinGraph
MATCH
  (src:Account {id: 7})-[e:Transfers WHERE e.amount > 100]->{0,2}
  (dst:Account)
WHERE src.id != dst.id
LET total_amount = SUM(e.amount)
RETURN
  src.id AS src_account_id, ARRAY_LENGTH(e) AS path_length,
  total_amount, dst.id AS dst_account_id;

結果

src_account_id path_length total_amount dst_account_id
7 1 300 16
7 2 600 20

任意のパスと任意の最短パス

同じ送信元ノードと送信先ノードを共有するパスの各グループで一致するパスを制限するには、ANY または ANY SHORTEST パスの検索接頭辞を使用します。これらの接頭辞は、パスパターン全体の前にのみ適用できます。かっこ内には適用できません。

ANY を使用して照合する

次のクエリは、特定の Account ノードから 1 つまたは 2 つの Transfers 離れた、到達可能な一意の口座をすべて検索します。

ANY パス検索接頭辞を使用すると、固有の src ノードと dst Account ノードのペア間のパスが 1 つだけ返されます。次の例では、送信元 Account ノードから 2 つの異なるパスで {id: 16} を使用して Account ノードに到達できますが、結果には 1 つのパスのみが含まれます。

GRAPH FinGraph
MATCH ANY (src:Account {id: 7})-[e:Transfers]->{1,2}(dst:Account)
LET ids_in_path = ARRAY_CONCAT(ARRAY_AGG(e.Id), [dst.Id])
RETURN src.id AS src_account_id, dst.id AS dst_account_id, ids_in_path;

結果

src_account_id dst_account_id ids_in_path
7 16 7,16
7 20 7,16,20

グラフパターン

グラフパターンは、カンマ(,)で区切られた 1 つ以上のパスパターンで構成されます。グラフパターンには WHERE 句を含めることが可能です。これにより、パスパターン内のすべてのグラフパターン変数にアクセスして、フィルタリング条件を形成できます。各パスパターンは、パスのコレクションを生成します。

グラフパターンを使用して照合する

次のクエリは、送信元口座からブロックされた口座に送金される、200 を超える金額の取引に関与する仲介口座とその所有者を特定します。

次のパスパターンがグラフパターンを形成します。

  • 最初のパターンは、中間アカウントを使用してある口座からブロック中の口座に送金が発生するパスを検索します。
  • 2 つ目のパターンは、口座からその所有者までのパスを検索します。

変数 interm は、2 つのパスパターンの間の共通リンクとして機能します。そのため、interm は両方のパスパターンで同じ要素ノードを参照する必要があります。これにより、interm 変数に基づく等結合オペレーションが作成されます。

GRAPH FinGraph
MATCH
  (src:Account)-[t1:Transfers]->(interm:Account)-[t2:Transfers]->(dst:Account),
  (interm)<-[:Owns]-(p:Person)
WHERE dst.is_blocked = TRUE AND t1.amount > 200 AND t2.amount > 200
RETURN
  src.id AS src_account_id, dst.id AS dst_account_id,
  interm.id AS interm_account_id, p.id AS owner_id;

結果

src_account_id dst_account_id interm_account_id owner_id
20 16 7 1

リニアクエリ ステートメント

複数のグラフ ステートメントを連結して、リニアクエリ ステートメントを作成できます。このステートメントは、クエリに表示されている順序で実行されます。

  • 各ステートメントは、前のステートメントの出力を入力として受け取ります。最初のステートメントの入力は空です。
  • 最後のステートメントの出力が最終結果です。

ブロック中の口座への最大送金額を確認する

次のクエリは、ブロックされた口座への送金額が最も大きい口座とその所有者を検索します。

GRAPH FinGraph
MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account {is_blocked:true})
ORDER BY transfer.amount DESC
LIMIT 1
MATCH (src_account:Account)<-[owns:Owns]-(owner:Person)
RETURN src_account.id AS account_id, owner.name AS owner_name;

次の表に、中間結果がステートメントに沿ってどのように渡されるかを示します。簡潔にするため、中間結果の一部のプロパティのみを示しています。

ステートメント 中間結果(省略)
MATCH
  (src_account:Account)
    -[transfer:Transfers]->
  (dst_account:Account {is_blocked:true})
src_account transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}
{id: 7} {amount: 100.0} {id: 16, is_blocked: true}
{id: 20} {amount: 200.0} {id: 16, is_blocked: true}

ORDER BY transfer.amount DESC
src_account transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}
{id: 20} {amount: 200.0} {id: 16, is_blocked: true}
{id: 7} {amount: 100.0} {id: 16, is_blocked: true}

LIMIT 1
src_account transfer dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}

MATCH
  (src_account:Account)
    <-[owns:Owns]-
  (owner:Person)
src_account transfer dst_account owns owner
{id: 7} {amount: 300.0} {id: 16, is_blocked: true} {person_id: 1, account_id: 7} {id: 1, name: Alex}

RETURN
  src_account.id AS account_id,
  owner.name AS owner_name
account_id owner_name
7 Alex

結果

account_id owner_name
7 Alex

RETURN ステートメント

RETURN ステートメントは、一致したパターンから返す内容を定義します。グラフパターン変数にアクセスし、式や ORDER_BY、GROUP_BY などの句を含めることができます。RETURN ステートメントをご覧ください。

Spanner Graph では、グラフ要素をクエリ結果として返すことはできません。グラフ要素全体を返すには、TO_JSON 関数または SAFE_TO_JSON 関数を使用します。これらの 2 つの関数のうち、SAFE_TO_JSON を使用することをおすすめします。

グラフ要素を JSON として返す

GRAPH FinGraph
MATCH (n:Account {id: 7})
-- Returning a graph element in the final results is NOT allowed. Instead, use
-- the TO_JSON function or explicitly return the graph element's properties.
RETURN TO_JSON(n) AS n;
GRAPH FinGraph
MATCH (n:Account {id: 7})
-- Certain fields in the graph elements, such as TOKENLIST, can't be returned
-- in the TO_JSON function. In those cases, use the SAFE_TO_JSON function instead.
RETURN SAFE_TO_JSON(n) AS n;

結果

n
{"identifier":"mUZpbkdyYXBoLkFjY291bnQAeJEO","kind":"node","labels":["Account"],"properties":{"create_time":"2020-01-10T14:22:20.222Z","id":7,"is_blocked":false,"nick_name":"Vacation Fund"}}

NEXT キーワードを使用した大規模なクエリの作成

NEXT キーワードを使用して、複数のグラフのリニアクエリ ステートメントを連結できます。最初のリニアクエリ ステートメントへの入力は空です。各リニアクエリ ステートメントの出力が、その次のリニアクエリ ステートメントの入力になります。

次の例では、複数のグラフのリニア ステートメントを連結して、送金を受け取った回数が最も多い口座の所有者を検索します。この例では同じ変数 account を使用して、複数のリニア ステートメントで同じグラフ要素を参照できます。

GRAPH FinGraph
MATCH (:Account)-[:Transfers]->(account:Account)
RETURN account, COUNT(*) AS num_incoming_transfers
GROUP BY account
ORDER BY num_incoming_transfers DESC
LIMIT 1

NEXT

MATCH (account:Account)<-[:Owns]-(owner:Person)
RETURN account.id AS account_id, owner.name AS owner_name, num_incoming_transfers;

結果

account_id owner_name num_incoming_transfers
16 Lee 3

関数と式

Spanner Graph クエリでは、すべての GoogleSQL 関数(集計関数とスカラー関数の両方)、演算子条件式を使用できます。Spanner Graph は、グラフ固有の関数と演算子もサポートしています。

組み込み関数と演算子

GQL では、次の関数演算子がよく使用されます。

  • PROPERTY_EXISTS(n, birthday): nbirthday プロパティを公開しているかどうかを返します。
  • LABELS(n): グラフスキーマで定義されている n のラベルを返します。
  • PROPERTY_NAMES(n): n のプロパティ名を返します。
  • TO_JSON(n): n を JSON 形式で返します。詳細については、TO_JSON 関数をご覧ください。

PROPERTY_EXISTS 述語、LABELS 関数、TO_JSON 関数、および ARRAY_AGGCONCAT などの他の組み込み関数。

GRAPH FinGraph
MATCH (person:Person)-[:Owns]->(account:Account)
RETURN person, ARRAY_AGG(account.nick_name) AS accounts
GROUP BY person

NEXT

RETURN
  LABELS(person) AS labels,
  TO_JSON(person) AS person,
  accounts,
  CONCAT(person.city, ", ", person.country) AS location,
  PROPERTY_EXISTS(person, is_blocked) AS is_blocked_property_exists,
  PROPERTY_EXISTS(person, name) AS name_property_exists
LIMIT 1;

結果

is_blocked_property_exists name_property_exists labels accounts location person
false true 人物 ["Vacation Fund"](有給休暇積立金) アデレード(オーストラリア) {"identifier":"mUZpbkdyYXBoLlBlcnNvbgB4kQI=","kind":"node","labels":["Person"],"properties":{"birthday":"1991-12-21T08:00:00Z","city":"Adelaide","country":"Australia","id":1,"name":"Alex"}}

サブクエリ

サブクエリは、別のクエリにネストされたクエリです。以下に、Spanner Graph のサブクエリのルールを示します。

  • サブクエリは中かっこ {} で囲まれます。
  • サブクエリは、スコープ内のグラフを指定するために、先頭に GRAPH 句を付けることがあります。指定されたグラフは、外側のクエリで使用されているグラフと同じである必要はありません。
  • サブクエリで GRAPH 句が省略されている場合、次のようになります。
    • スコープ内のグラフは、最も近い外側のクエリ コンテキストから推論されます。
    • サブクエリは、MATCH. を含むグラフパターン マッチング ステートメントから開始する必要があります。
  • サブクエリのスコープの外部で宣言されたグラフパターン変数は、サブクエリ内で再度宣言することはできませんが、サブクエリ内の式または関数で参照できます。

サブクエリを使用して、各口座からの合計送金数を確認する

次のクエリは、VALUE サブクエリの使用を示しています。サブクエリは、VALUE キーワードの接頭辞が付いた中かっこ {} で囲まれています。このクエリは、口座から開始された送金の合計回数を返します。

GRAPH FinGraph
MATCH (p:Person)-[:Owns]->(account:Account)
RETURN p.name, account.id AS account_id, VALUE {
  MATCH (a:Account)-[transfer:Transfers]->(:Account)
  WHERE a = account
  RETURN COUNT(transfer) AS num_transfers
} AS num_transfers;

結果

name account_id num_transfers
Alex 7 2
Dana 20 2
Lee 16 1

サポートされているサブクエリ式の一覧については、Spanner Graph のサブクエリをご覧ください。

クエリ パラメータ

Spanner Graph にはパラメータを指定してクエリを実行できます。詳細については、構文をご覧ください。また、Spanner クライアント ライブラリでパラメータを指定してデータをクエリする方法もご覧ください。

次のクエリは、クエリ パラメータの使用を示しています。

GRAPH FinGraph
MATCH (person:Person {id: @id})
RETURN person.name;

グラフとテーブルを同時にクエリする

グラフクエリを SQL と組み合わせて使用すると、1 つのステートメントでグラフとテーブルの両方の情報にアクセスできます。

GRAPH_TABLE

GRAPH_TABLE 演算子はリニアグラフ クエリを受け取り、その結果を表形式で返します。この結果は SQL クエリにシームレスに統合できます。この相互運用性により、グラフ以外のコンテンツでグラフクエリの結果を拡充できます。その逆も同様です。

たとえば、次の例に示すように、CreditReports テーブルを作成し、いくつかの信用報告書を挿入できます。

CREATE TABLE CreditReports (
  person_id     INT64 NOT NULL,
  create_time   TIMESTAMP NOT NULL,
  score         INT64 NOT NULL,
) PRIMARY KEY (person_id, create_time);
INSERT INTO CreditReports (person_id, create_time, score)
VALUES
  (1,"2020-01-10 06:22:20.222", 700),
  (2,"2020-02-10 06:22:20.222", 800),
  (3,"2020-03-10 06:22:20.222", 750);

次に、GRAPH_TABLE のグラフ パターン マッチングで関心のある人物を特定し、グラフクエリの結果を CreditReports テーブルと結合してクレジット スコアにアクセスします。

SELECT
  gt.person.id,
  credit.score AS latest_credit_score
FROM GRAPH_TABLE(
  FinGraph
  MATCH (person:Person)-[:Owns]->(:Account)-[:Transfers]->(account:Account {is_blocked:true})
  RETURN DISTINCT person
) AS gt
JOIN CreditReports AS credit
  ON gt.person.id = credit.person_id
ORDER BY credit.create_time;

結果:

person_id latest_credit_score
1 700
2 800

次のステップ

クエリをチューニングするためのベスト プラクティスを確認する。