步骤4:使用常用nGQL(CRUD命令)

本文介绍Nebula Graph查询语言的基础语法,包括用于Schema创建和常用增删改查操作的语句。

如需了解更多语句的用法,参见nGQL指南

图空间和Schema

一个Nebula Graph实例由一个或多个图空间组成。每个图空间都是物理隔离的,用户可以在同一个实例中使用不同的图空间存储不同的数据集。

Nebula Graph and graph spaces

为了在图空间中插入数据,需要为图数据库定义一个Schema。Nebula Graph的Schema是由如下几部分组成。

组成部分说明
点(Vertex)表示现实世界中的实体。一个点可以有一个或多个标签。
标签(Tag)点的类型,定义了一组描述点类型的属性。
边(Edge)表示两个点之间有方向的关系。
边类型(Edge type)边的类型,定义了一组描述边的类型的属性。

更多信息,请参见数据结构

本文将使用下图的数据集演示基础操作的语法。

The demo dataset

检查Nebula Graph集群的机器状态

Note

首先建议检查机器状态,确保所有的Storage服务连接到了Meta服务。执行命令SHOW HOSTS查看机器状态。

  1. nebula> SHOW HOSTS;
  2. +-------------+-----------+-----------+--------------+----------------------+------------------------+
  3. | Host | Port | Status | Leader count | Leader distribution | Partition distribution |
  4. +-------------+-----------+-----------+--------------+----------------------+------------------------+
  5. | "storaged0" | 9779 | "ONLINE" | 0 | "No valid partition" | "No valid partition" |
  6. | "storaged1" | 9779 | "ONLINE" | 0 | "No valid partition" | "No valid partition" |
  7. | "storaged2" | 9779 | "ONLINE" | 0 | "No valid partition" | "No valid partition" |
  8. | "Total" | __EMPTY__ | __EMPTY__ | 0 | __EMPTY__ | __EMPTY__ |
  9. +-------------+-----------+-----------+--------------+----------------------+------------------------+

在返回结果中,查看Status列,可以看到所有Storage服务都在线。

异步实现创建和修改

Caution

Nebula Graph中执行如下创建和修改操作,是异步实现的。要在下一个心跳周期之后才能生效;否则访问会报错。

  • CREATE SPACE
  • CREATE TAG
  • CREATE EDGE
  • ALTER TAG
  • ALTER EDGE
  • CREATE TAG INDEX
  • CREATE EDGE INDEX

Note

默认心跳周期是10秒。修改心跳周期参数heartbeat_interval_secs,请参见配置简介

为确保数据同步,后续操作能顺利进行,可采取以下方法之一:

  • 执行SHOWDESCRIBE命令检查相应对象的状态,确保创建或修改已完成。如果没有完成,请等待几秒重试。

  • 等待2个心跳周期(20秒)。

创建和选择图空间

nGQL语法

  • 创建图空间

    1. CREATE SPACE [IF NOT EXISTS] <graph_space_name> (
    2. [partition_num = <partition_number>,]
    3. [replica_factor = <replica_number>,]
    4. vid_type = {FIXED_STRING(<N>) | INT64}
    5. )
    6. [COMMENT = '<comment>'];

    参数详情请参见CREATE SPACE

  • 列出创建成功的图空间

    1. nebula> SHOW SPACES;
  • 选择数据库

    1. USE <graph_space_name>;

示例

  1. 执行如下语句创建名为basketballplayer的图空间。

    1. nebula> CREATE SPACE basketballplayer(partition_num=15, replica_factor=1, vid_type=fixed_string(30));
  2. 执行命令SHOW HOSTS检查分片的分布情况,确保平衡分布。

    1. nebula> SHOW HOSTS;
    2. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    3. | Host | Port | Status | Leader count | Leader distribution | Partition distribution |
    4. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    5. | "storaged0" | 9779 | "ONLINE" | 5 | "basketballplayer:5" | "basketballplayer:5" |
    6. | "storaged1" | 9779 | "ONLINE" | 5 | "basketballplayer:5" | "basketballplayer:5" |
    7. | "storaged2" | 9779 | "ONLINE" | 5 | "basketballplayer:5" | "basketballplayer:5" |
    8. | "Total" | | | 15 | "basketballplayer:15" | "basketballplayer:15" |
    9. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+

    如果Leader distribution分布不均匀,请执行命令BALANCE LEADER重新分配。更多信息,请参见Storage负载均衡

  3. 选择图空间basketballplayer

    1. nebula[(none)]> USE basketballplayer;

    用户可以执行命令SHOW SPACES查看创建的图空间。

    1. nebula> SHOW SPACES;
    2. +--------------------+
    3. | Name |
    4. +--------------------+
    5. | "basketballplayer" |
    6. +--------------------+

