跳转至

基础操作语法

本文介绍Nebula Graph基础操作的语法。

图空间和Schema

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

Nebula Graph and graph spaces

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

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

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

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

The demo dataset

检查Nebula Graph集群的机器状态

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

nebula> SHOW HOSTS;
+-------------+-----------+-----------+--------------+----------------------+------------------------+
| Host        | Port      | Status    | Leader count | Leader distribution  | Partition distribution |
+-------------+-----------+-----------+--------------+----------------------+------------------------+
| "storaged0" | 9779      | "ONLINE"  | 0            | "No valid partition" | "No valid partition"   |
+-------------+-----------+-----------+--------------+----------------------+------------------------+
| "storaged1" | 9779      | "ONLINE"  | 0            | "No valid partition" | "No valid partition"   |
+-------------+-----------+-----------+--------------+----------------------+------------------------+
| "storaged2" | 9779      | "ONLINE"  | 0            | "No valid partition" | "No valid partition"   |
+-------------+-----------+-----------+--------------+----------------------+------------------------+
| "Total"     | __EMPTY__ | __EMPTY__ | 0            | __EMPTY__            | __EMPTY__              |
+-------------+-----------+-----------+--------------+----------------------+------------------------+
Got 4 rows (time spent 1061/2251 us)

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

异步实现创建和修改

Nebula Graph中执行如下创建和修改操作,是异步实现的,需要在下一个心跳周期才同步数据。

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

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

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

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

创建和选择图空间

nGQL语法

  • 创建图空间
    CREATE SPACE [IF NOT EXISTS] <graph_space_name>
        [(partition_num = <partition_number>, 
        replica_factor = <replica_number>, 
        vid_type = {FIXED_STRING(<N>) | INT64})];
    
    参数 说明
    partition_num 指定图空间的分片数量。建议设置为5倍的集群硬盘数量。例如集群中有3个硬盘,建议设置15个分片。
    replica_factor 指定每个分片的副本数量。建议在生产环境中设置为3,在测试环境中设置为1。由于需要进行基于quorum的选举,副本数量必须是奇数
    vid_type 指定点ID的数据类型。可选值为FIXED_STRING(<N>)INT64FIXED_STRING(<N>)表示数据类型为字符串,最大长度为N,超出长度会报错;INT64表示数据类型为整数。默认值为FIXED_STRING(8)
  • 列出创建成功的图空间
    nebula> SHOW SPACES;
    
  • 选择数据库
    USE <graph_space_name>;
    

示例

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

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

    nebula> SHOW HOSTS;
    +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    | Host        | Port      | Status    | Leader count | Leader distribution              | Partition distribution |
    +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    | "storaged0" | 9779      | "ONLINE"  | 5            | "basketballplayer:5"             | "basketballplayer:5"   |
    +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    | "storaged1" | 9779      | "ONLINE"  | 5            | "basketballplayer:5"             | "basketballplayer:5"   |
    +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    | "storaged2" | 9779      | "ONLINE"  | 5            | "basketballplayer:5"             | "basketballplayer:5"   |
    +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    | "Total"     | __EMPTY__ | __EMPTY__ | 15           | "basketballplayer:15"            | "basketballplayer:15"  |
    +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
    Got 4 rows (time spent 1633/2867 us)
    

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

  3. 选择图空间basketballplayer

    nebula[(none)]> USE basketballplayer;
    Execution succeeded (time spent 1229/2318 us)
    

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

    nebula> SHOW SPACES;
    +--------------------+
    | Name               |
    +--------------------+
    | "basketballplayer" |
    +--------------------+
    Got 1 rows (time spent 977/2000 us)
    

创建标签和边类型

nGQL语法

CREATE {TAG | EDGE} {<tag_name> | <edge_type>}(<property_name> <data_type>
[, <property_name> <data_type> ...]);

示例

创建标签playerteam,以及边类型followserve。说明如下表。

名称 类型 属性
player Tag name (string), age (int)
team Tag name (string)
follow Edge type degree (int)
serve Edge type start_year (int), end_year (int)
nebula> CREATE TAG player(name string, age int);
Execution succeeded (time spent 20708/22071 us)

Wed, 24 Feb 2021 03:47:01 EST

