GQL图查询语言语法概览

举报
蜉蝣与海 发表于 2025/03/30 19:28:05 2025/03/30
【摘要】 2024年4月,图查询语言标准 GQL(Graph Query Language)发布(ISO/IEC 39075:202),这是继 ISO 发布第一版 SQL 37年后第二个数据库查询语言标准。笔者发现,目前网上对GQL相关语句的示例比较少,于是读了GQL标准文档,并试着写了一些符合GQL标准语法的语句,与大家分享。

2024年4月,国际标准化组织(ISO)与国际电工委员会(IEC)共同发布了编号为ISO/IEC 39075:202的图查询语言标准 GQL(Graph Query Language),这是继 ISO 发布第一版 SQL 37年后第二个数据库查询语言标准。目前,国际关联数据基准委员会(Linked Data Benchmark Council,以下简称LDBC)官网上已经发布了GQL相关的语法解析工具和基于antlr的文法文件。预计在GQL的影响下,图厂商会加强对GQL的支持力度,开发者后续进行图分析会更加方便。

笔者发现,目前网上对GQL相关语句的示例比较少,于是读了GQL标准文档,并试着写了一些符合GQL标准语法的语句,与大家分享。希望后面有越来越多的介绍GQL的相关文章,一方面让开发者了解图查询语言,进而了解图可以解决什么问题;另一方面,让图厂商了解GQL标准,推动GQL更快的普及。

注1:本文涉及的所有GQL语句,都在https://opengql.github.io/editor/上解析通过。GQL除了语法规则,还存在一部分语义约束,部分语句符合语法规则,但可能会违反语义约束,请读者自行甄别。
注2:本文只是GQL语法功能和应用场景探讨,不涉及某个特性在某个具体图产品上的具体实现讨论。

GQL语法介绍

本文集中对GQL的图管理、数据修改、数据查询、表达式这几部分给出语句示例,涵盖标准的12-20章的内容,每一部分会先尝试描述支持的特性,然后给出语句示例。
下列特性本文只是部分涉及,不做重点描述:

  • Session管理 (第7章)和事务(第8章)
  • 复杂引用类型和字面量(Literal)规则(17章、21章)
  • 语句返回状态和诊断(23章)
  • Binding Variable Definition Block(第9章)

图管理语句

图管理语句描述集中在标准的第12章,GQL提供了对图SCHEMA管理、图管理和图上点边类型管理三部分内容。

SCHEMA管理

SCHEMA管理(GC01)允许一个图数据库实例创建一个或多个SCHEMA,一个SCHEMA下可以创建多个图,不同的SCHEMA实现资源的隔离,从语义上支持逻辑多租。
创建一个图SCHEMA,使用下列语句:

CREATE SCHEMA IF NOT EXISTS /catelog/one_schema_name

其中if not exists是一个可选的语法(GC02)。
删除图SCHEMA,使用下列语句:

DROP SCHEMA IF EXISTS /catelog/one_schema_name

图管理

GQL同时兼容强类型图(Closed Graph Type,如GES)和弱类型图(Open Graph Type,如Neo4j),因此创图语句也同时支持两种类型的图。
创图-弱类型图

CREATE OR REPLACE GRAPH /catelog/one_schema_name/graph_name TYPED ANY

或者

CREATE GRAPH /catelog/one_schema_name/graph_name ::ANY

GQL中支持使用typed关键字或者::关键字指定类型。
创图-强类型图

CREATE GRAPH /catelog/one_schema_name/graph_name :: one_graph_type

或者在创图语句定义类型(GG03):

CREATE GRAPH graph_name TYPED graph {
  (customer:Customer => {id::STRING, name::STRING}),
  (account:Account => {no1 TYPED STRING, acct_type TYPED STRING }),
(customer)-[:HOLDS]->(account),
(account)-[:TRANSFER {amount::INTEGER}]->(account)
}

此外,还支持引用其他图类型(GG04),以及创图时拷贝其他图类型的数据:

CREATE GRAPH graph_name LIKE another_graph AS COPY OF another_graph

其中AS COPY OF another_graph是可选语法项(GG05),表示数据拷贝,而like关键字(GG04)是表示的图上点边类型信息的拷贝。
删除图的逻辑比较简单,示例如下:

