Spanner Graph 查询概览

本文档介绍了如何在 Spanner Graph 中查询属性图。通过 本部分中的示例将使用您在 设置和查询 Spanner Graph, 如下图所示:

Spanner 图架构示例。

运行 Spanner Graph 查询

您可以通过以下方式运行 Spanner 图查询:

Spanner 图查询结构

本部分详细介绍了每个查询组成部分。

以下示例展示了 Spanner 图的基本结构 查询。

Spanner 图查询结构示例。

借助 Spanner Graph,您可以在数据库中创建多个图表。 该查询首先使用 GRAPH 子句指定目标图 FinGraph

图表模式匹配

图表模式匹配可在图表中查找特定模式。最基本的模式是元素模式(节点模式和边模式),用于匹配图元素(分别是节点和边)。元素模式可以组合成路径模式和更复杂的模式。

节点模式

节点模式是与图中的节点匹配的模式。此模式 包含一对匹配的圆括号, 包含图表模式变量、标签表达式和属性过滤条件。

查找所有节点

以下查询会返回图中的所有节点。变量 n(称为图模式变量)会绑定到匹配的节点。在此例中,节点 模式匹配图中的所有节点。

GRAPH FinGraph
MATCH (n)
RETURN LABELS(n) AS label, n.id;

结果

该查询会返回 labelid,如下所示:

标签 id
账号 7
账号 16
账号 20
人物 1
人物 2
人物 3

查找具有特定标签的所有节点

以下查询会匹配图中具有 Person label 的所有节点。查询返回匹配节点的 labelid 以及 name 属性。

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

结果

标签 id name
人物 1 Alex
人物 2 Dana
人物 3 Lee

查找与标签表达式匹配的所有节点

您可以使用一个或多个逻辑运算符创建标签表达式

以下查询将匹配图表中具有 PersonAccount 标签。属性的集合 图表模式变量 n 是由 具有 PersonAccount 标签的节点。

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 图中的所有边都是有向的,但您可以在查询中使用 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

路径模式

路径模式由交替的节点模式和边模式构建而成。

使用路径模式查找具有指定标签和属性过滤条件的节点的所有路径

以下查询可查找从某个账号发起到该账号的所有转账 所有者为 Personid 等于 2

每个匹配结果都表示从 Person {id: 2} 通过连接的 Account(使用 Owns 边)到另一个 Account(使用 Transfers 边)的路径。

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

量化路径模式

量化模式允许在指定范围内重复模式。

匹配量化的边缘模式

以下查询可查找一到三个目标账号 从Accountid等于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。对于 如需了解详情,请参阅访问权限组变量

示例结果中的某些行会重复,因为同一对 srcdst 账号之间可能存在多个匹配该模式的路径。

匹配量化路径模式

以下查询查找 Account 节点之间具有一到两个的路径 Transfers边缘通过被屏蔽的中间账号。

括号中的路径模式会被量化,并且括号中会使用 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(在量化模式内)中的单个边。
  • 绑定到 ARRAY_LENGTH(e) 中边缘元素数组的组变量 在 RETURN 语句中(量化模式之外)。
  • 绑定到边缘元素数组的组变量,该数组由量化模式之外的 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

任意和任意最短路径

要限制每组路径中共享相同来源的匹配路径, 对于目标节点,您可以使用 ANYANY SHORTEST 路径搜索前缀。 您只能在整个路径模式之前应用这些前缀,而不能在括号内应用。

使用 ANY 进行匹配

以下查询可查找符合查询条件(只有 1-2 个)的所有唯一账号 距离给定 Account 节点 Transfers

ANY 路径搜索前缀可确保仅返回一对唯一的 srcdst Account 节点之间的一条路径。在以下示例中,虽然您可以通过从源 Account 节点出发的两条不同路径使用 {id: 16} 到达 Account 节点,但结果中仅包含一条路径。

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

图表模式

图模式由一个或多个路径模式组成,以英文逗号 , 分隔。图模式可以包含 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)
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;

下表说明了如何传递中间结果 语句。为简洁起见,仅显示了中间结果的部分属性。

语句 中间结果(缩写)
MATCH
  (src_account:Account)
    -[transfer:Transfers]->
  (dst_account:Account)
WHERE dst_account.is_blocked
src_account 转移 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 转移 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 转移 dst_account
{id: 7} {amount: 300.0} {id: 16, is_blocked: true}

MATCH
  (src_account:Account)
    <-[owns:Owns]-
  (owner:Person)
src_account 转移 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 将多个线性图表查询语句连在一起 关键字。第一个线性查询语句的输入为空。每个线性查询语句的输出都会成为下一个线性查询语句的输入。

以下示例将查找收到最多来电的账号的所有者 将多个线性图语句链接在一起进行传输。请注意,您可以在多个线性语句中使用相同的变量(在此示例中为 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

函数和表达式

您可以使用 GoogleSQL 中的所有函数、运算符和条件, 包括聚合函数 和其他标量函数。

Spanner Graph 还支持图元素的内置函数和运算符。

内置函数和运算符

以下函数运算符 通常用在 GQL 中:

  • 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_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 标签 账号 位置 个人
false true 人物 ["Vacation Fund"] 澳大利亚阿德莱德 {&quot;identifier&quot;:&quot;mUZpbkdyYXBoLlBlcnNvbgB4kQI=&quot;,&quot;kind&quot;:&quot;node&quot;,&quot;labels&quot;:[&quot;Person&quot;],&quot;properties&quot;:{&quot;birthday&quot;:&quot;1991-12-21T08:00:00Z&quot;,&quot;city&quot;:&quot;Adelaide&quot;,&quot;country&quot;:&quot;Australia&quot;,&quot;id&quot;:1,&quot;name&quot;:&quot;Alex&quot;}}

子查询

子查询是嵌套在另一个查询中的查询。以下列出了 Spanner 图子查询规则:

  • 子查询包含在一对大括号 {} 中。
  • 子查询可能以前导 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 图子查询

查询参数

您可以使用参数查询 Spanner 图。如需了解详情,请参阅语法,并了解如何在 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)
  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

后续步骤

了解调整查询的最佳实践