nebula> CREATE TAG team(name string);
Execution succeeded (time spent 5643/6810 us)

Wed, 24 Feb 2021 03:47:59 EST

nebula> CREATE EDGE follow(degree int);
Execution succeeded (time spent 12665/13934 us)

Wed, 24 Feb 2021 03:48:07 EST

nebula> CREATE EDGE serve(start_year int, end_year int);
Execution succeeded (time spent 5858/6870 us)

Wed, 24 Feb 2021 03:48:16 EST

插入点和边

您可以使用INSERT语句,基于现有的标签插入点,或者基于现有的边类型插入边。

nGQL语法

  • 插入点
    INSERT VERTEX <tag_name> (<property_name>[, <property_name>...])
    [, <tag_name> (<property_name>[, <property_name>...]), ...]
    {VALUES | VALUE} <vid>: (<property_value>[, <property_value>...])
    [, <vid>: (<property_value>[, <property_value>...];
    

    VID是Vertex ID的缩写,VID在一个图空间中是唯一的。

  • 插入边
    INSERT EDGE <edge_type> (<property_name>[, <property_name>...])
    {VALUES | VALUE} <src_vid> -> <dst_vid>[@<rank>] : (<property_value>[, <property_value>...])
    [, <src_vid> -> <dst_vid>[@<rank> : (<property_name>[, <property_name>...]), ...];
    

示例

  • 插入代表球员和球队的点。
    nebula> INSERT VERTEX player(name, age) VALUES "player100":("Tim Duncan", 42);
    Execution succeeded (time spent 28196/30896 us)
    
    Wed, 24 Feb 2021 03:55:08 EST
    
    nebula> INSERT VERTEX player(name, age) VALUES "player101":("Tony Parker", 36);
    Execution succeeded (time spent 2708/3834 us)
    
    Wed, 24 Feb 2021 03:55:20 EST
    
    nebula> INSERT VERTEX player(name, age) VALUES "player102":("LaMarcus Aldridge", 33);
    Execution succeeded (time spent 1945/3294 us)
    
    Wed, 24 Feb 2021 03:55:32 EST
    
    nebula> INSERT VERTEX team(name) VALUES "team200":("Warriors"), "team201":("Nuggets");
    Execution succeeded (time spent 2269/3310 us)
    
    Wed, 24 Feb 2021 03:55:47 EST
    
  • 插入代表球员和球队之间关系的边。
    nebula> INSERT EDGE follow(degree) VALUES "player100" -> "player101":(95);
    Execution succeeded (time spent 3362/4542 us)
    
    Wed, 24 Feb 2021 03:57:36 EST
    
    nebula> INSERT EDGE follow(degree) VALUES "player100" -> "player102":(90);
    Execution succeeded (time spent 2974/4274 us)
    
    Wed, 24 Feb 2021 03:57:44 EST
    
    nebula> INSERT EDGE follow(degree) VALUES "player102" -> "player101":(75);
    Execution succeeded (time spent 1891/3096 us)
    
    Wed, 24 Feb 2021 03:57:52 EST
    
    nebula> INSERT EDGE serve(start_year, end_year) VALUES "player100" -> "team200":(1997, 2016), "player101" -> "team201":(1999,  2018);
    Execution succeeded (time spent 6064/7104 us)
    
    Wed, 24 Feb 2021 03:58:01 EST
    

查询数据

  • GO语句可以根据指定的条件遍历数据库。GO语句从一个或多个点开始,沿着一条或多条边遍历,返回YIELD子句中指定的信息。
  • FETCH语句可以获得点或边的属性。
  • LOOKUP语句是基于索引的,和WHERE子句一起使用,查找符合特定条件的数据。
  • MATCH语句是查询图数据最常用的,但是它依赖索引去匹配Nebula Graph中的数据模型。

nGQL语法

  • GO
    GO [[<M> TO] <N> STEPS ] FROM <vertex_list>
    OVER <edge_type_list> [REVERSELY] [BIDIRECT]
    [WHERE <expression> [AND | OR expression ...])]
    YIELD [DISTINCT] <return_list>;
    
  • FETCH

    • 查询标签属性

      FETCH PROP ON {<tag_name> | <tag_name_list> | *} <vid_list>
      [YIELD [DISTINCT] <return_list>];
      
    • 查询边属性

      FETCH PROP ON <edge_type> <src_vid> -> <dst_vid>[@<rank>]
      [, <src_vid> -> <dst_vid> ...]
      [YIELD [DISTINCT] <return_list>];
      
  • LOOKUP
    LOOKUP ON {<tag_name> | <edge_type>} 
    WHERE <expression> [AND expression ...])]
    [YIELD <return_list>];
    
  • MATCH
    MATCH <pattern> [<WHERE clause>] RETURN <output>;
    

