通过图数据库实现数据持久化(仅对系统应用开放)

场景介绍

图数据库是一种专门用于处理复杂关系数据的数据库管理系统。它通过节点(顶点)和关系(边)的结构来存储和查询数据,能够高效地处理大量复杂的关系操作。图数据库的核心优势在于直接通过存储的边来遍历关系,相较于关系型数据库的多表JOIN遍历关系更为高效。常见使用场景有社交网络与关系分析、知识图谱、实时推荐系统等。当前接口均为系统能力,仅对系统应用开放。
从API version 18开始,支持通过图数据库实现数据持久化。

基本概念

  • :由节点关系组成的数据结构,用于表示实体及其关联。

  • Schema:定义了数据的结构化规则,类似于关系型数据库中的表结构设计。它明确了图中节点、关系、属性以及约束的组织方式,确保数据的一致性和查询效率。

  • 节点(顶点):图中的基本实体单元。

  • 关系(边):连接两个节点的有向边,表示实体间的关联。

  • 路径:由顶点和边按照从起点到终点的顺序组成的序列。

  • 标签:用于对节点或关系进行分类或分组的标记。例如下文建图语句中的“Person”、“Friend”。

  • 属性:附加在节点或关系上的键值对,存储具体数据。例如下文插入顶点语句中的“name: 'name_1'”、“age: 11”。

  • 点表:用于存储顶点信息的表。表名为点的标签(下文建图语句中的“Person”),表中内容包括点的id、属性等。

  • 边表:用于存储边信息的表。表名为边的标签(下文建图语句中的“Friend”),表中内容包括边的id、起终点的id、属性等。

  • 变量:GQL语句中用于在查询中临时存储和引用图数据(节点、边、路径等)的标识符,主要有以下三种:

    • 点变量:用于表示图中的一个顶点,通过变量名引用节点的属性、标签等(例如下文查询顶点的GQL语句中的“person”)。
    • 边变量:用于表示图中的边,通过变量名引用边的属性、标签等(例如下文查询边的GQL语句中的“relation”)。
    • 路径变量:用于表示图中的路径,即一系列顶点和边的有序集合,通常由路径遍历操作生成(例如下文查询路径的GQL语句中的“path”)。
const CREATE_GRAPH = "CREATE GRAPH test { (person:Person {name STRING, age INT}),(person)-[:Friend {year INT}]->(person) };"

const INSERT_VERTEX = "INSERT (:Person {name: 'name_1', age: 11});"

const QUERY_VERTEX = "MATCH (person:Person) RETURN person;"

const QUERY_EDGE = "MATCH ()-[relation:Friend]->() RETURN relation;"

const QUERY_PATH = "MATCH path=(a:Person {name: 'name_1'})-[]->{2, 2}(b:Person {name: 'name_3'}) RETURN path;"

运作机制

图数据库对应用提供通用的操作接口,底层使用自研组件作为持久化存储引擎,支持事务、索引、加密等特性。

约束限制

支持的数据类型及规格

ArkTS侧支持的基本数据类型:number、string、boolean。各数据类型的具体规格及约束见下表:

数据类型 规格说明
NULL 即nullptr,用来表示一个缺失值的项,建图时不允许设置数据类型为NULL。
number 1. INTEGER,取值范围与int64_t一致,NUMERIC、DATE、DATETIME、INT都映射成int64_t。
2. DOUBLE,取值范围与double一致,REAL、FLOAT都映射成double。
string 1. 最大64 * 1024字节,包含结束符'\0'。
2. CHARACTER(20)、VARXHAR(255)、VARYING CHARACTER(255)、NCHAR(55)、NATIVE CHARACTER(70)、NVARCHAR(100)都映射成STRING,其中数字没有实际意义。
3.字符串字面量必须使用成对的单引号,不能使用双引号,字符串内部不能包含单引号。
boolean 取值为true或false,BOOL、BOOLEAN都映射成int64_t。

属性图DDL规格及约束

DDL(Data Definition Language)语句: 数据定义语言,主要用于定义图的Schema。常用的DDL语句关键字为CREATE。DDL具体规格及约束见下表:

说明:

当前实现是GQL标准语法的子集,下列约束中除列约束与GQL标准无差异外,其他约束在GQL标准中均未指定。

