本文档介绍了如何在 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 查询结果
如果查询以 JSON 格式返回完整节点,您可以在 Spanner Studio 中直观呈现 Spanner Graph 查询结果。如需了解详情,请参阅使用 Spanner Graph 可视化图表。
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 |
查找与标签表达式匹配的所有节点
您可以创建包含一个或多个逻辑运算符的标签表达式。
以下查询会匹配图表中具有 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 |
边缘模式
边缘模式用于匹配节点之间的边缘或关系。边缘模式用英文方括号 []
括起来,并使用符号 -
、->
或 <-
表示方向。
与节点模式类似,图表模式变量用于绑定到匹配边缘元素。
查找具有匹配标签的所有边缘
以下查询会返回图中具有 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 | 金额 |
---|---|
304330008004315 | 300 |
304120005529714 | 100 |
103650009791820 | 300 |
302290001255747 | 200 |
路径模式
路径模式由交替的节点模式和边缘模式构建而成。
使用路径模式,通过指定的标签和属性过滤条件查找节点的所有路径
以下查询会查找从 Person
所拥有且 id
等于 2
的账号发起的所有针对账号的资金转移。
每个匹配的结果都表示一条路径,该路径从 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 |
量化路径模式
量化模式允许在指定范围内重复某个模式。
匹配量化边缘模式
以下查询会查找与来源 Account
(其中 id
等于 7
)相距一到三次资金转移(不包括它本身)的所有目标账号。
以限定符 {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
账号之间可能存在多条符合相应模式的路径。
匹配量化路径模式
以下查询会查找 Account
节点之间经过被屏蔽的中间账号且包含 1 到 2 条 Transfers
边缘的路径。
带英文圆括号的路径模式会进行量化,并且 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
的访问方式如下所示:
- 一个图形模式变量,该变量绑定到
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
节点之间仅返回一条路径。在以下示例中,虽然您可以从来源节点 Account
通过两条不同路径到达具有 {id: 16}
的 Account
节点,但结果仅包含一条路径。
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 |
图表模式
图表模式由一个或多个以英文逗号 (,
) 分隔的路径模式组成。图表模式可以包含 WHERE
子句,该子句可让您访问路径模式中的所有图表模式变量,以构成过滤条件。每个路径模式都会生成一个路径集合。
使用图表模式进行匹配
以下查询可识别超过 200 的交易金额中涉及的中间账号及其所有者,资金通过这些账号从来源账号转移到被屏蔽账号。
以下路径模式构成了该图形模式:
- 第一种模式会查找使用中间账号将资金从一个账号转移到被屏蔽账号的路径。
- 第二种模式会查找从账号到其所有者的路径。
变量 interm
充当两个路径模式之间的通用链接,这需要 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;
下表展示了中间结果如何沿着各个语句进行传递。为简洁起见,仅显示中间结果的部分属性。
Statement | 中间结果(缩写) | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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 |
|
结果
account_id | owner_name |
---|---|
7 | Alex |
return 语句
返回语句定义要从匹配的模式返回的内容。它可以访问图表模式变量,包含表达式和其他子句(例如 ORDER_BY、GROUP_BY)。请参阅 RETURN
语句。
Spanner Graph 不支持以查询结果的形式返回图表元素。如需返回整个图表元素,请使用 TO_JSON
函数或 SAFE_TO_JSON
函数。在这两个函数中,我们建议您使用 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 还支持图形专用函数和运算符。
内置函数和运算符
PROPERTY_EXISTS(n, birthday)
:返回n
是否公开birthday
属性。LABELS(n)
:返回图表架构中定义的n
的标签。PROPERTY_NAMES(n)
:返回n
的属性名称。TO_JSON(n)
:返回 JSON 格式的n
。如需了解详情,请参阅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 | 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 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 结合使用,以便在单个语句中同时访问图表和表中的信息。
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 |
后续步骤
了解对查询进行调优的最佳实践。