このドキュメントでは、Spanner Graph でプロパティ グラフにクエリを実行する方法について説明します。このセクションの例では、Spanner Graph を設定してクエリを実行するで作成したグラフスキーマを使用します。例を次の図に示します。
Spanner Graph クエリを実行する
Spanner Graph クエリは次の方法で実行できます。
Google Cloud コンソール
[Spanner Studio] ページでクエリを送信します。[Spanner Studio] ページにアクセスするには、[Database overview] ページまたは [Table overview] ページで [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 |
人物 | 1 |
人物 | 2 |
人物 | 3 |
特定のラベルを持つすべてのノードを検索する
次のクエリは、Person
ラベルを持つグラフ内のすべてのノードと一致します。このクエリは、一致したノードの label
、id
、name
の各プロパティを返します。
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 | 誕生日 | 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
ラベルが付いていて、プロパティ id
が 1
に等しいグラフ内のすべてのノードと一致します。
GRAPH FinGraph
MATCH (p:Person {id: 1})
RETURN LABELS(p) AS label, p.id, p.name, p.birthday;
結果
ラベル | id | name | 誕生日 |
---|---|---|---|
人物 | 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 | 誕生日 |
---|---|---|
人物 | Dana | 1980-10-31T08:00:00Z |
人物 | 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 のすべてのfunctions(集計関数とスカラー関数の両方)、演算子、条件式を使用できます。Spanner Graph は、グラフ固有の関数と演算子もサポートしています。
組み込み関数と演算子
GQL では、次のfunctionsと演算子がよく使用されます。
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 | ラベル | アカウント | ロケーション | 人物 |
---|---|---|---|---|---|
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 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 |
次のステップ
クエリをチューニングするためのベスト プラクティスを確認する。