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

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

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

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

    更多信息,请参见。

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

    The demo dataset

    检查Nebula Graph集群的机器状态

    Note

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

    在返回结果中,查看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. Execution succeeded (time spent 2817/3280 us)
    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. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
      7. | "storaged1" | 9779 | "ONLINE" | 5 | "basketballplayer:5" | "basketballplayer:5" |
      8. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
      9. | "storaged2" | 9779 | "ONLINE" | 5 | "basketballplayer:5" | "basketballplayer:5" |
      10. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
      11. | "Total" | | | 15 | "basketballplayer:15" | "basketballplayer:15" |
      12. +-------------+-----------+-----------+--------------+----------------------------------+------------------------+
      13. Got 4 rows (time spent 1633/2867 us)

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

    3. 选择图空间basketballplayer

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

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

      1. nebula> SHOW SPACES;
      2. +--------------------+
      3. | Name |
      4. +--------------------+
      5. | "basketballplayer" |
      6. +--------------------+
      7. Got 1 rows (time spent 977/2000 us)

    nGQL语法

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

    示例

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

    1. nebula> CREATE TAG player(name string, age int);
    2. Execution succeeded (time spent 20708/22071 us)
    3. nebula> CREATE TAG team(name string);
    4. Execution succeeded (time spent 5643/6810 us)
    5. nebula> CREATE EDGE follow(degree int);
    6. Execution succeeded (time spent 12665/13934 us)
    7. nebula> CREATE EDGE serve(start_year int, end_year int);
    8. Execution succeeded (time spent 5858/6870 us)

    插入点和边

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

    • 插入点

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

      参数详情请参见。

    示例

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

    • 插入代表球员和球队之间关系的边。

      1. nebula> INSERT EDGE follow(degree) VALUES "player100" -> "player101":(95);
      2. Execution succeeded (time spent 3362/4542 us)
      3. nebula> INSERT EDGE follow(degree) VALUES "player100" -> "player102":(90);
      4. Execution succeeded (time spent 2974/4274 us)
      5. nebula> INSERT EDGE follow(degree) VALUES "player102" -> "player101":(75);
      6. Execution succeeded (time spent 1891/3096 us)
      7. nebula> INSERT EDGE serve(start_year, end_year) VALUES "player100" -> "team200":(1997, 2016), "player101" -> "team201":(1999, 2018);
      8. Execution succeeded (time spent 6064/7104 us)

    查询数据

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

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

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

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

    nGQL语法

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

      • 查询Tag属性

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

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

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

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

    GO语句示例

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

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

      1. nebula> GO FROM "player100" OVER follow WHERE $$.player.age >= 35 \
      2. YIELD $$.player.name AS Teammate, $$.player.age AS Age;
      3. +---------------+-----+
      4. | Teammate | Age |
      5. +---------------+-----+
      6. | "Tony Parker" | 36 |
      7. +---------------+-----+
      8. Got 1 rows (time spent 8206/9335 us)
    • 从VID为player100的球员开始,沿着边follow查找连接的球员,然后检索这些球员的球队。为了合并这两个查询请求,可以使用管道符或临时变量。

      • 使用管道符

        1. nebula> GO FROM "player100" OVER follow YIELD follow._dst AS id | \
        2. GO FROM $-.id OVER serve YIELD $$.team.name AS Team, \
        3. $^.player.name AS Player;
        4. +-----------+---------------+
        5. | Team | Player |
        6. +-----------+---------------+
        7. | "Nuggets" | "Tony Parker" |
        8. +-----------+---------------+
        9. Got 1 rows (time spent 5055/8203 us)
      • 使用临时变量

        Note

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

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

    FETCH语句示例

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

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

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

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

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

    Note

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

    • 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的球员的属性,然后用FETCH语句检查结果。

      1. nebula> UPDATE VERTEX "player100" SET player.name = "Tim";
      2. Execution succeeded (time spent 3483/3914 us)
      3. nebula> FETCH PROP ON player "player100";
      4. +---------------------------------------------+
      5. | vertices_ |
      6. +---------------------------------------------+
      7. | ("player100" :player{age: 42, name: "Tim"}) |
      8. +---------------------------------------------+
      9. Got 1 rows (time spent 2463/3042 us)
    • UPDATE修改某条边的degree属性,然后用FETCH检查结果。

      1. nebula> UPDATE EDGE "player100" -> "player101" OF follow SET degree = 96;
      2. Execution succeeded (time spent 3932/4432 us)
      3. nebula> FETCH PROP ON follow "player100" -> "player101";
      4. +----------------------------------------------------+
      5. | edges_ |
      6. +----------------------------------------------------+
      7. | [:follow "player100"->"player101" @0 {degree: 96}] |
      8. +----------------------------------------------------+
      9. Got 1 rows (time spent 2205/2800 us)
    • UPSERT插入一个VID为player111的点。

      1. nebula> INSERT VERTEX player(name, age) VALUES "player111":("Ben Simmons", 22);
      2. Execution succeeded (time spent 2115/2900 us)
      3. Wed, 21 Oct 2020 11:11:50 UTC
      4. nebula> UPSERT VERTEX "player111" SET player.name = "Dwight Howard", player.age = $^.player.age + 11 \
      5. WHEN $^.player.name == "Ben Simmons" AND $^.player.age > 20 \
      6. YIELD $^.player.name AS Name, $^.player.age AS Age;
      7. +---------------+-----+
      8. | Name | Age |
      9. +---------------+-----+
      10. | Dwight Howard | 33 |
      11. +---------------+-----+
      12. Got 1 rows (time spent 1815/2329 us)

    删除点和边

    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 "team1", "team2";
      2. Execution succeeded (time spent 4337/4782 us)
    • 删除边

      1. nebula> DELETE EDGE follow "team1" -> "team2";
      2. Execution succeeded (time spent 3700/4101 us)

    索引

    用户可以通过语句为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>;

    为Tagplayer的属性name创建索引,并且重建索引。

    Note

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

    基于索引的LOOKUPMATCH示例

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

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

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