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),然后将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
- 点赞
- 收藏
- 关注作者
评论(0)