GO语句示例

  • 从VID为player100的球员开始,沿着边follow找到连接的球员。
    nebula> GO FROM "player100" OVER follow;
    +-------------+
    | follow._dst |
    +-------------+
    | "player101" |
    +-------------+
    | "player102" |
    +-------------+
    Got 2 rows (time spent 12097/14220 us)
    
  • 从VID为player100的球员开始,沿着边follow查找年龄大于或等于35岁的球员,并返回他们的姓名和年龄,同时重命名对应的列。
    nebula> GO FROM "player100" OVER follow WHERE $$.player.age >= 35 \
                    ->     YIELD $$.player.name AS Teammate, $$.player.age AS Age;
    +---------------+-----+
    | Teammate      | Age |
    +---------------+-----+
    | "Tony Parker" | 36  |
    +---------------+-----+
    Got 1 rows (time spent 8206/9335 us)
    
    子句/符号 说明
    YIELD 指定该查询需要返回的值或结果。
    $$ 表示边的终点。
    \ 表示换行继续输入。
  • 从VID为player100的球员开始,沿着边follow查找连接的球员,然后检索这些球员的球队。为了合并这两个查询请求,可以使用管道符或临时变量。

    • 使用管道符
    nebula> GO FROM "player100" OVER follow YIELD follow._dst AS id | \
                    GO FROM $-.id OVER serve YIELD $$.team.name AS Team, \
                    $^.player.name AS Player;
    +-----------+---------------+
    | Team      | Player        |
    +-----------+---------------+
    | "Nuggets" | "Tony Parker" |
    +-----------+---------------+
    Got 1 rows (time spent 5055/8203 us)
    
    子句/符号 说明
    $^ 表示边的起点。
    \| 组合多个查询的管道符,将前一个查询的结果集用于后一个查询。
    $- 表示管道符前面的查询输出的结果集。
    • 使用临时变量

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

    ngql nebula> $var = GO FROM "player100" OVER follow YIELD follow._dst AS id; \ GO FROM $var.id OVER serve YIELD $$.team.name AS Team, \ $^.player.name AS Player; +---------+-------------+ | Team | Player | +---------+-------------+ | Nuggets | Tony Parker | +---------+-------------+ Got 1 rows (time spent 3103/3711 us)

FETCH语句示例

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

nebula> FETCH PROP ON player "player100";
+----------------------------------------------------+
| vertices_                                          |
+----------------------------------------------------+
| ("player100" :player{age: 42, name: "Tim Duncan"}) |
+----------------------------------------------------+
Got 1 rows (time spent 2006/2406 us)

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

修改点和边

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

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

说明UPSERT操作是基于Nebula Graph的分区进行串行操作,所以执行速度比单独执行INSERTUPDATE慢。

nGQL语法

  • UPDATE
    UPDATE VERTEX <vid> SET <properties to be updated>
    [WHEN <condition>] [YIELD <columns>];
    
  • UPDATE
    UPDATE EDGE <source vid> -> <destination vid> [@rank] OF <edge_type>
    SET <properties to be updated> [WHEN <condition>] [YIELD <columns to be output>];
    
  • UPSERT点或边
    UPSERT {VERTEX <vid> | EDGE <edge_type>} SET <update_columns>
    [WHEN <condition>] [YIELD <columns>];
    

