Spanner Graph クエリの概要

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

Spanner Graph スキーマの例。

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
Person 1
Person 2
Person 3

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

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

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

結果

ラベル id name
Person 1 Alex
Person 2 Dana
Person 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 誕生日 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
Person 1 1991-12-21T08:00:00Z NULL
Person 2 1980-10-31T08:00:00Z NULL
Person 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 誕生日
Person 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 誕生日
Person Dana 1980-10-31T08:00:00Z
Person Lee 1986-12-07T08:00:00Z

エッジパターン

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

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

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

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

GRAPH FinGraph
MATCH -[e:Owns]->
RETURN e.id AS owner_id, e.account_id;

結果

owner_id account_id
1 7
3 16
2 20

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

ノードパターンと同様に、エッジパターンでは、次のクエリに示すように、ラベル式、プロパティ仕様、WHERE 句を使用できます。このクエリは、指定された期間に Owns というラベルが付けられ、プロパティ create_time を持つすべてのエッジを見つけます。

GRAPH FinGraph
MATCH -[e:Owns WHERE e.create_time > '2020-01-14'
                 AND e.create_time < '2020-05-14']->
RETURN e.id AS owner_id, e.create_time, e.account_id;

結果

owner_id create_time account_id
2 2020-01-28T01:55:09.206Z 20
3 2020-02-18T13:44:20.655Z 16

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

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

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

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

結果

order_number 金額
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)
  ((:Account)-[:Transfers]->(interm:Account) WHERE interm.is_blocked){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(SELECT e.to_id FROM UNNEST(e) AS e)
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 16
7 20 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)
WHERE dst_account.is_blocked
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;

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

Statement 中間結果(省略)
MATCH
  (src_account:Account)
    -[transfer:Transfers]->
  (dst_account:Account)
WHERE dst_account.is_blocked
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 関数を使用します。

グラフ要素を 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;

結果

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 ラベル アカウント location person
false true Person [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 SUM(transfer.amount) AS total_transfer
} AS total_transfer;

結果

name account_id total_transfer
Alex 7 400
Dana 20 700
Lee 16 300

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

クエリ パラメータ

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

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

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

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

Graph クエリを 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)
  WHERE account.is_blocked
  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

次のステップ

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