DROP GRAPH IF NOT EXISTS /catelog/schema_name/graph_name

其中IF NOT EXISTS是可选语法项(GC05)。

Graph Type管理

在GQL中,对于Closed Graph Type,提供了一套定义点边类型的能力。
这里有三点值得关注:

  • 对点边类型,GQL认为点边的类型不一定等价于点边的Label(GG02),即在某些情况下,类型可以表示为一组label sets和一组属性的定义的集合。
  • GQL对无向边提供了支持(GH02),通过波浪线可以定义某条类型的边是无向边。
  • 如果启用了特性GG26, 不同type下的同名属性,允许支持不同类型。

一个典型的定义Graph Type的语句如:

CREATE GRAPH TYPE IF NOT EXISTS one_graph_type AS {
  (account:Account => {no1::STRING, acct_type::STRING }),
  (account)-[:Transfer {amount::INTEGER}]->(account),
  (account)~[:Transfer2 {amount::INTEGER}]~(account)
}

这里Account和Transfer都是Label名。
如果Account和Transfer是类型名,要这么写:

CREATE GRAPH TYPE IF NOT EXISTS one_graph_type AS {
 NODE Account(account{id::STRING, acct_type::STRING }),
 DIRECTED EDGE Transfer{amount::INTEGER} CONNECTING (account -> account),
 UNDIRECTED EDGE Transfer2{amount::INTEGER} CONNECTING (account ~ account)
}

如果图中类型信息包含了一组label信息(如支持可选特性GG02的图),语句估计会这么写:

CREATE GRAPH TYPE IF NOT EXISTS one_graph_type AS {
 NODE Account(account:AccountA&AccountB{id::STRING, acct_type::STRING }),
 DIRECTED EDGE Transfer:Transfer{amount::INTEGER} CONNECTING (account -> account),
 UNDIRECTED EDGE Transfer2:Transfer2{amount::INTEGER} CONNECTING (account ~ account)
}

此外,也支持拷贝其他的graph_type,如:

CREATE GRAPH TYPE one_graph_type AS COPY OF graph_or_external_reference;
CREATE GRAPH TYPE one_graph_type LIKE like_graph;
CREATE OR REPLACE GRAPH TYPE one_graph_type LIKE like_graph;

删除Graph Type语法比较简单:

DROP GRAPH TYPE IF EXISTS /catelog/one_graph_type

数据修改语句

数据修改语句集中在第13章,和Cypher差异不大,这里只给出示例,不做过多描述。

// insert
USE graph_name INSERT (n:SomeLabel{prop1:'value1'})
INSERT (n:Account)-[r:Transfer{amount:10}]->(m:Account)
// set
MATCH (n:LabelA) WHERE element_id(n)='' SET n.prop='value1', n:LabelA, n =  {p1:'v1',p2:'v2'}
// remove
MATCH (n:LabelA) WHERE element_id(n)='' REMOVE n.prop, n:LabelA
// delete
MATCH p=(n)--(m) DELETE n,m
MATCH p=(n)--(m) DELETE nodes(p)

官方文档中,还有delete subQuery的语义(GD03),因为之前没有类似的概念参考,所以这里不进行举例。

数据查询语句

在数据查询语句部分(第14章),GQL扩展了较多的能力,将文法文件读下来,感受有几点:

  • 支持语句像电路一样串并联形成执行流水线,支持临时结果集,以及更灵活的子查询逻辑
  • 支持多种路径模式,对查询意图的表达能力更强
  • 支持变长路径(Quantified paths)以及嵌套变长路径查询,对时序图比较友好
  • 支持SELECT关键字,使用SQL风格的文法查询图数据库

在开始介绍之前,首先枚举一下GQL涉及的查询类子句,以及Cypher中类似语义的子句或者关键字,方便了解Cypher的同学快速入门。

GQL子句 Cypher子句 备注
match match 能力增强
optional optional GQL支持optional后跟子查询
filter/where where 能力类似,区别在表达式的不同
let with 能力类似
for unwind GQL支持输出元素下标值
order by order by GQL支持自定义null值排序时优先级
limit/offset limit/offset 能力类似
return return 能力类似, GQL支持显式的group by子句
select GQL独有的SQL风格的子句,语义上支持多图联合查询
union/except/otherwise … union 能力增强
yield yield 能力类似