创建Tag和Edge type

nGQL语法

  1. CREATE {TAG | EDGE} {<tag_name> | <edge_type>}(<property_name> <data_type>
  2. [, <property_name> <data_type> ...])
  3. [COMMENT = '<comment>'];

参数详情请参见CREATE TAGCREATE EDGE

示例

创建Tag:playerteam,以及Edge type:followserve。说明如下表。

名称类型属性
playerTagname (string), age (int)
teamTagname (string)
followEdge typedegree (int)
serveEdge typestart_year (int), end_year (int)
  1. nebula> CREATE TAG player(name string, age int);
  2. nebula> CREATE TAG team(name string);
  3. nebula> CREATE EDGE follow(degree int);
  4. nebula> CREATE EDGE serve(start_year int, end_year int);

插入点和边

用户可以使用INSERT语句,基于现有的Tag插入点,或者基于现有的Edge type插入边。

nGQL语法

  • 插入点

    1. INSERT VERTEX [IF NOT EXISTS] <tag_name> (<property_name>[, <property_name>...])
    2. [, <tag_name> (<property_name>[, <property_name>...]), ...]
    3. {VALUES | VALUE} <vid>: (<property_value>[, <property_value>...])
    4. [, <vid>: (<property_value>[, <property_value>...];

    VID是Vertex ID的缩写,VID在一个图空间中是唯一的。参数详情请参见INSERT VERTEX

  • 插入边

    1. INSERT EDGE [IF NOT EXISTS] <edge_type> (<property_name>[, <property_name>...])
    2. {VALUES | VALUE} <src_vid> -> <dst_vid>[@<rank>] : (<property_value>[, <property_value>...])
    3. [, <src_vid> -> <dst_vid>[@<rank>] : (<property_name>[, <property_name>...]), ...];

    参数详情请参见INSERT EDGE

示例

  • 插入代表球员和球队的点。

    1. nebula> INSERT VERTEX player(name, age) VALUES "player100":("Tim Duncan", 42);
    2. nebula> INSERT VERTEX player(name, age) VALUES "player101":("Tony Parker", 36);
    3. nebula> INSERT VERTEX player(name, age) VALUES "player102":("LaMarcus Aldridge", 33);
    4. nebula> INSERT VERTEX team(name) VALUES "team203":("Trail Blazers"), "team204":("Spurs");
  • 插入代表球员和球队之间关系的边。

    1. nebula> INSERT EDGE follow(degree) VALUES "player101" -> "player100":(95);
    2. nebula> INSERT EDGE follow(degree) VALUES "player101" -> "player102":(90);
    3. nebula> INSERT EDGE follow(degree) VALUES "player102" -> "player100":(75);
    4. nebula> INSERT EDGE serve(start_year, end_year) VALUES "player101" -> "team204":(1999, 2018),"player102" -> "team203":(2006, 2015);

查询数据

  • GO语句可以根据指定的条件遍历数据库。GO语句从一个或多个点开始,沿着一条或多条边遍历,返回YIELD子句中指定的信息。

  • FETCH语句可以获得点或边的属性。

  • LOOKUP语句是基于索引的,和WHERE子句一起使用,查找符合特定条件的数据。

  • MATCH语句是查询图数据最常用的,可以灵活的描述各种图模式,但是它依赖索引去匹配Nebula Graph中的数据模型,性能也还需要调优。

nGQL语法

  • GO

    1. GO [[<M> TO] <N> STEPS ] FROM <vertex_list>
    2. OVER <edge_type_list> [{REVERSELY | BIDIRECT}]
    3. [ WHERE <conditions> ]
    4. [YIELD [DISTINCT] <return_list>]
    5. [{SAMPLE <sample_list> | LIMIT <limit_list>}]
    6. [| GROUP BY {col_name | expr | position} YIELD <col_name>]
    7. [| ORDER BY <expression> [{ASC | DESC}]]
    8. [| LIMIT [<offset>,] <number_rows>];
  • FETCH

    • 查询Tag属性

      1. FETCH PROP ON {<tag_name>[, tag_name ...] | *}
      2. <vid> [, vid ...]
      3. [YIELD <return_list> [AS <alias>]];
    • 查询边属性

      1. FETCH PROP ON <edge_type> <src_vid> -> <dst_vid>[@<rank>] [, <src_vid> -> <dst_vid> ...]
      2. [YIELD <output>];
  • LOOKUP

    1. LOOKUP ON {<vertex_tag> | <edge_type>}
    2. [WHERE <expression> [AND <expression> ...]]
    3. [YIELD <return_list> [AS <alias>]];
  • MATCH

    1. MATCH <pattern> [<WHERE clause>] RETURN <output>;

GO语句示例

  • 从VID为player101的球员开始,沿着边follow找到连接的球员。

    1. nebula> GO FROM "player101" OVER follow;
    2. +-------------+
    3. | follow._dst |
    4. +-------------+
    5. | "player100" |
    6. | "player102" |
    7. +-------------+
  • 从VID为player101的球员开始,沿着边follow查找年龄大于或等于35岁的球员,并返回他们的姓名和年龄,同时重命名对应的列。

    1. nebula> GO FROM "player101" OVER follow WHERE properties($$).age >= 35 \
    2. YIELD properties($$).name AS Teammate, properties($$).age AS Age;
    3. +--------------+-----+
    4. | Teammate | Age |
    5. +--------------+-----+
    6. | "Tim Duncan" | 42 |
    7. +--------------+-----+
    子句/符号说明
    YIELD指定该查询需要返回的值或结果。
    $$表示边的终点。
    \表示换行继续输入。
  • 从VID为player101的球员开始,沿着边follow查找连接的球员,然后检索这些球员的球队。为了合并这两个查询请求,可以使用管道符或临时变量。

    • 使用管道符

      1. nebula> GO FROM "player101" OVER follow YIELD dst(edge) AS id | \
      2. GO FROM $-.id OVER serve YIELD properties($$).name AS Team, \
      3. properties($^).name AS Player;
      4. +-----------------+---------------------+
      5. | Team | Player |
      6. +-----------------+---------------------+
      7. | "Trail Blazers" | "LaMarcus Aldridge" |
      8. +-----------------+---------------------+
      子句/符号说明
      $^表示边的起点。
      |组合多个查询的管道符,将前一个查询的结果集用于后一个查询。
      $-表示管道符前面的查询输出的结果集。
    • 使用临时变量

      Note

      当复合语句作为一个整体提交给服务器时,其中的临时变量会在语句结束时被释放。

      1. nebula> $var = GO FROM "player101" OVER follow YIELD dst(edge) AS id; \
      2. GO FROM $var.id OVER serve YIELD properties($$).name AS Team, \
      3. properties($^).name AS Player;
      4. +-----------------+---------------------+
      5. | Team | Player |
      6. +-----------------+---------------------+
      7. | "Trail Blazers" | "LaMarcus Aldridge" |
      8. +-----------------+---------------------+

FETCH语句示例

查询VID为player100的球员的属性。

  1. nebula> FETCH PROP ON player "player100";
  2. +----------------------------------------------------+
  3. | vertices_ |
  4. +----------------------------------------------------+
  5. | ("player100" :player{age: 42, name: "Tim Duncan"}) |
  6. +----------------------------------------------------+

Note

LOOKUPMATCH的示例在下文的索引部分查看。

修改点和边

用户可以使用UPDATE语句或UPSERT语句修改现有数据。

UPSERTUPDATEINSERT的结合体。当使用UPSERT更新一个点或边,如果它不存在,数据库会自动插入一个新的点或边。

Note

每个 partition 内部,UPSERT 操作是一个串行操作,所以执行速度比执行 INSERTUPDATE 慢很多。其仅在多个 partition 之间有并发。

nGQL语法

  • UPDATE

    1. UPDATE VERTEX <vid> SET <properties to be updated>
    2. [WHEN <condition>] [YIELD <columns>];
  • UPDATE

    1. UPDATE EDGE <source vid> -> <destination vid> [@rank] OF <edge_type>
    2. SET <properties to be updated> [WHEN <condition>] [YIELD <columns to be output>];
  • UPSERT点或边

    1. UPSERT {VERTEX <vid> | EDGE <edge_type>} SET <update_columns>
    2. [WHEN <condition>] [YIELD <columns>];

示例

  • UPDATE修改VID为player100的球员的name属性,然后用FETCH语句检查结果。

    1. nebula> UPDATE VERTEX "player100" SET player.name = "Tim";
    2. nebula> FETCH PROP ON player "player100";
    3. +---------------------------------------------+
    4. | vertices_ |
    5. +---------------------------------------------+
    6. | ("player100" :player{age: 42, name: "Tim"}) |
    7. +---------------------------------------------+
  • UPDATE修改某条边的degree属性,然后用FETCH检查结果。

    1. nebula> UPDATE EDGE "player101" -> "player100" OF follow SET degree = 96;
    2. nebula> FETCH PROP ON follow "player101" -> "player100";
    3. +----------------------------------------------------+
    4. | edges_ |
    5. +----------------------------------------------------+
    6. | [:follow "player101"->"player100" @0 {degree: 96}] |
    7. +----------------------------------------------------+
  • INSERT插入一个VID为player111的点,然后用UPSERT更新它。

    1. nebula> INSERT VERTEX player(name,age) values "player111":("David West", 38);
    2. nebula> UPSERT VERTEX "player111" SET player.name = "David", player.age = $^.player.age + 11 \
    3. WHEN $^.player.name == "David West" AND $^.player.age > 20 \
    4. YIELD $^.player.name AS Name, $^.player.age AS Age;
    5. +---------+-----+
    6. | Name | Age |
    7. +---------+-----+
    8. | "David" | 49 |
    9. +---------+-----+

删除点和边

nGQL语法

  • 删除点

    1. DELETE VERTEX <vid1>[, <vid2>...]
  • 删除边

    1. DELETE EDGE <edge_type> <src_vid> -> <dst_vid>[@<rank>]
    2. [, <src_vid> -> <dst_vid>...]

示例

  • 删除点

    1. nebula> DELETE VERTEX "player111", "team203";
  • 删除边

    1. nebula> DELETE EDGE follow "player101" -> "team204";

索引

用户可以通过CREATE INDEX语句为Tag和Edge type增加索引。

使用索引必读

MATCHLOOKUP语句的执行都依赖索引,但是索引会导致写性能大幅降低(降低90%甚至更多)。请不要随意在生产环境中使用索引,除非很清楚使用索引对业务的影响。

必须为“已写入但未构建索引”的数据重建索引,否则无法在MATCHLOOKUP语句中返回这些数据。参见重建索引

nGQL语法

  • 创建索引

    1. CREATE {TAG | EDGE} INDEX [IF NOT EXISTS] <index_name>
    2. ON {<tag_name> | <edge_name>} ([<prop_name_list>]) [COMMENT = '<comment>'];
  • 重建索引

    1. REBUILD {TAG | EDGE} INDEX <index_name>;

Note

为没有指定长度的变量属性创建索引时,需要指定索引长度。在utf-8编码中,一个中文字符占3字节,请根据变量属性长度设置合适的索引长度。例如10个中文字符,索引长度需要为30。详情请参见创建索引

基于索引的LOOKUPMATCH示例

确保LOOKUPMATCH有一个索引可用。如果没有,请先创建索引。

找到Tag为player的点的信息,它的name属性值为Tony Parker

  1. // 为name属性创建索引player_index_1。
  2. nebula> CREATE TAG INDEX player_index_1 ON player(name(20));
  3. // 重建索引确保能对已存在数据生效。
  4. nebula> REBUILD TAG INDEX player_index_1
  5. +------------+
  6. | New Job Id |
  7. +------------+
  8. | 31 |
  9. +------------+
  10. // 使用LOOKUP语句检索点的属性。
  11. nebula> LOOKUP ON player WHERE player.name == "Tony Parker" \
  12. YIELD properties(vertex).name AS name, properties(vertex).age AS age;
  13. +-------------+---------------+-----+
  14. | VertexID | name | age |
  15. +-------------+---------------+-----+
  16. | "player101" | "Tony Parker" | 36 |
  17. +-------------+---------------+-----+
  18. // 使用MATCH语句检索点的属性。
  19. nebula> MATCH (v:player{name:"Tony Parker"}) RETURN v;
  20. +-----------------------------------------------------+
  21. | v |
  22. +-----------------------------------------------------+
  23. | ("player101" :player{age: 36, name: "Tony Parker"}) |
  24. +-----------------------------------------------------+

最后更新: October 28, 2021