nGQL(Nebula Graph Query Language)是Nebula Graph使用的的声明式图查询语言,支持灵活高效的图模式。
nGQL可以做什么
• 支持图遍历
• 支持模式匹配
• 支持聚合
• 支持修改图
• 支持访问控制
• 支持聚合查询
• 支持索引
• 支持大部分openCypher 9图查询语法
点用一对括号来描述,通常包含一个名称。例如:(a)
示例为一个简单的模式,描述了单个点,并使用变量a命名该点。
多个点通过边相连是常见的结构,模式用箭头来描述两个点之间的边。例如:(a)-[]->(b)
示例为一个简单的数据结构:两个点和一条连接两个点的边,两个点分别为a和b,边是有方向的,从a到b。
这种描述点和边的方式可以扩展到任意数量的点和边,例如:
(a)-[]->(b)<-[]-©
这样的一系列点和边称为路径(path)。
只有在涉及某个点时,才需要命名这个点。如果不涉及这个点,则可以省略名称,例如:(a)-[]->()<-[]-©
模式除了简单地描述图中的点之外,还可以描述点的Tag。例如:(a:User)-[]->(b)
模式也可以描述有多个Tag的点,例如:(a:User:Admin)-[]->(b)
openCypher兼容性
nGQL中的MATCH语句不支持用(a:User:Admin)匹配多个标签。如需匹配多标签可使用过滤条件,如WHERE “User” IN tags(n) AND “Admin” IN tags(n)。
点和边是图的基本结构。nGQL在这两种结构上都可以增加属性,方便实现更丰富的模型。
在模式中,属性的表示方式为:用花括号括起一些键值对,用英文逗号分隔。例如一个点有两个属性:
(a {name: ‘Andres’, sport: ‘Brazilian Ju-Jitsu’})
在这个点上可以有一条边是:(a)-[{blocked: false}]->(b)
描述一条边最简单的方法是使用箭头连接两个点。
可以用以下方式描述边以及它的方向性。如果不关心边的方向,可以省略箭头,例如:(a)-[]-(b)
和点一样,边也可以命名。一对方括号用于分隔箭头,变量放在两者之间。例如:(a)-[r]->(b)
和点上的Tag一样,边也可以有类型。描述边的类型,例如:(a)-[r:REL_TYPE]->(b)
和点上的Tag不同,一条边只能有一种Edge type。但是如果我们想描述多个可选Edge type,可以用管道符号(|)将可选值分开,例如:(a)-[r:TYPE1|TYPE2]->(b)
和点一样,边的名称可以省略,例如:(a)-[:REL_TYPE]->(b)
在图中指定边的长度来描述多条边(以及中间的点)组成的一条长路径,不需要使用多个点和边来描述。例如:(a)-[*2]->(b)
该模式描述了3点2边组成的图,它们都在一条路径上(长度为2),等价于: (a)-[]->()-[]->(b)
也可以指定长度范围,这样的边模式称为variable-length edges,例如:(a)-[*3…5]->(b)
3…5表示最小长度为3,最大长度为5。
该模式描述了4点3边、5点4边或6点5边组成的图。
也可以忽略最小长度,只指定最大长度,例如:(a)-[…5]->(b)
ps.必须指定最大长度,不支持仅指定最小长度((a)-[3…]->(b))或都不指定((a)-[]->(b))。
一系列连接的点和边称为路径。nGQL允许使用变量来命名路径,例如:p = (a)-[*3…5]->(b)
可以在MATCH语句中使用路径变量。
在Tag player的name属性和Edge type follow上创建索引 :
CREATE TAG INDEX name ON player(name(20));
CREATE EDGE INDEX follow_index ON follow();
重建索引使其生效:
REBUILD TAG INDEX name;
REBUILD EDGE INDEX follow_index
使用RETURN {
MATCH (v:player{name:"Tim Duncan"}) \
RETURN v;
MATCH (v:player{name:"Tim Duncan"})-[e]->(v2) \
RETURN e;
使用id()函数检索点ID:
MATCH (v:player{name:"Tim Duncan"}) \
RETURN id(v);
使用labels()函数检索点上的Tag列表:
MATCH (v:player{name:"Tim Duncan"}) \
RETURN labels(v);
使用labels(v)[0]检索第一个元素:
MATCH (v:player{name:"Tim Duncan"}) \
RETURN labels(v)[0];
使用RETURN {
MATCH (v:player{name:"Tim Duncan"}) \
RETURN v.age;
使用AS设置属性的别名:
MATCH (v:player{name:"Tim Duncan"}) \
RETURN v.age AS Age;
使用properties()函数检索点或边的所有属性:
MATCH p=(v:player{name:"Tim Duncan"})-[]->(v2) \
RETURN properties(v2);
使用type()函数检索匹配的Edge type:
MATCH p=(v:player{name:"Tim Duncan"})-[e]->() \
RETURN DISTINCT type(e);
使用RETURN
MATCH p=(v:player{name:"Tim Duncan"})-[*3]->() \
RETURN p;
使用nodes()函数检索路径中的所有点:
MATCH p=(v:player{name:"Tim Duncan"})-[]->(v2) \
RETURN nodes(p);
使用relationships()函数检索路径中的所有边:
MATCH p=(v:player{name:"Tim Duncan"})-[]->(v2) \
RETURN relationships(p);
使用length()函数检索路径的长度:
MATCH p=(v:player{name:"Tim Duncan"})-[*..2]->(v2) \
RETURN p AS Paths, length(p) AS Length;
nGQL提供引用符来表示WHERE和YIELD子句中的属性,或者复合查询中管道符之前的语句数据结果。
• $^: 引用起始点。
• $$: 引用目的点。
• $-: 引用复合查询中管道符之前的语句输出结果。
i) 返回起始点和目的点的年龄:
GO FROM "player100" OVER follow YIELD $^.player.age AS SrcAge, $$.player.age AS DestAge;
ii) 返回player100 follow的player的名称和团队:
GO FROM "player100" OVER follow \
YIELD follow._dst AS id | \
GO FROM $-.id OVER serve \
YIELD $^.player.name AS Player, $$.team.name AS Team;
MATCH语句提供基于模式(pattern)匹配的搜索功能。MATCH语句在模式中搜索,寻找匹配的边或点。
一个MATCH语句定义了一个搜索模式,用该模式匹配存储在Nebula Graph中的数据,然后用RETURN子句检索数据。
MATCH语句使用原生索引查找起始点或边,起始点或边可以在模式的任何位置。即一个有效的MATCH语句,必须有一个属性、Tag或Edge type已经创建索引,或者在WHERE子句中用id()函数指定了特定点的VID。
语法:MATCH [] RETURN
在一对括号中使用自定义变量来表示模式中的点。例如:(v)
在点的右侧用:
方法一.在Tag的右侧用{
MATCH (v:player{name:"Tim Duncan"}) RETURN v;
方法二.使用WHERE子句实现相同的操作:
MATCH (v:player) WHERE v.name == "Tim Duncan" RETURN v;
使用点ID去匹配点。id()函数可以检索点的ID:
MATCH (v) WHERE id(v) == 'player101' RETURN v;
要匹配多个点的ID,可以用WHERE id(v) IN [vid_list]:
MATCH (v:player { name: 'Tim Duncan' })--(v2) \
WHERE id(v2) IN ["player101", "player102"] RETURN v2;
使用–/-->/<–符号表示有边连接,并匹配这些边连接的点:
MATCH (v:player{name:"Tim Duncan"})--(v2) \
RETURN v2.name AS Name;
MATCH (v:player{name:"Tim Duncan"})-->(v2) \
RETURN v2.name AS Name;
扩展到更多的点和边:
MATCH (v:player{name:"Tim Duncan"})-->(v2)<--(v3) \
RETURN v3.name AS Name;
连接起来的点和边构成了路径,可以使用自定义变量命名路径:
MATCH p=(v:player{name:"Tim Duncan"})-->(v2) \
RETURN p;
除了用–、-->、<–表示未命名的边之外,还可以在方括号中使用自定义变量命名边。例如-[e]-:
MATCH (v:player{name:"Tim Duncan"})-[e]-(v2) \
RETURN e;
可以扩展模式,匹配路径中的多条边:
MATCH (v:player{name:"Tim Duncan"})-[]->(v2)<-[e:serve]-(v3) \
RETURN v2, v3;
和点一样,可以用:
MATCH ()-[e:follow]-() \
RETURN e;
使用|可以匹配多个Edge type:
MATCH (v:player{name:"Tim Duncan"})-[e:follow|:serve]->(v2) \
RETURN e;
用{
MATCH (v:player{name:"Tim Duncan"})-[e:follow{degree:95}]->(v2) \
RETURN e;
可以在模式中使用:
MATCH p=(v:player{name:"Tim Duncan"})-[e:follow*2]->(v2) \
RETURN DISTINCT v2 AS Friends;
可以在模式中使用:
MATCH p=(v:player{name:"Tim Duncan"})-[e:follow*1..3]->(v2) \
RETURN v2 AS Friends;
可以使用DISTINCT关键字聚合重复结果:
MATCH p=(v:player{name:"Tim Duncan"})-[e:follow*1..3]->(v2:player) \
RETURN DISTINCT v2 AS Friends, count(v2);
可以在变长或定长模式中指定多个Edge type。hop、minHop和maxHop对所有Edge type都生效:
MATCH p=(v:player{name:"Tim Duncan"})-[e:follow|serve*2]->(v2) \
RETURN DISTINCT v2;
LOOKUP根据索引遍历数据。可以使用LOOKUP实现如下功能:
• 根据WHERE子句搜索特定数据。
• 通过Tag列出点:检索指定Tag的所有点ID。
• 通过Edge type列出边:检索指定Edge type的所有边的起始点、目的点和rank。
• 统计包含指定Tag的点或属于指定Edge type的边的数量。
语法:
LOOKUP ON {<vertex_tag> | <edge_type>}
[WHERE <expression> [AND <expression> ...]]
[YIELD <return_list>];
返回Tag为player且name为Tony Parker的点:
CREATE TAG INDEX index_player ON player(name(30), age);
REBUILD TAG INDEX index_player;
LOOKUP ON player \
WHERE player.name == "Tony Parker" \
YIELD player.name, player.age;
返回Edge type为follow且degree为90的边:
CREATE EDGE INDEX index_follow ON follow(degree);
REBUILD EDGE INDEX index_follow;
LOOKUP ON follow \
WHERE follow.degree == 90 \
YIELD follow.degree;
如果需要通过Tag列出所有的点,或通过Edge type列出边,则Tag、Edge type或属性上必须有至少一个索引。例如一个Tag player有属性name和age,为了遍历所有包含Tag player的点ID,Tag player、属性name或属性age中必须有一个已经创建索引。
CREATE TAG player(name string,age int);
CREATE TAG INDEX player_index on player();
REBUILD TAG INDEX player_index;
INSERT VERTEX player(name,age) \
VALUES "player100":("Tim Duncan", 42), "player101":("Tony Parker", 36);
LOOKUP ON player;
CREATE EDGE like(likeness int);
CREATE EDGE INDEX like_index on like();
REBUILD EDGE INDEX like_index;
INSERT EDGE like(likeness) \
VALUES "player100"->"player101":(95);
LOOKUP ON like;
统计Tag为player的点和Edge type为like的边:
LOOKUP ON player |\
YIELD COUNT(*) AS Player_Number;
LOOKUP ON like | \
YIELD COUNT(*) AS Like_Number;
GO用指定的过滤条件遍历图,并返回结果。
语法:
GO [[<M> TO] <N> STEPS ] FROM <vertex_list>
OVER <edge_type_list> [{REVERSELY | BIDIRECT}]
[ WHERE <conditions> ]
[YIELD [DISTINCT] <return_list>]
[| ORDER BY <expression> [{ASC | DESC}]]
[| LIMIT [<offset_value>,] <number_rows>]
GO [[<M> TO] <N> STEPS ] FROM <vertex_list>
OVER <edge_type_list> [{REVERSELY | BIDIRECT}]
[ WHERE <conditions> ]
[| GROUP BY {col_name | expr | position} YIELD <col_name>]
• STEPS:指定跳数。如果没有指定跳数,默认值N为1。如果N为0,Nebula Graph不会检索任何边。
• M TO N STEPS:遍历M~N跳的边。如果M为0,输出结果和M为1相同,即GO 0 TO 2和GO 1 TO 2是相同的。
• :用逗号分隔的点ID列表,或特殊的引用符$-.id。
• :遍历的Edge type列表。
• REVERSELY | BIDIRECT:默认情况下检索的是的出边(正向),REVERSELY表示反向,即检索入边;BIDIRECT 为双向,即检索正向和反向,通过返回 ._type 字段判断方向,其正数为正向,负数为反向。
• WHERE :指定遍历的过滤条件。
• YIELD [DISTINCT] :指定输出结果。如果没有指定,默认返回目的点ID。
• ORDER BY:指定输出结果的排序规则。
• LIMIT:限制输出结果的行数。
• GROUP BY:根据指定属性的值将输出分组。
示例:
1)返回player102所属队伍:
GO FROM "player102" OVER serve;
2)返回距离player102两跳的朋友:
GO 2 STEPS FROM "player102" OVER follow;
3)添加过滤条件:
GO FROM "player100", "player102" OVER serve \
WHERE serve.start_year > 1995 \
YIELD DISTINCT $$.team.name AS team_name, serve.start_year AS start_year, $^.player.name AS player_name;
4)返回player100的入边:
方法一:
GO FROM "player100" OVER follow REVERSELY \
YIELD follow._dst AS destination;
方法二:
MATCH (v)<-[e:follow]- (v2) WHERE id(v) == 'player100' \
RETURN id(v2) AS destination;
5)查询player100的朋友和朋友所属队伍:
方法一:
GO FROM "player100" OVER follow REVERSELY \
YIELD follow._dst AS id | \
GO FROM $-.id OVER serve \
WHERE $^.player.age > 20 \
YIELD $^.player.name AS FriendOf, $$.team.name AS Team;
方法二:
MATCH (v)<-[e:follow]- (v2)-[e2:serve]->(v3) \
WHERE id(v) == 'player100' \
RETURN v2.name AS FriendOf, v3.name AS Team;
6)返回player102的出边和入边:
方法一:
GO FROM "player102" OVER follow BIDIRECT \
YIELD follow._dst AS both;
方法二:
MATCH (v) -[e:follow]-(v2) \
WHERE id(v)== "player102" \
RETURN id(v2) AS both;
7)查询player100 1~2跳内的朋友:
方法一:
GO 1 TO 2 STEPS FROM "player100" OVER follow \
YIELD follow._dst AS destination;
方法二:
MATCH (v) -[e:follow*1..2]->(v2) \
WHERE id(v) == "player100" \
RETURN id(v2) AS destination;
8)根据年龄分组:
GO 2 STEPS FROM "player100" OVER follow \
YIELD follow._src AS src, follow._dst AS dst, $$.player.age AS age \
| GROUP BY $-.dst \
YIELD $-.dst AS dst, collect_set($-.src) AS src, collect($-.age) AS age
9)分组并限制输出结果的行数:
$a = GO FROM "player100" OVER follow YIELD follow._src AS src, follow._dst AS dst; \
GO 2 STEPS FROM $a.dst OVER follow \
YIELD $a.src AS src, $a.dst, follow._src, follow._dst \
| ORDER BY $-.src | OFFSET 1 LIMIT 2;
10)在多个边上通过IS NOT EMPTY进行判断:
GO FROM "player100" OVER * WHERE $$.player.name IS NOT EMPTY YIELD follow._dst;
FETCH可以获取指定点或边的属性值。
语法(点):
FETCH PROP ON {<tag_name>[, tag_name ...] | *}
<vid> [, vid ...]
[YIELD <output>]
示例:
1)基于Tag获取点的属性值:
FETCH PROP ON player "player100";
2)获取点的指定属性值:
FETCH PROP ON player "player100" \
YIELD player.name;
3)获取多个点的属性值:
FETCH PROP ON player "player101", "player102", "player103";
4)基于多个Tag获取点的属性值:
FETCH PROP ON player, t1 "player100";
5)在所有标签中获取点的属性值:
FETCH PROP ON * "player100", "player106", "team200";
语法(边):
FETCH PROP ON <edge_type> <src_vid> -> <dst_vid>[@<rank>] [, <src_vid> -> <dst_vid> ...]
[YIELD <output>]
示例:
1)获取边的所有属性:
FETCH PROP ON serve "player100" -> "team204";
2)获取边的指定属性:
FETCH PROP ON serve "player100" -> "team204" \
YIELD serve.start_year;
3)获取多条边的属性值:
FETCH PROP ON serve "player100" -> "team204", "player133" -> "team202";
4)基于rank获取属性值:
i) 插入不同属性值、不同rank的边
insert edge serve(start_year,end_year) \
values "player100"->"team204"@1:(1998, 2017);
insert edge serve(start_year,end_year) \
values "player100"->"team204"@2:(1990, 2018);
ii) 默认返回rank为0的边
FETCH PROP ON serve "player100" -> "team204";
iii) 要获取rank不为0的边,需要在FETCH语句中指定rank
FETCH PROP ON serve "player100" -> "team204"@1;
5)复合语句中使用FETCH:
i) 返回从点player101开始的follow边的degree值
GO FROM "player101" OVER follow \
YIELD follow._src AS s, follow._dst AS d \
| FETCH PROP ON follow $-.s -> $-.d \
YIELD follow.degree;