规格项 规格说明
创建属性图 1. 一个数据库实例最多只能创建一个属性图。
2. 不支持在一个子句上同时定义点表和边表,即:(person:Person {name STRING, age INT})-[:Friend {year INT}]->(person)。
3. 创建属性图时的边表必须指定方向,当前只允许从左到右的方向,即‘-[’、‘]->’。
4. 属性图名称长度上限128字节,不区分大小写。
5. 变量名区分大小写,点表必须指定变量名,变量名长度上限128字节,且变量名不能以anon_开头,边表不能指定变量名,不同点表对应的变量名不能重复。
6. ‘-[’、‘]->’、‘]-’、‘<-[’中间不能出现空格,如‘- [’
7. 创建属性图时,必须先定义点表,再定义边表,必须至少定义一个点表,边表可以不定义。
8. 点标签和边标签不能同名。
9. GQL系统表中使用变长字段类型保存建图语句,因此建图语句长度需要小于64 * 1024字节。
点表/边表总个数 1. 用户创建的点边表名称不能与系统表名(以预留表前缀‘GM_’开头)重复。
2. 不允许用户修改系统表。
3. 暂不允许用户查询系统表。
4. 单进程非共享模式,一个数据库实例点表数量上限为2000,边表数量上限为10000。
5. 由于变长字段类型长度上限为64 * 1024字节,点边表实际能够创建的最大个数可能会小于上限值,例如10000个边表的建图语句超过了64 * 1024字节会导致属性图创建失败。
点表/边表属性数量 1. 一个点表/边表里最多允许1023个属性(不包含DB内部默认添加的identity属性)。
2. 不允许指定属性名为rowid或identity,DB内部会默认给每个点、边标签都加上identity属性。
3. 属性名称长度上限128字节,不区分大小写。
4. identity属性不允许插入时指定值,不允许更新,不能通过属性名identity查询,只能通过element_id(v)获取变量v的identity。
表名长度 表名长度上限128字节,不区分大小写,如table和TABLE是相同的表。
属性名长度 属性名长度上限128字节,不区分大小写。
变长字段类型长度 string类型的属性值的长度应不超过64 * 1024字节。
默认值 1. 仅支持常量表达式作为默认值,如100,‘China’等。
2. 当默认值为时间关键字(CURRENT_DATE、CURRENT_TIMESTAMP、CURRENT_TIME)时,对应的类型应为string而不是int64_t。
列约束 当属性设置NOT NULL时,该属性值不能为NULL。

属性图DML/DQL规格及约束

DML(Data Manipulation Language)语句:数据操纵语言,主要用于对数据进行增加、删除和修改操作。常用的DML语句关键字有:INSERT、SET、DETACH DELETE等。
DQL(Data Query Language)语句:数据查询语言,主要用于对数据进行查询操作。常用的DQL语句关键字有MATCH、WHERE等。

关键字规格及约束

关键字 规格说明 与GQL标准差异
MATCH 1. 不支持无上限的变长跳数(下N跳:0<=N<=3)。
2. 变量名区分大小写,且变量名不可以‘anon_’开头。
3. 不允许变长边和定长边同时出现,例如“MATCH p = (a: A)-[e1]->(b)-[e2]->{1, 2}(c)”,e1是定长边,e2是变长边。
4. 插入语句的MATCH子句中,path的数量不可以超过2,其他语句不可以超过1。
5. 变长下N跳只能出现一次,变长下N跳的边上不能再指定表名、属性过滤列表(如{id: 1})、WHERE子句。
6. 同一个variableName不可以对应多个路径或多个边,同一个variableName可以对应多个点表,但如果指定点表必须指定相同的labelName。
7. ‘-[’、‘]->’、‘]-’、‘<-[’中间不能出现空格,如‘- [’。
8. 不支持一个GQL语句中使用两个及以上的MATCH。
9. 匹配模式中出现{}时,必须带有属性名和属性值,不允许出现空的{},例如“MATCH (n: Person {}) RETURN n”会出现语法错误。
除9外,GQL标准未明确定义左侧约束。
WHERE 1. WHERE后面不支持使用变长边变量和路径变量,点变量和边变量必须指定属性名称。
2. 如果WHERE后面直接是属性列(例如“WHERE id”,则转换成bool的值,然后进行判断(id=0时转换后为false,否则为true)。
3. WHERE子句后不能出现“()-[]->()”等图匹配形式。
除3外,GQL标准不存在左侧约束。
INSERT 1. 插入语句必须指定待插入的点和边所在的标签(表)名。
2. 不支持INSERT后跟RETURN一起使用。
3. 不支持同时插入点和边。
4. 不支持MATCH+WHERE+INSERT组合。
5. 匹配模式中出现{}时,必须带有属性名和属性值,不允许出现空的{},例如:INSERT (: Person {})”会出现语法错误。
除5外,GQL标准不存在左侧约束。
SET 1. 不支持更新点边所属的标签名(表名),不支持一个点多标签。
2. 不支持SET后跟RETURN一起使用。
3. 不支持更新空的属性值(例如“SET p = {}”),至少要有一个属性被设置。
4. SET子句后不能出现“()-[]->()”等图匹配形式。
除4外,GQL标准不存在左侧约束。
DETACH DELETE 1. 匹配点如果存在边(关系)会一并删除,匹配边则只删除边。
2.不支持DETACH DELETE后跟RETURN一起使用。
3. 不支持删除变长边变量和路径变量,DELETE子句后不能出现“()-[]->()”等图匹配形式。
4. 不支持未加关键字的DELETE(同义词:NODETACH DELETE)。
除1、3外,GQL标准不存在左侧约束。
RETURN 1. 不支持返回变长边变量,例如“MATCH p=(a: Person)-[e]->{0, 2}(d) RETURN e;”,只能返回变量p,a,b,不能返回变长边变量e。
2. 不支持RETURN *。
3. RETURN子句后不能出现“()-[]->()”等图匹配形式。
4. 返回结果中(变量、属性、表达式)每一列的字节长度限制为64 * 1024字节,包含结尾符‘\0’在内。
5. RETURN返回点、边、路径变量时,返回结果(json字符串)不包含内容为null的属性列。
6. 未显示指定GROUP KEY的聚集查询,不允许返回“变量.属性”字段,允许返回重复列(包括重复字段列、聚集函数拓展列、COUNT(*))。
7. 显式指定GROUP KEY的聚集查询,RETURN中的“变量.属性”字段必须和GROUP KEY相同,不允许返回部分GROUP KEY字段、不存在的“变量.属性”字段以及重复列(包括重复字段列、聚集函数拓展列、COUNT(*))。
8. 显示指定GROUP KEY的聚集查询,聚集查询返回的列的排列方式为:GROUP KEY字段+聚集函数拓展列。
9.存在聚集查询时,不允许在RETURN中返回表达式和基础函数。
10. GQL语句中有聚集函数时,只支持返回属性列或者聚集函数列,不支持RETURN点边变量或者路径变量。
11. 列别名可在ORDER BY中使用,但不能在GROUP BY中使用。
12. 不允许存在重复的列别名。
13. 列别名不区分大小写。
除3外,GQL标准不存在左侧约束。
LIMIT 不支持LIMIT后使用负数。 无差异。
OFFSET 不支持OFFSET后使用负数。 不支持SKIP作为OFFSET的同义词。
ORDER BY 1. 不支持用数字指代RETURN子句中的投影列进行排序。
2. 不支持对整个变量进行排序。
3. 不支持ORDER BY后使用聚集函数。
4. 新增如下关键字:
保留关键字ORDER、BY、ASC、ASCENDING、DESC、DESCENDING、NULLS。非保留关键字:FIRST、LAST。
5. 当存在聚合查询时,ORDER BY必须与GROUP BY配合使用。
6. 当将ORDER BY与GROUP BY配合使用时,排序KEY中所使用的属性列必须在投影结果中存在。
7. 未指定排序顺序时,默认升序排列。
8. 未指定NULL值优先级时,默认NULL值优先级最低。
GQL标准中未明确指定约束1。不存在约束2、3。
GROUP BY 1. GROUP KEY数量上限为32个。
2. GROUP KEY不支持对无标签变量的字段进行分组。即作为GROUP BY后接的KEY在MATCH子句中的变量必须带标签。
3. GROUP KEY只能为变量.属性,如a.prop,不支持对点边标签、点边变量、路径、变长边及其上的字段进行分组。
4. GROUP KEY不能重复,包括重复的字段列,重复的拓展聚焦列。
左侧约束为GQL标准的子集。

运算、函数规格及约束

运算/函数 规格说明 与GQL标准差异
算数运算 1. 支持+、-、*、/、%。
2. 支持定长与定长类型的运算,不支持变长类型的算数运算,不支持定长与变长类型的算数运算。
3. 当将高精度类型的数据,设置给低精度类型字段时,会丢失精度。
GQL标准中不存在约束2。
比较运算 1. 支持=、!=、>、>=、<、<=、<>。
2. 不支持连续的运算,比如0<=F1<=10,需要改写为0<=F1 AND F1<=10,直接写0<=F1<=10相当于(0<=F1)<=10。
3. 支持定长与定长类型的运算,支持变长与变长类型的运算,不支持定长与变长类型的比较运算。
4. 浮点数的精度定位误差为+/-0.000000000000001。
5. 不支持(a, b) < (1, 2)的比较。
除第1点外,GQL标准不包含其他约束。
逻辑运算 1. 支持AND、OR、NOT、IS NULL、IS NOT NULL、IN、NOT IN、LIKE、NOT LIKE、||(字符串拼接)。
2. 对于AND、OR、NOT运算符,其操作数会被强制转为bool类型,例如:WHERE 0.00001 AND '0.1',0.00001是浮点数,与0比较精度定义误差为+/-0.000000000000001,因此0.00001不等于0,转换为bool后为true,'0.1'为字符串类型,首先从字符串转换为double类型的0.1,然后与0比较时不等于0,转换为bool后为true。
3. 对于LIKE、NOT LIKE运算符,其操作数会被强转为字符串类型,例如:WHERE 0.5 LIKE 0.5,0.5会被强转为字符串'0.5',相当于WHERE '0.5' LIKE '0.5',结果为true。
4. IN、NOT IN目前不支持右边为子查询,会报31300009错误码。
除1外,GQL标准不存在左侧约束。
时间函数 1. 仅支持DATE()、LOCAL_TIME()、LOCAL_DATETIME()。
2. 入参支持的time-value格式:
YYYY-MM-DD
YYYY-MM-DD HH:MM
YYYY-MM-DD HH:MM:SS
YYYY-MM-DDTHH:MM
YYYY-MM-DDTHH:MM:SS
HH:MM
HH:MM:SS
3. 不支持函数嵌套。
4. 入参只支持字符串字面量。
不支持从记录中解析日期,例如:date({year: 1984, month: 11, day: 27})。
取整函数 1. 支持FLOOR()、CEIL()/CEILING()。
2. 入参必须为数字型。
3. 不支持函数嵌套。
4. 不支持科学计数法作为函数入参。
GQL标准中不存在约束4。
字符串函数 1. 支持CHAR_LENGTH()/CHARACTER_LENGTH()、LOWER()、UPPER()、SUBSTR()/SUBSTRING()、SUBSET_OF()。
2. 除SUBSTR()/SUBSTRING()外,其他函数入参必须为字符串。SUBSTR()/SUBSTRING()的第一个参数为字符串类型,第二个参数和第三个参数为数值类型。
3. 入参使用字符串拼接运算符‘||’时允许拼接数字类型。
4. SUBSTR()/SUBSTRING()、SUBSET_OF()的参数可以互相嵌套,此外其他函数不支持函数嵌套。
5. 不支持科学计数法作为函数入参。
6. SUBSTR()/SUBSTRING()参数的个数必须是3,第一个入参是原始字符串,第二个入参是从第几个(左起第一个字符填1,右起第一个字符填-1)字符开始切分子串,第三个入参表示子串的长度。第二个入参和第三个入参如果是浮点型,会向下取整。
7. SUBSET_OF()第一个参数是原始字符串,第二个参数是查询字符串,第三个参数是分词符,返回结果为bool类型(即返回1或0),分词符字符串长度必须是1,前两个参数的开头和结尾不能有多余的分词符,分词符不可以连续。
GQL标准中不存在约束4。
聚集函数 1. 聚集函数只支持SUM、MAX、MIN、AVG以及COUNT,不支持FIRST和LAST。
2. 聚集函数内只允许存在单列有效的“变量.属性”字段,不允许使用空值、多个字段、不存在字段、表达式、变量等,不支持使用无标签变量的属性字段。
3. 不支持聚合函数(内/间)的表达式运算、聚集函数嵌套。
4. 聚集函数内计算的字段类型仅支持:INTEGER/BOOLEAN/DOUBLE/STRING类型,与GQL支持的数据类型一致。
5. 在GQL中,如果单条查询超过100MB,不会使用临时文件,会报31300004错误码。
左侧约束为GQL标准的子集。
类型转换函数 1. 不支持函数嵌套。
2. 不支持科学计数法作为函数入参。
3. CAST AS INT
  i. 入参支持STRING、INTEGER、BOOLEAN、DOUBLE。
  ii. 入参为true返回1,false返回0。
  iii. 入参不能被转换为INT的字符串将会报错。
  iv. 入参为浮点数截断返回整数。
4. CAST AS BOOL
  i. 入参支持INTEGER、BOOLEAN、DOUBLE。
  ii. 不支持CAST('true' AS BOOL)。
  ii. 输入为BOOLEAN内部实际使用INT类型,0代表false,1代表true,转其他任何INTEGER为BOOLEAN返回其本身。
5. CAST AS DOUBLE
  i. 入参支持STRING、INTEGER、BOOLEAN、DOUBLE。
  ii. 入参不能被转换为DOUBLE的字符串将会报错。
6. CAST AS STRING
  i. 入参支持STRING、INTEGER、BOOLEAN、DOUBLE。
  ii. CAST(true AS STRING)返回值为'1'。
GQL标准中不支持BOOL与INT以及DOUBLE之间的转换。

索引规格及约束

索引是优化查询性能的关键工具,其核心在于加速节点和边的属性查找。具体规格及约束见下表:

说明:

GQL标准不存在索引相关语法。

规格项 规格说明
索引名长度 索引名长度上限为128字节,不区分大小写,且不能与标签名相同(不区分大小写)。
索引的大小 单条索引中,所有索引列的总大小不能超过1024字节。
变长字段索引长度 设置变长字段为Key时,该字段的大小应小于1024字节。
索引使用约束 需满足连续最左匹配原则,否则索引不生效,将全表扫描。
1. BTree不支持联合索引同时指定多个字段的范围查询,例如{0<F1<10, 0<F2<10}。
2. BTree不支持联合索引非连续字段查询,例如在F1,F2,F3,F4建索引,指定{F1, F3}这样的非连续前缀字段的条件。
复合索引 单个索引包含的列数上限是32。
索引名唯一性 允许不同标签中存在相同名称的索引,例如,t1.id和t2.id,id是索引名。
创建索引 1. 唯一索引中,重复的NULL值不会报索引冲突。
2. 单个标签上最多可以创建10个索引。
3. 创建属性图时不支持使用Primary Key和Unique关键字来创建索引,仅支持通过创建索引语句建立索引。
4. 创建索引时可通过指定Unique关键字来创建唯一索引。
删除索引 删除索引时需指定索引所属的标签名,例如:Drop Index label.index。
索引升降序 ASC:升序,DESC:降序,默认升序,暂不支持设置升降序。
表达式索引 暂不支持。

事务规格及约束

规格项 规格说明 与GQL标准差异
显式事务 1. 默认可串行化隔离级别。
2. 暂不支持SAVEPOINT(数据库中用于事务管理的重要机制,具体表现为在事务内部创建标记点,以便进行部分回滚。)。
3. 不支持DDL与DML混合事务,不支持DDL事务,不支持DDL事务回滚。
4. 当前事务中的单条语句执行失败,回滚单条语句。
5. 事务必须显示地提交或者回滚,否则事务都将回滚。
6. 不能在无事务状态下提交或回滚事务。
7. 同时创建两个事务,写写互斥,读写/写读互斥,读读并发。
8. 长事务资源:事务中操作上限和缓存大小与undo日志相关,受文件系统空间限制,同时存在的锁等待线程数与开库连接上限相关。
GQL标准支持基本的事务语法,不支持SAVEPOINT,支持开启只读事务和读写事务。
并发操作 支持多并发,仅支持可串行化隔离级别。多线程涉及写操作的并发会有一定程度的阻塞。 GQL标准支持SQL使用的所有隔离级别。

其他规格及约束

  • 系统默认日志方式是WAL(Write Ahead Log)模式,系统默认落盘方式是FULL模式。

  • 为确保数据准确无误,数据库在同一时间仅支持单一写操作。

  • 应用卸载完成后,设备将自动清除相关数据库文件和临时文件。

  • 不支持多进程。

  • 暂不支持图数据库的备份和恢复。

接口说明

以下是图数据库持久化功能的相关接口,更多接口及使用方式请见图数据库(系统接口)

接口名称 描述
getStore(context: Context, config: StoreConfig): Promise<GraphStore> 获得一个GraphStore,操作图数据库,可以根据需求配置GraphStore的参数,然后通过GraphStore调用相关接口执行数据操作。
read(gql: string): Promise<Result> 执行数据查询语句,返回查询结果。
write(gql: string): Promise<Result> 执行数据写入语句(增、删、改)。
close(): Promise<void> 关闭数据库,回滚所有未提交的事务。
createTransaction(): Promise<Transaction> 创建事务实例。
Transaction.read(gql: string): Promise<Result> 使用事务实例执行数据查询语句,返回查询结果。
Transaction.write(gql: string): Promise<Result> 使用事务实例执行数据写入语句(增、删、改)。
Transaction.commit(): Promise<void> 提交当前事务中已经执行的GQL语句。
Transaction.rollback(): Promise<void> 回滚当前事务中已经执行的GQL语句。
deleteStore(context: Context, config: StoreConfig): Promise<void> 使用指定的数据库文件配置删除数据库。

开发步骤

下文仅提供Stage模型下的示例代码。

  1. 使用图数据库实现数据持久化,需要调用getStore()接口获取一个GraphStore,其中包括建库、变更安全等级、变更为加密数据库等操作。示例代码如下所示:

    import { graphStore } from '@kit.ArkData'; // 导入模块
    import { UIAbility } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { window } from '@kit.ArkUI';
    
    let store: graphStore.GraphStore | null = null;
    
    const STORE_CONFIG: graphStore.StoreConfig = {
      name: "testGraphDb", // 数据库文件名,不需要加.db后缀
      securityLevel: graphStore.SecurityLevel.S2, // 数据库安全级别
      encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
    };
    
    const STORE_CONFIG_NEW: graphStore.StoreConfig = {
      name: "testGraphDb", // 数据库文件名需要与之前开库使用的文件名保持一致
      securityLevel: graphStore.SecurityLevel.S3,
      encrypt: true,
    };
    
    // 此示例在EntryAbility中实现,也可以在其他合理场景中使用
    class EntryAbility extends UIAbility {
      onWindowStageCreate(windowStage: window.WindowStage) {
        graphStore.getStore(this.context, STORE_CONFIG).then(async (gdb: graphStore.GraphStore) => {
          store = gdb;
          console.info('Get GraphStore successfully.')
        }).catch((err: BusinessError) => {
          console.error(`Get GraphStore failed, code is ${err.code}, message is ${err.message}`);
        })
    
        // 变更安全等级、变更为加密数据库前需要先调用close()接口关闭数据库
        if(store != null) {
          (store as graphStore.GraphStore).close().then(() => {
            console.info(`Close successfully`);
    
            graphStore.getStore(this.context, STORE_CONFIG_NEW).then(async (gdb: graphStore.GraphStore) => {
              store = gdb;
              console.info('Update StoreConfig successfully.')
            }).catch((err: BusinessError) => {
              console.error(`Update StoreConfig failed, code is ${err.code}, message is ${err.message}`);
            })
          }).catch ((err: BusinessError) => {
            console.error(`Close failed, code is ${err.code}, message is ${err.message}`);
          })
        }
      }
    }
    
  2. 获取到GraphStore后,调用write()接口创建图。示例代码如下所示:

    const CREATE_GRAPH = "CREATE GRAPH test " +
      "{ (person:Person {name STRING, age INT}),(person)-[:Friend {year INT}]->(person) };"
    
    if(store != null) {
      (store as graphStore.GraphStore).write(CREATE_GRAPH).then(() => {
        console.info('Create graph successfully');
      }).catch((err: BusinessError) => {
        console.error(`Create graph failed, code is ${err.code}, message is ${err.message}`);
      })
    }
    
  3. 成功创建图后,调用write()接口插入、更新顶点及边。示例代码如下所示:

    说明:

    图数据库没有显式的flush操作实现持久化,数据插入即保存在持久化文件。

    const INSERT_VERTEX_1 = "INSERT (:Person {name: 'name_1', age: 11});";
    const INSERT_VERTEX_2 = "INSERT (:Person {name: 'name_2', age: 22});";
    const INSERT_VERTEX_3 = "INSERT (:Person {name: 'name_3', age: 0});";
    
    const UPDATE_VERTEX_3 = "MATCH (p:Person) WHERE p.name='name_3' SET p.age=33;"
    
    const INSERT_EDGE_12 = "MATCH (p1:Person {name: 'name_1'}), (p2:Person {name: 'name_2'}) " +
      "INSERT (p1)-[:Friend {year: 12}]->(p2);";
    const INSERT_EDGE_23 = "MATCH (p2:Person {name: 'name_2'}), (p3:Person {name: 'name_3'}) " +
      "INSERT (p2)-[:Friend {year: 0}]->(p3);";
    
    const UPDATE_EDGE_23 = "MATCH (p2:Person {name: 'name_2'})-[relation:Friend]->(p3:Person {name: 'name_3'})" +
      " SET relation.year=23;";
     
    let writeList = [
     INSERT_VERTEX_1,
     INSERT_VERTEX_2,
     INSERT_VERTEX_3,
     UPDATE_VERTEX_3,
     INSERT_EDGE_12,
     INSERT_EDGE_23,
     UPDATE_EDGE_23,
    ]
    
    if(store != null) {
      writeList.forEach((gql) => {
        (store as graphStore.GraphStore).write(gql).then(() => {
          console.info('Write successfully');
        }).catch((err: BusinessError) => {
          console.error(`Write failed, code is ${err.code}, message is ${err.message}`);
        });
      });
    }
    
  4. 调用read()接口查询顶点、边、路径。示例代码如下所示:

    const QUERY_VERTEX = "MATCH (person:Person) RETURN person;"
    
    const QUERY_EDGE = "MATCH ()-[relation:Friend]->() RETURN relation;"
    
    const QUERY_PATH = "MATCH path=(a:Person {name: 'name_1'})-[]->{2, 2}(b:Person {name: 'name_3'}) RETURN path;"
    
    if(store != null) {
      (store as graphStore.GraphStore).read(QUERY_VERTEX).then((result: graphStore.Result) => {
        console.info('Query vertex successfully');
        result.records?.forEach((data) => {
          for (let item of Object.entries(data)) {
            const key = item[0];
            const value = item[1];
            const vertex = value as graphStore.Vertex;
            console.info(`key : ${key}, vertex.properties : ${JSON.stringify(vertex.properties)}`);
          }
        });
      }).catch((err: BusinessError) => {
        console.error(`Query vertex failed, code is ${err.code}, message is ${err.message}`);
      });
    
      (store as graphStore.GraphStore).read(QUERY_EDGE).then((result: graphStore.Result) => {
        console.info('Query edge successfully');
        result.records?.forEach((data) => {
          for (let item of Object.entries(data)) {
            const key = item[0];
            const value = item[1];
            const edge = value as graphStore.Edge;
            console.info(`key : ${key}, edge.properties : ${JSON.stringify(edge.properties)}`);
          }
        });
      }).catch((err: BusinessError) => {
        console.error(`Query edge failed, code is ${err.code}, message is ${err.message}`);
      });
    
      (store as graphStore.GraphStore).read(QUERY_PATH).then((result: graphStore.Result) => {
        console.info('Query path successfully');
        result.records?.forEach((data) => {
          for (let item of Object.entries(data)) {
            const key = item[0];
            const value = item[1];
            const path = value as graphStore.Path;
            console.info(`key : ${key}, path.length : ${path.length}`);
          }
        });
      }).catch((err: BusinessError) => {
        console.error(`Query path failed, code is ${err.code}, message is ${err.message}`);
      })
    }
    
  5. 调用write()接口删除顶点、边。示例代码如下所示:

    const DELETE_VERTEX_AND_RELATED_EDGE = "MATCH (p:Person {name: 'name_1'}) DETACH DELETE p;"
    
    const DELETE_EDGE_ONLY = "MATCH (p2:Person {name: 'name_2'})-[relation: Friend]->(p3:Person {name: 'name_3'})" +
      " DETACH DELETE relation;"
    
    if(store != null) {
      (store as graphStore.GraphStore).write(DELETE_VERTEX_AND_RELATED_EDGE).then(() => {
        console.info('Delete vertex and related edge successfully');
      }).catch((err: BusinessError) => {
        console.error(`Delete vertex and related edge failed, code is ${err.code}, message is ${err.message}`);
      });
    
      (store as graphStore.GraphStore).write(DELETE_EDGE_ONLY).then(() => {
        console.info('Delete edge only successfully');
      }).catch((err: BusinessError) => {
        console.error(`Delete edge only failed, code is ${err.code}, message is ${err.message}`);
      })
    }
    
  6. 创建事务并使用事务进行写入、查询、提交、回滚。示例代码如下所示:

    let transactionRead: graphStore.Transaction | null = null;
    let transactionWrite: graphStore.Transaction | null = null;
    
    const INSERT = "INSERT (:Person {name: 'name_5', age: 55});";
    
    const QUERY = "MATCH (person:Person) RETURN person;";
    
    if(store != null) {
      (store as graphStore.GraphStore).createTransaction().then((trans: graphStore.Transaction) => {
        transactionRead = trans;
        console.info('Create transactionRead successfully');
      }).catch((err: BusinessError) => {
        console.error(`Create transactionRead failed, code is ${err.code}, message is ${err.message}`);
      });
    
      (store as graphStore.GraphStore).createTransaction().then((trans: graphStore.Transaction) => {
        transactionWrite = trans;
        console.info('Create transactionWrite successfully');
      }).catch((err: BusinessError) => {
        console.error(`Create transactionWrite failed, code is ${err.code}, message is ${err.message}`);
      });
    
      if(transactionRead != null) {
        (transactionRead as graphStore.Transaction).read(QUERY).then((result: graphStore.Result) => {
          console.info('Transaction read successfully');
          result.records?.forEach((data) => {
            for (let item of Object.entries(data)) {
              const key = item[0];
              const value = item[1];
              const vertex = value as graphStore.Vertex;
              console.info(`key : ${key}, vertex.properties : ${JSON.stringify(vertex.properties)}`);
            }
          });
        }).catch((err: BusinessError) => {
          console.error(`Transaction read failed, code is ${err.code}, message is ${err.message}`);
        });
    
        (transactionRead as graphStore.Transaction).rollback().then(() => {
          console.info(`Rollback successfully`);
          transactionRead = null;
        }).catch ((err: BusinessError) => {
          console.error(`Rollback failed, code is ${err.code}, message is ${err.message}`);
        })
      }
    
      if(transactionWrite != null) {
        (transactionWrite as graphStore.Transaction).write(INSERT).then(() => {
          console.info('Transaction write successfully');
        }).catch((err: BusinessError) => {
          console.error(`Transaction write failed, code is ${err.code}, message is ${err.message}`);
        });
    
        (transactionWrite as graphStore.Transaction).commit().then(() => {
          console.info(`Commit successfully`);
          transactionWrite = null;
        }).catch ((err: BusinessError) => {
          console.error(`Commit failed, code is ${err.code}, message is ${err.message}`);
        })
      }
    }
    
  7. 删除数据库。调用deleteStore()方法,删除数据库及其相关文件。示例代码如下所示:

    const DROP_GRAPH_GQL = "DROP GRAPH test;"
    
    class EntryAbility extends UIAbility {
      onWindowStageDestroy() {
        if(store != null) {
          // 删除图,可忽略此步
          (store as graphStore.GraphStore).write(DROP_GRAPH_GQL).then(() => {
            console.info('Drop graph successfully');
          }).catch((err: BusinessError) => {
            console.error(`Drop graph failed, code is ${err.code}, message is ${err.message}`);
          });
    
          // 关闭数据库,示例在EntryAbility中实现,使用者也可以在其他合理场景中使用
          (store as graphStore.GraphStore).close().then(() => {
            console.info(`Close successfully`);
          }).catch ((err: BusinessError) => {
            console.error(`Close failed, code is ${err.code}, message is ${err.message}`);
          })
        }
    
        // 删库时使用的StoreConfig应与最后一次开库时使用的StoreConfig保持一致
        graphStore.deleteStore(this.context, STORE_CONFIG_NEW).then(() => {
          store = null;
          console.info('Delete GraphStore successfully.');
        }).catch((err: BusinessError) => {
          console.error(`Delete GraphStore failed, code is ${err.code},message is ${err.message}`);
        })
      }
    }