语句流水线

Q1
Q2
Q3
Q4

经常使用图查询来进行业务开发的同学可能会有这种需求,需要将同一个语句的结果(如Q1)作为输入同时给多条语句(如Q2和Q3),然后将Q2和Q3的结果进行进一步加工处理,GQL提供了类似的能力,可以将若干语句像电路一样进行串联和并联,形成流水线执行。其中关键字如下:

  • 并联:
    • UNION(GQ03)
    • OTHERWISE(GQ02)
    • EXCEPT(GQ04,GQ05)
    • INTERSECT(GQ07)
  • 串联:
    • NEXT(GQ20)

例如下面的语句中,通过next和except关键字,检索了某个person的好友喜欢评论的帖子,都分布在哪些标签下,其中这些帖子不包含person自己早于$date时间创作的帖子。

MATCH (person)-[:KNOWS]->{2}(friend) WHERE element_id(n)=$personId RETURN person, friend
NEXT YIELD person, friend
MATCH (friend)<-[:HAS_CREATOR]-(comment:Comment)-[:REPLY_OF]->(post:Post) RETURN post
EXCEPT ALL
MATCH (person)<-[:HAS_CREATOR]-(post:Post) WHERE post.creationDate < $date0 RETURN post
MEXT YIELD post
MATCH (post)<-[:HAS_TAG]-(tag)
RETURN tag.name, count(*)

虽然Cypher也能表达相同的语义,但是,要么子查询会写的很复杂,要么受限于语句文法,会有重复遍历或者数据膨胀的问题。
从这一点上看,语句流水线这个特性,能大大缓解一部分Cypher做星型查询或者长链路查询时,由于查询路径上某一类点结果特别多,造成的数据膨胀以及反复遍历的问题。

Match子句

match子句GQL的描述,Neo4j官网已经介绍了很多内容。这里主要围绕四个特性进行介绍:

  • 遍历模式
  • 可变长路径
  • 路径拼接
  • 点边pattern和label过滤

遍历模式
GQL提供了match mode和path mode/search mode两类遍历模式,其中match mode只能写在每个match关键字后,path mode/search mode可以写在每条路径前,或者match语句后接的keep语句中,这里简要列一下文档中的语法规则。

<simple match statement> ::= MATCH <graph pattern binding table>
<graph pattern binding table> ::= <graph pattern> [ <graph pattern yield clause> ]
<graph pattern yield clause> ::= YIELD <graph pattern yield item list>
<graph pattern yield item> ::=
	<element variable reference>
	| <path variable reference>
<graph pattern> ::=
	[ <match mode> ] <path pattern list>
 	[ <keep clause> ]
 	[ <graph pattern where clause> ]
<path pattern list> ::=
	<path pattern> [ { <comma> <path pattern> }... ]
<path pattern> ::=
	[ <path variable declaration> ] [ <path pattern prefix> ] <path pattern expression>

提炼其中关于遍历模式相关的语法规则,简化后大概为:

<graph pattern> ::=
	[ <match mode> ]
    [ <path variable declaration> ] [ <path pattern prefix> ] <path pattern expression> 
    [ { <comma> [ <path variable declaration> ] [ <path pattern prefix> ] <path pattern expression> }... ]
    [ KEEP <path pattern prefix> ]
    [ <graph pattern where clause> ]

其中关于match mode和path pattern prefix,有如下规则:

<match mode> ::=
	<repeatable elements match mode>
	| <different edges match mode>
<path pattern prefix> ::=
	<path mode prefix>
	| <path search prefix>
<path mode prefix> ::=
	<path mode> [ <path or paths> ]
<path mode> ::=
	WALK
	| TRAIL
	| SIMPLE
	| ACYCLIC
<path search prefix> ::=
	<all path search>
	| <any path search>
	| <shortest path search>

关于Match Mode,可以看到起作用范围为整条match子句,有下列两种模式,根据模式名称可以大概猜出其含义:

模式名称 含义 特性编号
DIFFERENT EDGES 要求match后同一条路径中不能含有相同的边 G002
REPEATABLE ELEMENTS 允许路径中出现重复元素 G003

