本文档介绍了如何在 Spanner 图中查询属性图。本部分中的示例使用您在设置和查询 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 |
用户 | 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 |
查找与标签表达式匹配的所有节点
您可以使用一个或多个逻辑运算符创建标签表达式。
以下查询会匹配图中具有 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 |
边缘模式
边缘模式会匹配节点之间的边缘或关系。边缘模式用方括号 []
括起,并使用 -
、->
或 <-
符号来指明方向。
与节点模式类似,图形模式变量用于绑定到匹配的边缘元素。
查找所有标签匹配的边缘
以下查询会返回图中具有 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
相距 1 到 3 次转移(除自身以外),且 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
节点之间的路径,这些路径通过一个或两个 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
节点相距一个或两个 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 语句
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 | 用户 | [“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 |
后续步骤
了解调整查询的最佳实践。