このドキュメントでは、Spanner Graph でプロパティグラフにクエリを実行する方法について説明します。このセクションの例では、Spanner Graph を設定してクエリを実行するで作成したグラフスキーマを使用します。例を次の図に示します。
Spanner Graph クエリを実行する
Spanner Graph クエリは次の方法で実行できます。
Google Cloud コンソール
[Spanner Studio] ページでクエリを送信します。[Spanner Studio] ページにアクセスするには、[データベースの概要] ページまたは [テーブルの概要] ページで [Spanner Studio] をクリックします。Spanner Studio へのアクセスの詳細については、Google Cloud コンソールを使用してデータを管理するをご覧ください。
gcloud spanner
コマンドライン ツールgcloud spanner databases execute-sql
コマンドを使用してコマンドを送信します。executeSql
とexecuteStreamingSql
REST APIExecuteSql
とExecuteStreamingSql
RPC API
Spanner Graph のクエリ構造
このセクションでは、各クエリ コンポーネントについて詳しく説明します。
次の例は、Spanner Graph クエリの基本構造を示しています。
Spanner Graph を使用すると、データベース内に複数のグラフを作成できます。クエリは、GRAPH
句を使用してターゲット グラフ FinGraph
を指定することから始まります。
グラフパターン マッチング
グラフパターン マッチングは、グラフ内の特定のパターンを検出します。最も基本的なパターンは、グラフ要素(ノードとエッジ)に一致する要素パターン(ノードパターンとエッジパターン)です。要素パターンは、パスパターンやより複雑なパターンに構成できます。
ノードパターン
ノードパターンは、グラフのノードに一致するパターンです。このパターンは、一致するかっこのペアで構成されます。必要に応じて、グラフパターン変数、ラベル式、プロパティ フィルタを含めることができます。
すべてのノードを検索する
次のクエリは、グラフ内のすべてのノードを返します。グラフ パターン変数と呼ばれる変数 n
は、一致するノードにバインドされます。この場合、ノードパターンはグラフ内のすべてのノードに一致します。
GRAPH FinGraph
MATCH (n)
RETURN LABELS(n) AS label, n.id;
結果
このクエリは、次のように label
と id
を返します。
ラベル | id |
---|---|
アカウント | 7 |
アカウント | 16 |
アカウント | 20 |
Person | 1 |
Person | 2 |
Person | 3 |
特定のラベルを持つすべてのノードを検索する
次のクエリは、Person
ラベルを持つグラフ内のすべてのノードと一致します。このクエリは、一致したノードの label
、id
、name
プロパティを返します。
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
ラベルが付いていて、プロパティ id
が 1
に等しいグラフ内のすべてのノードを照合します。
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
ラベルが付いていて、プロパティ birthday
が 1990-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 |
パスパターン
パスパターンは、ノードパターンとエッジパターンを交互に配置して作成されます。
パスパターンを使用して、指定されたラベルとプロパティのフィルタを持つノードからのすべてのパスを検索する
次のクエリは、id
が 2
に等しい 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 |
定量化されたパスパターン
量化パターンを使用すると、指定した範囲内でパターンを繰り返すことができます。
定量化されたエッジパターンを照合する
次のクエリは、id
が 7
に等しい送金元 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
は次のようにアクセスされます。
WHERE
句e.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 |
|
||||||||||||
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 |
|
結果
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 は、グラフ要素の組み込み関数と演算子もサポートしています。
組み込み関数と演算子
PROPERTY_EXISTS(n, birthday)
:n
がbirthday
プロパティを公開しているかどうかを返します。LABELS(n)
: グラフ スキーマで定義されているn
のラベルを返します。PROPERTY_NAMES(n)
:n
のプロパティ名を返します。TO_JSON(n)
:n
を JSON 形式で返します。詳細については、TO_JSON
関数をご覧ください。
次のクエリは、PROPERTY_EXISTS
述部、LABELS
関数、TO_JSON
関数、および ARRAY_AGG
や CONCAT
などの他の組み込み関数を示しています。
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 |
次のステップ
クエリをチューニングするためのベスト プラクティスを確認する。