其中Note 220提到了当match mode为DIFFERENT EDGES时,似乎是对Match后跟的path pattern数量有约束,即如果路径中出现selective path pattern,则当match mode为DIFFERENT EDGES时,path pattern list中只能有这一条path pattern。
这里给出match mode相关的例句:

MATCH DIFFERENT EDGES (n)-[r]->(m),(m)-[r1]->(s) WHERE element_id(n)='1'  RETURN count(*)
MATCH REPEATABLE ELEMENTS (n)-[r]->(m),(m)-[r1]->(s) WHERE element_id(n)='1'  return count(*)

关于path mode四种模式,其含义在聊聊超级快的图上多跳过滤查询文章中已经提到过,这里不再赘述。
至于path search prefix,可以用来约束查询数量以及是否优先返回最短路。
下面给出这些模式相关的例句:

编号 特性描述 例句
G010 Explicit WALK keyword MATCH p=WALK (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G011 Advanced path modes: TRAIL MATCH p=TRAIL (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G012 Advanced path modes: SIMPLE MATCH p=SIMPLE (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G013 Advanced path modes:ACYCLIC MATCH p=ACYCLIC (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G015 All path search: explicit ALL keyword” MATCH p=ALL WALK (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G016 Any path search MATCH p=ANY 5 TRAIL (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G017 All shortest path search MATCH p=ALL SHORTEST (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G018 Any shortest path search MATCH p=ANY SHORTEST (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G019 Counted shortest path search match p=SHORTEST 5 (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G020 Counted shortest group search match p=SHORTEST 5 GROUP (n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) WHERE element_id(n)='1' RETURN p
G006 Graph pattern KEEP clause: path mode prefix match p=(n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) KEEP WALK WHERE element_id(n)='1' RETURN p
G007 Graph pattern KEEP clause: path search prefix match p=(n)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(s:Post) KEEP ANY SHORTEST 5 WHERE element_id(n)='1' RETURN p

这里有一个shortest group的概念(G020),其含义是将不同长度的路径分组,例如SHORTEST 3 GROUP就是将所有最短路、所有次短路以及除了最短路和次短路外的最短路返回,相关概念neo4j也解释的很好,可以参阅Neo4j - Shortest paths。

可变长路径
与Cypher不同,GQL提供可不一样的变长路径参数,相关例句Neo4j官网也有解释:

编号 特性名 例句
G035 Quantified paths MATCH (:Station {name: 'Peckham Rye'})-[link:LINK]-(:Station {name: 'Clapham Junction'}){1,3} return count(*)
G036 Quantified edges MATCH (:Station {name: 'Peckham Rye'})-[link:LINK]-{1,3}(:Station {name: 'Clapham Junction'}) return count(*)

这里{1,3}还可以替换成*{3}+?这样其他的表达方式,分别表示不限定跳数、固定跳数,至少1跳,存在性验证这样的场景。
这种改写方式,对时序图会更友好,想象一个列车中转的场景,要查询两班列车能否接续中转,可以用下列的写法:

MATCH (:Station)<-[:CALLS_AT]-(d:Stop)
      ((s:Stop)-[:NEXT0]->(f:Stop) WHERE s.time0 > f.time0){1,3}
      (a:Stop)-[:CALLS_AT]->(:Station)
RETURN d.departs AS departureTime, a.arrives AS arrivalTime

这里通过嵌套路径(G038)的where s.time0 > f.time0表达了路径上节点关系之间的强时序约束,可以用于微博转发分析、疫情传播分析等场景。在具体实现时,相关约束可以下推到遍历过程中。如果cypher要表达类似的语义,至少需要在所有路径产生后,使用ListComprehension语义和range枚举下标结合才能完成,且难以下沉到遍历过程中完成。
此外match语句的相关规则中,对无向边匹配也有相关描述,这里举个例子:

MATCH (a:Station)~[:CALLS_AT]~(d:Stop WHERE d.time0 > a.time0) RETURN d
MATCH (a:Station)~[:CALLS_AT]~>(d:Stop WHERE d.time0 > a.time0) RETURN d
MATCH (a:Station)<~[:CALLS_AT]~(d:Stop WHERE d.time0 > a.time0) RETURN d

此外还有些路径简写的例子,笔者没有做深入分析,只写几条通过了parser的示例语句:

编号 特性名 例句
G047 Relaxed topological consistency: concise edge patterns MATCH p=<-[:R]--[:S]- RETURN p
G044 Basic abbreviated edge patterns MATCH (a:Station)->(d:Stop) return d

路径拼接
GQL还支持对路径进行拼接,以及路径中使用|,以及|+|操作符,这里直接给例子:
G030 Path multiset alternation:

MATCH (p:Person)-[:LIVES_IN]->(c:City)|+|(p:Person)-[:LOCATED_IN]->(c:Place) RETURN p

GQ032 Path pattern union:

MATCH (p:Person)-[:LIVES_IN]->(c:City)|(p:Person)-[:LOCATED_IN]->(c:Place) RETURN p

以及查询结果的路径拼接(GE06):

match p=(a)->(b),p1=(b)->(d) return p||p1
match (a)-[r]->(b),(b)-[r1]->(d) return PATH[a,r,b,r1,d]

点边pattern和label过滤
在GQL中,点边的属性过滤支持两种模式:

特性名 例句
element pattern where clause MATCH (a:Station)<-[:CALLS_AT]-(d:Stop where d.time0 > 1) RETURN d
element property specification MATCH (a:Station)<-[:CALLS_AT]-(d:Stop{time0:1}) RETURN d

显然使用where表达过滤,能力会更为强大,当然,相关过滤条件也可以写在后面的where子句中。
此外label支持若干复杂的与或非操作,示例如下:

MATCH (a:Station|Stop) return a
MATCH (a:Station&Stop) return a
MATCH (a:(Station&Stop)|OtherLabel) return a
match (a:%) return a

这里%是wildcard label(G074), match (a:%) return a其含义是匹配有label的点,也就是在G074特性启用后,支持判断点上是否有label。

let子句

Let子句(GQ09)与cypher的with子句类似,这里只给一个示例:

MATCH (n) LET gender = n.gender RETURN gender

for子句

for子句与cypher的unwind类似,表示遍历某个可以遍历的结构,与unwind不同的是,for提供了一种类似于python中enumerate函数的用法,可以同时给出元素下标和索引值。
在官方文档中也同步说明,for语句中可以嵌套一个临时表结构,这里试着给出示例:
GQ10 FOR statement: list value support

MATCH (n:LabelA) WHERE n.age < 10 LET v=collect_list(n) FOR s IN v RETURN s 

GQ24 FOR statement: WITH OFFSET

MATCH (n:LabelA) WHERE n.age < 10 LET v=collect_list(n) 
FOR s IN v WITH OFFSET index 
RETURN index, s 

GQ23 FOR statement: binding table support

TABLE userActivity = {
     MATCH (u:User)-[a:ACTION]->()
     RETURN u.id AS userId, a.ts AS timestamps
 }
 FOR u IN userActivity RETURN u.userId, u.timestamps

GQ11 FOR statement: WITH ORDINALITY

TABLE userActivity = {
     MATCH (u:User)-[a:ACTION]->()
     RETURN u.id AS userId, a.ts AS timestamps
 }
 FOR u IN userActivity WITH ORDINALITY index 
 RETURN index,userId, timestamps

排序和分页子句

排序和分页相关语义与Cypher差异不大,GQL支持了排序时定义null的优先级(GA03),这里给出一个示例:

MATCH (n) RETURN n.name AS name ORDER BY name ASC NULLS FIRST, n.date0 DESC NULLS LAST

也同步给一个limit和offset的示例:

MATCH (n) RETURN n SKIP 10 LIMIT 10

return子句

GQL的结果返回子句中,除了支持order by之外,还可以支持group by子句(GQ15),其他和Cypher差别不大,这里试着写一个sql风格的group by:

MATCH (n) LET type0=n.type0, name = n.name RETURN count(*) GROUP BY type0

select子句

在GQL文档中,提供了SQL风格的select子句,但是奇怪的是,select子句是在focused linear query statement语法规则下,而如果GQL没用启用GQ01 Use grpah相关的规则,focused linear query statement不应出现在GQL语句中,也就是说,select子句的特性是一组可选特性,但是select也可以有不显式声明使用某个graph的写法。
另一个奇怪的点在于,GQL提供了临时表的语法,但是select的语法规则中,其后必须跟一个match子句或者是嵌套子查询,而不能用来操作临时表,这就和SQL+Graph的设计理念有一些冲突。
先来看select的具体规则:

<select statement> ::=
SELECT [ <set quantifier> ] { <asterisk> | <select item list> }
     [ <select statement body>
     [ <where clause> ]
     [ <group by clause> ]
     [ <having clause> ]
     [ <order by clause> ]
     [ <offset clause> ] [ <limit clause> ] ]
<select item list> ::=
	<select item> [ { <comma> <select item> }... ]
<select item> ::=
	<aggregating value expression> [ <select item alias> ]
<select item alias> ::=
	AS <identifier>
<having clause> ::=
	HAVING <search condition>
<select statement body> ::=
	FROM { <select graph match list> | <select query specification> }
<select graph match list> ::=
	<select graph match> [ { <comma> <select graph match> }... ]
<select graph match> ::=
	<graph expression> <match statement>
<select query specification> ::=
	<nested query specification>
	| <graph expression> <nested query specification>

这样很容易写出一条符合语法规则的语句:

SELECT n.name AS name, n.age AS age FROM CURRENT_GRAPH MATCH (n:LabelA) WHERE n.score > 10 ORDER BY age OFFSET 10 LIMIT 10

但是如果用来操作临时表,就得这么写:

TABLE userActivity = {
     MATCH (u:User)-[a:ACTION]->()
     RETURN u.id AS userId, a.ts AS timestamps
 }
 SELECT userId, ts FROM 
 {
 	FOR u IN userActivity 
    RETURN u.userId AS userId, u.timestamps AS ts
 }
 where ts > 2

可以看到显得有些鸡肋,虽然下面的写法也能parse成功,但是总感觉少了些什么:

TABLE userActivity = {
     MATCH (u:User)-[a:ACTION]->()
     RETURN u.id AS userId, a.ts AS timestamps
 }
 SELECT userId, ts FROM 
 {
 	FOR u IN userActivity 
 }
 where ts > 2

此外select也能使用group by和having,这里给个例子,做例子时没有做到只在group by子句中出现group key,可能在具体语义上会有些问题:

SELECT n.gender AS gender,count(*) AS count0 FROM CURRENT_GRAPH 
MATCH (n:LabelA) WHERE n.score > 10 
GROUP BY gender having count0 > 10

表达式

GQL的表达式主要在第20章,总体上继承了Cypher的风格,但是在一些表现形式上有差异。与Cypher相比,其做的好的点有:

  • 支持判断值的类型
  • 支持显示类型转换
  • 支持一系列值拼接语法,内置了丰富的谓词

感觉缺失的点有:

  • 缺少访问列表下标相关语义(也可能是笔者没读到,毕竟collect_first函数、for子句都有了)
  • 缺少一些原生的、与返回格式相关的关键字(毕竟都有table、graph这样的显式值类型了)

表达式就不展开描述了,只讲下做的好的几个点:

值类型判断

GQL中支持判断值类型(GA06),相关的类型有15种,这里给出14种类型判断的语句,至于判断某个类型是否为Graph,笔者实在是不知道怎么用GQL构造一个语义上很清晰的Graph对象,暂时不给出示例。

类型 关联特性编号 示例语句
BOOL/BOOLEAN VALUE a = true RETURN a IS TYPED BOOL, a IS ::BOOL
String/CHAR/VARCHAR VALUE a = 'hello' RETURN a IS TYPED STRING, a IS ::VARCHAR
BYTES/BINARY/VARBINARY GV35 VALUE v::BYTES=X'AFDEAA' RETURN v IS TYPED BYTES, v IS::VARBINARY
Numeric VALUE a = 1 RETURN a IS TYPED INT32, a IS ::INT64
Temporal GV39 VALUE a = date('2025-03-28') RETURN a IS ::DATE
Duration GV41 VALUE a = DURATION({hours:1, minutes:1,seconds:1}) RETURN a IS ::DURATION(YEAR TO MONTH), a IS ::DURATION(DAY TO SECOND)
Immaterial GV71,GV72 VALUE a = null RETURN a IS ::NOTHING, a IS ::NULL
Node MATCH (n) RETURN n IS ::NODE, n IS ::VERTEX
Edge MATCH (n)-[r]->() RETURN r IS ::EDGE, r IS ::RELATIONSHIP
Path GV55 MATCH p=(a)->(b) RETURN p IS ::PATH
List GV50 VALUE a = [1,2,3,4] RETURN a IS ::LIST, a IS ::List<int>, a IS ::ARRAY
Record GV45 VALUE s = {name:'s', age:10} RETURN s IS ::RECORD,s IS ::RECORD{name::STRING, age::INTEGER}

Dynamic Union Type(GV67):

VALUE a = 1 RETURN a is ::int|bool

Binding Table Type(GV61,GE02):

TABLE userActivity = {
     MATCH (u:User)-[a:ACTION]->()
     RETURN u.id AS userId, a.ts AS timestamps
 }
 RETURN userActivity IS ::BINDING Table{userId::INT, timestamps::TIMESTAMP}

支持显示类型转换

GQL支持下列语法,可以显示转换类型:

RETURN CAST(1 as INT64)

支持一系列拼接语法,内置丰富的谓词

GQL支持字符串、list和path等对象的拼接,如:

RETURN "aaa"||"bbb"
MATCH p=(a)->(b),p1=(b)->(d) RETURN p||p1
MATCH [1,2,3,4]||[5,6,7,8]

也内置了一系列丰富的谓词,如:

MATCH (n)-[r]->(m) RETURN n IS SOURCE OF r,m IS DESTINATION OF r
MATCH (n) WHERE n.id=1 LET a=n  WHERE EXISTS{(a)-->()} RETURN a

其他资料和困惑

除了上述介绍的语法特征,GQL还支持事务管理、session管理,语句状态和诊断,以及将子查询的结果绑定到某个表或者某个变量上(GQ18),这些后面再聊。

有点疑惑的点在于,GQL支持表和图作为变量的类型,但是有下面的矛盾:

  • 文档中给出了定义临时表的语法,处理临时表的语法却很少(只有一个for语句)
  • 文档中给出了处理图的语法,但是没有给出定义一个临时图的语法

也就是说文档里没有显示的标明,在Binding Variable Definition Block中,如何通过子查询来定义一个图,而不是表。虽然可以用value关键字将某个子查询绑定到图上,但是这种绑定依赖的是等号左边声明的图类型,而非语句本身结果就是一个图,例如下列语句能过GQL的解析器,但是却不一定有实际含义,等号右侧不一定是一个图,如果右侧可以是一个图,也是因为等号左侧声明了GRAPH userActivity

GRAPH userActivity = value {
     MATCH (u:User)-[a:ACTION]->()
     RETURN u,a
 } 
 return userActivity 

相反表的定义就很自然,毕竟处理结果总是能整理成一张表的,不过GQL中处理表的文法就有点鸡肋了,select后的from子句还无法直接跟表的名称。这块等后面有时间再讨论。

在github OPENGQL 代码仓中,给出了GQL的文法,以及解析工具,还有railroad图,本文的所有示例都是在代码仓的GQL Editor中解析通过的,毕竟解析通过的,才至少"看起来是正确的”。

另外,文中所有的GV45,GC02,G002这样的编号,都是GQL官方文档中可选特性的特性编号,可以直接对号入座方便了解和学习。

参考文档(需要将&&替换为//,$$替换为/,@替换为.):

1.LDBC open-source GQL tools: https:&&ldbcouncil@org$$pages/opengql-announce/
2.ISO/IEC 39075:2024 GQL: https:&&www.iso@org$$standard/76120.html
3.Neo4j GQL conformance: https:&&neo4j@com$$docs/cypher-manual/current/appendix/gql-conformance/
4.Neo4j - Shortest paths: https:&&neo4j@com$$docs/cypher-manual/current/patterns/shortest-paths/
5.GQL overview https: &&cloud.google@com$$spanner/docs/reference/standard-sql/graph-intro
6.What is the database language GQL?: https:&&www.linkedin@com$$posts/jtc1news_gql-database-language-standard-isoiec-39075-activity-7186398287963279360-E6rV?utm_source=share&utm_medium=member_desktop
7.Open GQL: https:&&github@com$$opengql

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。