示例

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

    ```ngql nebula> UPDATE VERTEX "player100" SET player.name = "Tim"; Execution succeeded (time spent 3483/3914 us)

    Wed, 21 Oct 2020 10:53:14 UTC

    nebula> FETCH PROP ON player "player100"; +---------------------------------------------+ | vertices_ | +---------------------------------------------+ | ("player100" :player{age: 42, name: "Tim"}) | +---------------------------------------------+ Got 1 rows (time spent 2463/3042 us) ```

  • UPDATE修改某条边的degree属性,然后用FETCH检查结果。
    nebula> UPDATE EDGE "player100" -> "player101" OF follow SET degree = 96;
    Execution succeeded (time spent 3932/4432 us)
    
    nebula> FETCH PROP ON follow "player100" -> "player101";
    +----------------------------------------------------+
    | edges_                                             |
    +----------------------------------------------------+
    | [:follow "player100"->"player101" @0 {degree: 96}] |
    +----------------------------------------------------+
    Got 1 rows (time spent 2205/2800 us)
    
  • UPSERT插入一个VID为player111的点。
    nebula> INSERT VERTEX player(name, age) VALUES "player111":("Ben Simmons", 22);
    Execution succeeded (time spent 2115/2900 us)
    
    Wed, 21 Oct 2020 11:11:50 UTC
    
    nebula> UPSERT VERTEX "player111" SET player.name = "Dwight Howard", player.age = $^.player.age + 11 \
    WHEN $^.player.name == "Ben Simmons" AND $^.player.age > 20 \
    YIELD $^.player.name AS Name, $^.player.age AS Age;
    +---------------+-----+
    | Name          | Age |
    +---------------+-----+
    | Dwight Howard | 33  |
    +---------------+-----+
    Got 1 rows (time spent 1815/2329 us)
    

删除点和边

nGQL语法

  • 删除点
    DELETE VERTEX <vid1>[, <vid2>...]
    
  • 删除边
    DELETE EDGE <edge_type> <src_vid> -> <dst_vid>[@<rank>]
    [, <src_vid> -> <dst_vid>...]
    

示例

  • 删除点
    nebula> DELETE VERTEX "team1", "team2";
    Execution succeeded (time spent 4337/4782 us)
    
  • 删除边
    nebula> DELETE EDGE follow "team1" -> "team2";
    Execution succeeded (time spent 3700/4101 us)
    

索引

您可以通过CREATE INDEX语句为标签(tag)和边类型(edge type)增加索引。

使用索引必读

  • MATCHLOOKUP语句的执行都依赖索引,但是索引会导致写性能大幅降低(降低90%甚至更多)。请不要随意在生产环境中使用索引,除非您很清楚使用索引对业务的影响。
  • 必须为已存在的数据重建索引,否则不能索引已存在的数据,导致无法在MATCHLOOKUP语句中返回这些数据。更多信息,请参见重建索引

nGQL语法

  • 创建索引
    CREATE {TAG | EDGE} INDEX [IF NOT EXISTS] <index_name>
    ON {<tag_name> | <edge_name>} (prop_name_list);
    
  • 重建索引
    REBUILD {TAG | EDGE} INDEX <index_name>;
    

示例

为标签player的属性name创建索引,并且重建索引。

nebula> CREATE TAG INDEX player_index_0 on player(name(20));
nebula> REBUILD TAG INDEX player_index_0;

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

基于索引的LOOKUPMATCH示例

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

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

// 为name属性创建索引player_name_0。
nebula> CREATE TAG INDEX player_name_0 on player(name(10));
Execution succeeded (time spent 3465/4150 us)

// 重建索引确保能对已存在数据生效。
nebula> REBUILD TAG INDEX player_name_0
+------------+
| New Job Id |
+------------+
| 31         |
+------------+
Got 1 rows (time spent 2379/3033 us)

// 使用LOOKUP语句检索点的属性。
nebula> LOOKUP ON player WHERE player.name == "Tony Parker" \
YIELD player.name, player.age;
+-------------+---------------+------------+
| VertexID    | player.name   | player.age |
+-------------+---------------+------------+
| "player101" | "Tony Parker" | 36         |
+-------------+---------------+------------+

// 使用MATCH语句检索点的属性。
nebula> MATCH (v:player{name:"Tony Parker"}) RETURN v;
+-----------------------------------------------------+
| v                                                   |
+-----------------------------------------------------+
| ("player101" :player{age: 36, name: "Tony Parker"}) |
+-----------------------------------------------------+
Got 1 rows (time spent 5132/6246 us)

最后更新: 2021年4月29日