创建数据加载任务

有了图数据库Schema,便可以将数据导入到图数据库中。 本GSQL语言提供易于理解和使用的数据加载命令,可执行企业的ETL(提取,转换和加载)系统中的许多相同的数据转换、映射、过滤和合并操作。

GSQL系统可以从文本文件中读取结构化或半结构化的数据。加载语言可识别表格或JSON数据类型,但条件子句和数据操作函数(data manipulation function)能够读取更复杂或不规则构造的数据。对于表格类数据,每一行都包含一系列值,须以逗号,制表符,空格或任何其他指定的ASCII字符进行分隔(仅支持单字符分隔符)。每一行除了数据值和分隔符外,不能包含额外的空格。在表格视图中,每行数据就是一行(row),每行包含一组不同的列值(column value)。

加载数据分为两步。第一步,定义加载作业。第二步,使用RUN语句执行作业。对于这两个语句以及它们包含的组件,详述如下。

加载作业的语句结构会按照层级,自上而下展示:

CREAT…JOB命令会包含一系列的DEFINE和LOAD语句

  • DEFINE 声明语句

  • LOAD 声明语句,它包含了数个子句

新版本中加载作业的新功能

从2.0版本开始,TigerGraph平台增加了新的语法来创建和执行加载作业,该更新提供了数个新的优势:

  • TigerGraph平台支持数个加载作业同时执行,从而大幅提升带宽。

  • 数据文件路径可以在编译时指定,也可以在执行时指定。执行时的设定会覆盖编译时的设定。

  • 创建加载作业时可以包含一组输入文件。在执行作业时,用户可以指定只导入某一些输入文件,从而实现只执行作业的一部分。

  • 加载作业可以被监控、中止和重启。

并发加载

RESTPP组件有很多功能,最主要的功能就是负责管理加载作业。以前,RESTPP只能同时管理一个加载作业。但在2.0版本中,它可以包含多个RESTPP-LOADER子组件,每个子组件可以独立地执行一个加载作业。并行加载作业允许的最大数目,由配置参数RESTPP-LOADER.Replicas定义。

此外,如果TigerGraph图分布在数个计算机节点上,每台计算机的RESTPP-LOADER都可以投入使用。每个RESTPP-LOADER只读取本地的数据源文件,但计算出的结果可以存储在集群中的任何计算机节点上。

为了使集群的加载性能最大化,建议在每个节点上至少使用两个加载器,并使得每个加载器上处理数据量大致相当。

为了使新功能生效,我们开发了一套补充语法,用于建立和执行加载作业。下面的例子简要展示了这些改动项和添加项。如需了解细节,请参阅本文档的提示部分(GSQL 语言参考- 第一部分)

  • 一个加载作业的开头为CREATE LOADING JOB。(该版本中包括了关键词LOADING)

  • 增加了一个新的参数DEFINE FILENAME用于定义文件名变量。

  • 文件路径可以在是本机上,或指定的计算机上,亦或是所有计算机上。

  • 当一个作业开始的时候,系统会分配一个job_id给这个作业。通过这个id,使用者可以检查该作业的状态,也可以终止或重启该作业。

下面是一个简单的例子:

建立并行加载作业示例:
CREATE LOADING JOB job1 FOR GRAPH graph1 {
 
  DEFINE FILENAME file1 = "/data/v1.csv";
  DEFINE FILENAME file2;
 
  LOAD file1 TO VERTEX v1 VALUES ($0, $1, $2);
  LOAD file2 TO EDGE e2 VALUES ($0, $1);
}
RUN LOADING JOB job1 USING file1="m1:/data/v1_1.csv", file2="m2:/data/e2.csv"

一个包含并行作业的加载可以在逻辑上按照每个文件变量被分解为不同的部分。当一个包含并行作业的加载被编译的时候,针对每个文件变量,都会添加一个RESTPP终止符(RESTPP endpoint)。这样,这个作业便可以依照每个文件变量分步执行。

不再建议使用2.0版本之前的CREATE JOB语法

请使用新版本中包含了DEFINE FILENAME的CREATE LOADING JOB语法,这样能够利用新版本中的并发加载功能。

2.0版本之前的的语法仍然支持,但是不建议使用,因为2.0版本之前的加载操作的语法不支持并发加载功能。

你可以在TigerGraph安装目录的/doc/examples/gsql_ref 文件夹中,找到前文中用户书籍评分图框架的加载作业和数据文件范例。

创建加载任务

可以通过加载作业的标头(header)轻松区分CREATE LOADING JOB属于2.0版本还是2.0之前的版本,此外,也可以通过判断是否包含DEFINE FILENAME子句,来区分作业的版本。当加载作业的版本确认后,便可以选择正确的LOAD和RUN的格式。

加载作业类型

块标头

是否包含DEFINE FILENAME?

执行

2.0版本的加载作业

CREATE LOADING JOB

RUN LOADING JOB

非并发离线加载(不建议)

CREATE LOADING JOB

RUN JOB

非并发在线加载(不建议)

CREATE ONLINE_POST JOB

不允许

RUN JOB USING FILENAME...

CREATE LOADING JOB和DROP LOADING JOB的权限预留给设计者、管理员和超级用户。

CREATE LOADING JOB

CREATE LOADING JOB定义了DEFINE、LOAD和DELETE语句块,用于向指定图中加载数据或将数据从中移除。操作的顺序须用括号括起来。语句块中的每个语句,包括最后一个语句,都须用分号结尾。

离线加载CREATE LOAD
CREATE LOADING JOB job_name FOR GRAPH graph_name {
  [zero or more DEFINE statements, each ending in a semicolon]
  [zero or more LOAD statements, each ending in a semicolon]
  [zero or more DELETE statements, each ending in a semicolon]
}

DROP JOB语法

要丢弃(删除)一个作业,只需要执行命令"DROP JOB job_name",这样便可以将该作业从GSQL中删除。若想删除所有作业,则可以运行下面两个命令中的任何一个:

  • DROP JOB ALL

  • DROP JOB *

参数ALL所作用的范围取决于用户的当前的工作范围。如果用户设定了工作图,则DROP ALL会删除该工作图中的所有作业。如果一个超级用户将他的工作范围设定为全局,则DROP ALL会删除所有图空间中的作业。

DEFINE的语法

DEFINE语句用于定义一个局部变量或表达式,用于被其后的LOAD语句调用。

DEFINE FILENAME

DEFINE FILENAME语句定义一个文件名变量。这个变量可以在之后的JOB语句块中通过LOAD语句调用,用于指定数据来源。每个并行的加载作业都必须至少包括一个DEFINE FILENAME声明。

DEFINE FILENAME filevar ["=" filepath_string ]; 
filepath_string = ( path | " all :" path | " any :" path | mach_aliases " :" path ["," mach_aliases ":" path ]* ) 
mach_aliases = name["|"name]*

可以选择在filevar之后添加filepath_string参数,用于告诉加载作业去哪里寻找数据源。从命名中可以看出,filepath_string参数是一个字符串值。所以,它必须用双引号括起来。

filepath_string

filepath_string参数有四种形式:

  • path: 指定了加载作业在该计算机上运行的路径,路径可以是绝对路径或相对路径,可以是文件也可以是文件夹。如果是文件夹,则加载器会尝试加载该文件夹中所有的非隐藏文件。

path 的例子
"/data/graph.csv"

若CREATE LOADING JOB执行时发现该路径不存在,则GSQL会报告错误。

绝对路径可以用会话变量$sys.data_root开头:

例: 在加载作业中使用 $sys.data_root
CREATE LOADING JOB filePathEx FOR GRAPH gsql_demo {
 LOAD "$sys.data_root/persons.csv" TO ...
} 

接下来,当执行加载作业时,先为该变量赋值再执行作业:

例: 设定sys.data_root 会话参数
SET sys.data_root="/data/mydata"
RUN JOB filePathEx

从名字中可以看出,会话参数仅在当前GSQL会话中保留。当用户退出当前会话,则该赋值失效。

  • "all:" path : 如果路径前添加前缀all:,则加载作业会尝试在集群中所有拥有RESTPP组件的计算机上执行,每台计算机都会尝试在本地寻找path路径中的数据。如果该路径在本机不存在,则作业终止。同时,会话变量$sys.data_root在此种条件下不可用。

ALL:path 的例子
"ALL:/data/graph.csv"   
  • any:" path :如果路径前添加前缀any:,则加载作业会尝试在集群中所有拥有RESTPP组件的计算机上执行,每台计算机都会尝试在本地寻找path路径中的数据。如果该路径在本机不存在,则作业会跳过该计算机节点。同时,会话变量$sys.data_root在此种条件下不可用。

ANY:path 的例子
"ANY:/data/graph.csv"
  • . 路径列表,列表指定了每台计算机上的具体文件路径:machine_alias是诸如m1,m2这样的名称,它们在设定集群配置的时候定义。这种情况下,filepath_string值可以包含一系列的路径,由逗号分隔。如果数个计算机节点指定的路径相同,则可以合并写入。不同的machine_alias由竖线“|“分隔。加载作业会在列表中列出的所有计算机上运行。每台计算机的RESTPP-LOADER组件会在本机运行。

机器-路径列表的例子
"m1:/data1.csv, m2|m3|m5:/data/data2.csv"

DEFINE HEADER

DEFINE HEADER语句定义数据源文件的列的名称。第一列的名称对应第一列,第二列的名称对应第二列,以此类推。

DEFINE HEADER header_name = " column_name "[," column_name "]*;

DEFINE INPUT_LINE_FILTER

DEFINE INPUT_LINE_FILTER语句定了一个布尔表达式,它的值基于数据源中的某一行的列属性。当它与子句USING reject_line_rule一并在LOAD语句中使用时,过滤器会判断是否忽略源数据的某一行。

DEFINE INPUT_LINE_FILTER filter_name = boolean_expression_using_column_variables ;

LOAD的语法

LOAD语句告诉GSQL加载器如何将行数据解析为列值,被称作代号(token),然后描述如何使用这些值来创建新的顶点或边实例。 一个LOAD语句可用于生成多个顶点或边,每个顶点或边拥有自己的目标子句(Destination_Clause),如下例所示。 另外,两个或多个LOAD语句可以引入相同的源数据文件。 在这种情况下,GSQL加载器将合并操作,只需遍历一遍数据文件就能完成两次加载操作。

LOAD语句有很多选项。 本参考指南只展示了最主要的功能与示例。 平台知识库(Platform Knowledge Base)和常见问题解答(FAQs ),以及教程(如Get Started with TigerGraph )提供了更多面向解决方案和应用的示例。

不同的LOAD语句类型对应不同的USING子句的使用方式; 有关此方面的详细信息,请参阅下面的USING 子句部分

LOAD statement

LOAD [filepath_string|filevar|TEMP_TABLE table_name ] Destination_Clause [, Destination_Clause ]* [USING clause ];

filevar必须已经在DEFINE FILENAME语句中定义过。

filepath_string必须遵循与前文DEFINE FILENAME的相同规则。

__GSQL_FILENAME_n__":基于位置属性的文件标识符

执行CREATE LOADING JOB语句块时,GSQL会判断唯一的filepath_strings的数量,并自上而下地为它们分配基于位置的索引号(index)0,1,2等。 每个filepath_string都会被认定为单个个体,即便它可能包含多个机器索引值和文件位置路径。随后,这些索引号可被用作filespath_strings的备用名:

执行加载作业时,第n个filepath_string可以被叫做“__GSQL_FILENAME_n__”,其中n替换为索引号。 请注意,该字符串在左端和右端都有双下划线。

本章节的其余部分将介绍有关file_path,Destination_Clause及其子句的格式和用法的详细信息。 USING子句将在后面的“其他可选LOAD子句”一节中介绍。

目标子句(Destination Clause)

目标子句(Destination_Clause)描述了如何使用数据源中的代号(token)来构造三种类型的数据对象:在临时表(TEMP_TABLE)中的顶点,边或行。 这三种类型的目标子句的格式非常相似,但为了清楚起见,下面将依次介绍:

顶点目标子句
TO VERTEX vertex_type_name VALUES (id_expr [, attr_expr]*)
   [WHERE conditions] [OPTION (options)]
变目标子句
TO EDGE edge_type_name VALUES (source_id_expr, target_id_expr [, attr_expr]*)
   [WHERE conditions] [OPTION (options)]
临时表目标子句
TO TEMP_TABLE table_name (id_name [, attr_name]*) VALUES (id_expr [, attr_expr]*)
   [WHERE conditions] [OPTION (options)]

对于TO VERTEX和TO EDGE目标子句,vertex_type_name或edge_type_name必须和在CREATE VERTEX或CREATE UNDIRECTED | DIRECTED EDGE语句中已经定义了的顶点类或边类的名称匹配。 VALUE列表中的值(id_expr,attr_expr1,attr_expr2,...)将按照它们在CREATE语句中列出的顺序分配给新的顶点实例或边实例,成为它们的id和属性。 id_expr与 attr_expr遵循相同的规则,而只有attr_expr才可以使用reducer函数。之后会有详细介绍。

相反,TO TEMP_TABLE子句定义了一个新的临时数据结构。 其独特性将在一个单独的小节中描述。 此刻让我们还是暂时专注于TO VERTEX和TO EDGE。

属性和属性表达式

LOAD语句处理输入文件的每一行,将每一行(根据SEPARATOR字符,参见章节“其他可选LOAD子句”以获取更多详细信息)拆分为一系列的Token。 每个目标子句提供“Token-属性”映射,该映射定义如何构造新的顶点,边或临时表行实例(例如一个数据对象)。 Token也可以被认为是表中的列值。 指定列可以用两种方法,按位置或按名称。 假设列具有名称,则可以使用任一一种方法,并且可以在同一个表达式内使用这两种方法。

  • 按位置:列值(Token)从左到右编号,从$ 0开始。 下一列是$ 1,依此类推。

  • 按名称:可以通过输入文件中的标题行或通过DEFINE HEADER语句命名每个列。 如果有标题行,则输入文件的第一行应使用与数据行相同的分隔符,除非每一列都包含列名字符串而不是数据值。 名称用双引号括起来,例如$"age"。

  • 数据文件名:$ sys.file_name是指当前输入的数据文件名。

在简单的情况下,Token值直接复制到属性。 例如,在以下LOAD语句中,

例: 用 $sys.file_name 赋值属性值
LOAD "xx/yy/a.csv" TO VERTEX person VALUES ($0, $1, $sys.file_name)
  • person顶点的PRIMARY_ID来自文件“xx / yy / a.csv”的第0列。

  • person顶点的下一个属性来自第1列。

  • person顶点的再下一个属性被赋值为“xx / y / a.csv”(文件名本身)。

累积加载(Cumulative Loading)

GSQL 加载器的基本原理是累积加载。 累积加载意味着某个特定数据对象可以被多次写入,并且多个加载的结果取决于写入的顺序。 这通常意味着如果数据行提供了有效的数据对象,且满足WHERE子句和OPTION子句的条件,则数据对象便会被加载。

  • 有效输入:针对源数据中的每一行,每个目标子句会构造一个或多个新的数据对象。 要成为有效的数据对象,它必须具有正确类型的ID值,和属性值,并满足可能存在的WHERE子句。 如果数据对象无效,则拒绝(跳过)该对象并在日志文件中记录该错误。 无效属性值的判别规则如下:

    1. UINT:任何非数字字符。 (超出范围的值会导致溢出而不是拒绝)

    2. INT:任何非数字或非符号字符。 (超出范围的值会导致溢出而不是拒绝)

    3. FLOAT和DOUBLE:任何格式错误

    4. STRING,STRING COMPRESS,FIXED_BINARY:无

    5. DATETIME:格式错误,日期时间无效或超出范围。

    6. 复杂类型:取决于字段类型或元素类型。 任何在UDT中无效字段,在LIST或SET中元素,在MAP中键或值都会导致系统拒绝该对象。

  • 新数据对象:如果一个有效的数据对象具有一个新的ID值,则将数据对象添加到图数据库中。 任何未赋值的属性都将分配为该数据类型或该属性的默认值。

  • 覆盖现有数据对象:如果一个有效数据对象的ID值已经存在,则新对象将覆盖现有数据对象,并带有以下说明和例外:

    1.新对象的属性值覆盖现有数据对象的属性值。

    2.缺少Token:如果输入行中缺少Token,导致生成的属性丢失,则该属性将保留其先前的值。

永远不能没有STRING Token; 假设没有字符,则讲该字符串设定为空字符串

  • 跳过某个属性:若LOAD语句不希望加载某个属性,则可以使用特殊字符 _(即下划线)作为其属性表达式(attr_expr)。 例如,

LOAD TO VERTEX person VALUES ($0, $1, _, $2)

的意思是跳过倒数第二个属性。 这个技巧往往使用在已知输入数据文件不包含某些属性的数据时。

1.如果LOAD正在创建新的顶点或边,则跳过的属性将被赋予默认值。

2.如果LOAD正在覆写现有的顶点或边,则跳过的属性将保留其现有值。

更多复杂属性表达式

属性表达式可以使用列 (例如$ 0),文字(常量数或字符串)或任何内置加载器的Token函数或用户定义的Token函数。 属性表达式可能不包含数学或布尔运算符(例如+,*,AND)。 属性表达式的规则与id表达式的规则相同,但属性表达式还可以使用reducer函数:

  • id_expr := $column_number | $"column_name" | constant | $sys.file_name | token_function_name( id_expr [, id_expr ]* )

  • attr_expr := id_expr | REDUCE(reducer_function_name(id _expr ))

请注意,Token函数可以嵌套,也就是说,一个Token函数可以用作另一个Token函数的输入参数。 内置加载器的Token/reducer函数和用户定义的Token函数在“内置加载器Token函数”一节中详述。

下面的章节描述了有关加载特定数据类型的详细信息。

加载一个 DOUBLE 型或 FLOAT型属性

一个浮点数的基本格式:

[sign][digits].digits[[sign](e|E)digits]

或者

[sign]digits[.[digits]][[sign](e|E)digits]

在第一种格式下,小数点和小数点右边的数字是必须的。在第二种格式下,小数点左边的数字是必须的(看起来像一个整数),小数点和小数点右边的数字可有可没有。

在这两种格式下,正负号(“+”或“-”)可有可没有。可以用指数形式(使用字符e或者E),但不能有逗号和空格。

合规与不合规的浮点数
# Valid floating point values
-198256.03
+16.
-.00036
7.14285e15
9.99E-22
 
 
# Invalid floating point values
-198,256.03
9.99 E-22

加载一个DATETIME属性

将数据加载到一个DATETIME属性时,GSQL加载程序将自动将读取到的日期时间信息转换为内部日期时间格式。 加载程序接受以下一种字符串格式:

  • %Y-%m-%d %H:%M:%S (e.g., 2011-02-03 01:02:03)

  • %Y/%m/%d %H:%M:%S (e.g., 2011/02/03 01:02:03)

  • %Y-%m-%dT%H:%M:%S.000z (e.g., 2011-02-03T01:02:03.123z, 123 will be ignored)

  • %Y-%m-%d (only date, no time, e.g., 2011-02-03 )

  • %Y/%m/%d (only date, no time, e.g., 2011/02/03)

  • 任何整数 (Unix 历, 即1970年1月1日0时0分0秒为整数 0)

格式规则:

%Y 表示年份(四位数),不可用两位数表示年份

%m 和 %s 分别表示月份(1 到 12) 和日期 (1 到 31)。可以选择在开头添加0。

%H, %M, %S 分别表示小时 (0 到23), 分钟(0 到59) 和秒 (0 到59)。可以选择在开头添加0。

当加载数据的时候,加载程序会检查年份、月份、日期、小时、分钟、秒数是否都位于合法的范围以内。若任何一个值超出限定范围(例如出现了2010年13月05日或者2004年04月31日0时0分0秒),则该属性不成立,该对象(顶点或边)也无法建立。

加载一个客户自定义类(UDT)的属性

要加载客户自定义类的属性,请在括号中,首先声明客户自定义类的名称,然后是客户自定义类字段的属性表达式列表。 请参阅下面的示例。

加载UDT 的例子
TYPEDEF TUPLE <f1 INT (1), f2 UINT, f3 STRING (10), f4 DOUBLE > myTuple   # define a UDT
CREATE VERTEX v_udt  (PRIMARY_ID id STRING, att_udt myTuple)
CREATE LOADING JOB load_udt FOR GRAPH test_graph {
   DEFILE FILENAME f;
   LOAD f TO VERTEX v_udt VALUES ($0, myTuple($1, $2, $3, $4) );  # $1 is loaded as f1, $2 is loaded as f2, and so on
}
RUN LOADING JOB v_udt USING f="./udt.csv"

加载一个LIST或SET属性

加载LIST或SET有三种方法。

第一种方法加载拥有相同id值的数行数据,并附上各个属性值,从而构造一个值的集合。 每次从某个符合条件的数据行读取一个值后,该值都会被添加到集合中,逐步形成和扩充该集合。 当加载作业处理一行时,它会检查具有某个id值的顶点或边是否已经存在。 如果不存在(即该id是个新的值),则会创建一个新list或set(只包含一个值,即刚读取到的值),并随之创建一个新的顶点或边。 如果该id已存在,则刚读取到的值会附加到现有list或set中。 下面展了一个示例:

例: 将数行数据累积加载到一个SET或LIST中
CREATE VERTEX test_vertex (PRIMARY_ID id STRING, iset SET<INT>, ilist LIST<INT>)
CREATE UNDIRECTED EDGE test_edge(FROM test_vertex, TO test_vertex)
CREATE GRAPH test_set_list (*)
 
CREATE LOADING JOB load_set_list FOR GRAPH test_set_list {
 DEFINE FILENAME f;
 LOAD f TO VERTEX test_vertex VALUES ($0, $1, $1);                                                                                  
}
RUN LOADING JOB load_set_list USING f="./list_set_vertex.csv"

list_set_vertex.csv
1,10
3,30
1,20
3,30
3,40
1,20

因为数据文件中有两个唯一的id值,作业load_set_list将加载两个test_vertex顶点。 顶点1的属性值为iset = [10,20],ilist = [10,20,20]。 顶点3的值为iset = [30,40],ilist = [30,30,40]。 请注意,集合不包含重复值,而列表可以包含重复值。

由于GSQL加载是多线程的,因此加载到LIST中的值的顺序可能与输入顺序不匹配。

如果输入文件包含多个列,且它们应该被全部添加到LIST或SET中,则可以使用第二种方法。 使用LIST()或SET()函数,如下例所示:

例: 将多个列加载到一个SET/LIST中
CREATE VERTEX v_set  (PRIMARY_ID id STRING, nick_names SET<STRING>)
CREATE VERTEX v_list (PRIMARY_ID id STRING, lucky_nums LIST<INT>)
CREATE GRAPH test_graph (*)
CREATE LOADING JOB load_set_list FOR GRAPH test_graph {
   DEFINE FILENAME f;
   LOAD f TO VERTEX v_set  VALUES ($0, SET($1,$2,$3) );
   LOAD f TO VERTEX v_list VALUES ($0, LIST($2,$4) );
}

第三种方法是使用SPLIT()函数读取复合Token并将其拆分为一个元素的集合,以形成LIST或SET集合。 SPLIT()函数有两个参数:列索引和元素分隔符。 元素分隔符应与整个文件中的分隔符不同。 下面展示了一个示例:

例: 通过SPLIT()载入数据到SET/LIST
CREATE VERTEX test_vertex (PRIMARY_ID id STRING, ustrset SET<STRING>, ilist LIST<INT>)
CREATE UNDIRECTED EDGE test_edge(FROM test_vertex, TO test_vertex)
CREATE GRAPH test_split (*)
 
CREATE LOADING JOB set_list_job FOR GRAPH test_split {
 DEFINE FILENAME f;
 LOAD f TO VERTEX test_vertex VALUES ($0, SPLIT($1,"|") , SPLIT($2,"#") );                                                                                  
}
RUN LOADING JOB set_list_job USING f="./split_list_set.csv"
split_list_set.csv
vid,names,numbers 
v1,mike|tom|jack, 1 # 2 # 3 
v2,john, 5 # 4 # 8

SPLIT()函数不适用于UDT元素类型

加载MAP属性

加载一个MAP有三种方法。

第一种方法是加载拥有相同id值的多行数据。在一个符合条件的数据行中读取一个“键值对”,然后多个键值对逐步形成映射表。 当加载作业处理一行时,它会检查具有该id值的顶点或边是否已存在。如果该id不存在(即该id是个新的值),则使用包含这个键值对的新映射创建新的顶点或边。如果该id已存在,则加载作业将检查该键是否存在于映射中。 如果映射中不存在该键,则插入新的键值对。 否则,该值将替换为新值。

加载顺序可能与原始数据中的顺序不同。 如果数据文件有数个行,具有相同ID相同的键,但值不同,则将它们一起加载后的最终值不确定。

方法1:下面是通过第一种方法加载MAP的语法:使用箭头( - >)分隔映射表中的键和值。

方法1-通过分隔符->加载MAP
CREATE VERTEX v_map  (PRIMARY_ID id STRING, att_map MAP<INT, STRING>)
CREATE GRAPH test_graph (*)
CREATE LOADING JOB load_map FOR GRAPH test_graph {
   DEFINE FILENAME f;
   LOAD f TO VERTEX v_map  VALUES ($0, ($1 -> $2) );
}

方法2:第二种方法是使用MAP()函数。 如果多列之中存在多个键值对,则MAP()函数可以将它们一起加载。 以下是一个例子:

方法2-通过MAP()函数加载MAP
CREATE VERTEX v_map  (PRIMARY_ID id STRING, att_map MAP<INT, STRING>)
CREATE GRAPH test_graph (*)
CREATE LOADING JOB load_map FOR GRAPH test_graph {
   DEFINE FILENAME f;
   LOAD f TO VERTEX v_map  VALUES ($0, MAP( ($1 -> $2), ($3 -> $4) ) );  # $1 and $3 are keys and $2 and $4 are the corresponding values.
}

方法3:第三种方法是使用SPLIT()函数。 与加载LIST或SET中的SPLIT()类似,下面两种情况下可以使用SPLIT()函数:当键值对位于一列中且由键值分隔符分隔,或者多个键值对在一列中且由元素分隔符和键值分隔符分隔。 SPLIT()这里有三个参数:第一个是列索引,第二个是键值分隔符,第三个是元素分隔符。 第三个参数是可选的。 如果某一行的原始数据只有一个键值对,则可以跳过第三个参数。 下面分别是没有给定元素分隔符和给定元素分隔符的示例。

例:每行一个键值对
vid,key_value
v1,1:mike
v2,2:tom
v1,3:lucy
例:每行多个键值对
vid,key_value_list
v1,1:mike#4:lin
v2,2:tom
v1,3:lucy#1:john#6:jack
方法3-通过SPLIT()函数加载MAP
CREATE VERTEX v_map  (PRIMARY_ID id STRING, att_map MAP<INT, STRING>)
CREATE GRAPH test_graph (*)
CREATE LOADING JOB load_map FOR GRAPH test_graph {
   DEFINE FILENAME f;
   LOAD f TO VERTEX v_map  VALUES ($0, SPLIT($1, ":", "#") );
}

SPLIT()函数不适用于UDT元素类型

加载通配符类型的边(Wildcard Type Edge)

如果已使用带有通配符的顶点类定义了某条边,则必须在加载声明中赋予该条边一个名称以及一个顶点id。 下面是一个例子:

例:为某条无类型的边指定类型
#schema setup
CREATE VERTEX user(PRIMARY_ID id UINT)
CREATE VERTEX product(PRIMARY_ID id UINT)
CREATE VERTEX picture(PRIMARY_ID id UINT)
CREATE UNDIRECTED EDGE purchase (FROM *, TO *)
CREATE GRAPH test_graph(*)
 
#loading job
CREATE LOADING JOB test2 FOR GRAPH test_graph {
 DEFINE FILENAME f;
 LOAD f
    TO EDGE purchase VALUES ($0 user, $1 product),
    TO EDGE purchase VALUES ($0 user, $2 picture);
 }

内建的加载器Token函数(Loader Token Function)

GSQL 加载器提供了几个对Token进行操作的内置函数。 它们中的某一些可用于构造属性表达式,另一些则可用于WHERE子句中的条件表达式。

用于属性表达式的Token函数

下列Token函数可被用于一个id或属性表达式

函数名和参数

输出类型函数描述

函数描述

gsql_reverse( main_string )

string

返回一个字符串,字符顺序与 main_string 相反

gsql_concat( string1, string2,...,stringN )

string

返回一个字符串,内容为输入的所有字符串的合并拼接

gsql_split_by_space( main_string )

string

返回一个修正过的字符串 main_string ,其中所有的空格都被替换为一个ASCII30值(十进制)

gsql_to_bool( main_string )

bool

Returns true if the main_string is either "t" or "true", with case insensitive checking. Returns false otherwise.

若 main_string 是t或true,则返回true,大小写敏感。反之,则返回false

gsql_to_uint( main_string )

uint

如果 main_string 是一个不带正负号的整数,则函数返回该整数。

如果main_string 是一个非负浮点数,则函数返回该数的求整

gsql_to_int( main_string )

int

如果 main_string 是一个整数,则函数返回该整数。

如果main_string 是一个浮点数,则函数返回该数的求整

gsql_ts_to_epoch_seconds( main_string )

uint

将标准字符串格式的时间戳转换为UNIX 历的时间格式,即自1970年1月1日以来的秒数。

字符串main_string应该是以下三种格式之一: "%Y-%m-%d %H:%M:%S" "%Y/%m/%d %H:%M:%S" "%Y-%m-%dT%H:%M:%S.000z" // 小数点后的文字将被忽略

gsql_current_time_epoch(0)

uint

以UNIX 历的时间格式返回当前时间。*按照惯例,输入的参数是0,但是它被忽略。

flatten( column_to_be_split, group_separator, 1 )

flatten( column_to_be_split, group_separator, sub_field_separator, number_of_sub_fields_in_one_group )

参考下文中的 "临时表和展开函数"

flatten_json_array ( $"array_name" )

flatten_json_array ( $"array_name", $"sub_obj_1", $"sub_obj_2", ..., $"sub_obj_n" )

参考下文中的 "临时表和展开函数"

split( column_to_be_split, element_separator )

split( column_to_be_split, key_value_separator, element _separator )

参考上文的“加载LIST/SET属性”章节

参考上文的“加载MAP属性”章节

Reducer函数

Reducer函数合并多个非id属性值,并将结果注入到某个顶点或边的某个属性中去。 Reducer函数的计算过程是一个渐进的过程; 也就是说,每次输入新的Token时,都会计算新的结果值。

要减少数据规模并加载聚合后的数据,属性表达式具有特定的格式:

REDUCE( reducer_function ( input_expr ) )

其中reducer_function是下表中的函数之一。 input_expr可以包含非reducer函数,而reducer函数不允许嵌套。

每个reducer函数都被过量载入,使得一个函数可以用于几种不同的数据类型。 对于原始数据类型,输出类型与input_expr类型相同。 对于LIST,SET和MAP容器,input_expr类型是这些容器允许的元素类型之一(详见“属性数据类型”章节中的“复杂类型”一节)。 输出结果是整个容器。

函数名

arg的数据类型:函数返回值的说明

max( arg )

INT, UINT, FLOAT, DOUBLE:所有arg值的中的最大值

min( arg )

INT, UINT, FLOAT, DOUBLE: 所有arg值的中的最小值

add( arg )

INT, UINT, FLOAT, DOUBLE: 所有arg值的和 STRING: 所有arg值的字符串拼接 LIST, SET element: 所有arg值组成的LIST/SET MAP (key -> value) pair: 所有arg值中的键值对组成的键值对字典

and( arg )

BOOL: 对所有arg的值执行AND运算的结果 INT, UINT: 对所有arg的值逐位执行AND运算的结果

or( arg )

BOOL: 对所有arg的值执行OR运算的结果 INT, UINT: 对所有arg的值逐位执行OR运算的结果

overwrite( arg )

非容器: arg LIST, SET: 一个新的只包含arg的 list/set

ignore_if_exists( arg )

任何类型: 如果一个属性值已经存在,则返回(保留)现有值;反之,返回(加载)arg的值

每个函数都支持一组特定的属性。 调用包含不兼容类型的reducer函数会使服务崩溃。 为了防止这种情况,请在必要时使用WHERE子句(下面介绍)和IS NUMERIC或其他运算符,函数,判定进行类型检查。

WHERE子句

WHERE子句是一个可选子句。 WHERE子句的条件是布尔表达式。 表达式可以使用下文中的列token变量,token函数和运算符。 表达式会针对输入数据的每一行进行评估。 如果情况为真,则将顶点或边实例加载到图形存储中。 如果情况为假,则跳过此实例。 请注意,所有属性值在表达式中都被视为字符串。为此我们提供了类型转换函数to_int()和to_float()(下文详述)以以满足需要使用数字格式的情况。

WHERE子句中的运算符

GSQL 加载器支持C ++中的大多数标准的算术,关系和布尔运算符。 加载器同样使用标准的运算顺序,通常括号内的运算优先级更高。

  • 算术运算符:+, -, *, /, ^

    算术运算符用于表示数学上的复杂运算。 正如在普通的数学表达式中一样,括号可用于定义组并修改优先顺序。

由于大多数情况下,计算机只能存储大多数DOUBLE和FLOAT值的近似值,因此不建议对高精度的等式或不等式执行测试。 相反,应该留有一些冗余度。 下例为检验浮点值$0是否等于5,错误冗余度为0.00001:

WHERE to_float($0) BETWEEN 5-0.00001 AND 5+0.00001

  • 关系运算符:<, >, ==, !=, <=, >=

    可以在两个字符串或数字间进行比较。

  • 判定运算符:

    • o AND,OR,NOT运算符与SQL中的运算符相同。 它们可用于将多个条件组合在一起。

      例,$ 0 <“abc”AND $ 1>“abc”选择第一个token小于“abc”且第二个token大于“abc”的行。

      例,NOT $ 1 <“abc”选择第二个token大于或等于“abc”的行。

    • IS NUMERIC 如果某个token是数字,则token IS NUMERIC返回真。 数字格式为整数,可以是十进制表示法或指数表示法。 具体来说,如果token的格式匹配以下正则表达式,则IS NUMERIC为真:(+/-) ? [0-9] + (.[0-9]) ? [0-9] * ((e/E)(+/-) ? [0-9] + ) ? 。 token前后的空格都被忽略,且当中不允许有空格。

      例,$0 IS NUMERIC检查第一个token是否为数字格式。

    • IS EMPTY 如果token是空字符串,则token IS EMPTY返回真 例,$1 IS EMPTY检查第二个token是否为空

    • IN 如果token等于一组指定值的一个,则token IN ( set_of_values ) 返回真。 值可以是字符串或数字类型。

      例,$2 IN ("abc", "def", "lhm") 检验第三个token是否等于给定的三个字符串中的一个。

      例,to_int($3) IN (10, 1, 12, 13, 19) 检验第四个token是否等于指定的五个数字之一。

    • BETWEEN ... AND 如果token在某个指定的范围内(包含两端),则token BETWEEN lowerVal AND upperVal返回真。 值可以是字符串或数字类型。

      例,$4 BETWEEN "abc" AND "def" 检验第五个token是否大于或等于“abc”并且还小于或等于“def”

      例,to_float($5) BETWEEN 1 AND 100.5检查第六个token是否大于或等于1.0且小于或等于100.5。

在WHERE子句中 的Token函数

GSQL加载语法中提供了多种WHERE子句下的内建函数:

函数名

输出类型

函数描述

to_int( main_string )

int

将main_string转换为整数

to_float( main_string )

float

将main_string转换为浮点数

concat( string1, string2 )

string

将string1 和string2 拼合后返回

token_len( main_string )

int

返回main_string的长度

gsql_is_not_empty_string( main_string )

bool

若main_string字符串在删除空格后为空,则返回真;否则返回假。

gsql_token_equal( string1, string2 )

bool

若string1 字符串与string2字符串完全相同(大小写敏感),则返回真;否则返回假。

gsql_token_ignore_case_equal( string1, string2 )

bool

若string1 字符串与string2字符串完全相同(大小写不敏感),则返回真;否则返回假。

gsql_is_true( main_string )

bool

若main_string 字符串为t或true(大小不写敏感),则返回真;否则返回假。

gsql_is_false( main_string )

bool

若main_string 字符串为f或false(大小不写敏感),则返回真;否则返回假。

WHERE子句中的token函数和属性表达式中的的token函数是不同的。 它们不能交换使用。

用户自定义Token函数

用户自定义Token函数

用户可以用C ++编写自己的Token函数,并将它们添加到GSQL系统中。 系统安装时已经提供了包含示例的源代码文件。 用户只需将其自己编写的token函数添加到此文件中即可。 可以在以下地址找到用于属性表达式或WHERE子句的用户自定义token函数:<tigergraph.root.dir>/dev/gdk/gsql/src/TokenBank/TokenBank.cpp。 此文件中包含一些示例,详细信息如下所示。

测试您的自定义函数很简单。 在TokenBank.cpp文件所在的目录中,有一个名为compile的命令脚本。

1. 测试您的函数编译:

./compiles

2. 要测试您的函数是否正常工作,可编写自己的测试并将其添加到TokenBank.cpp中的main()过程中。 然后,编译该文件并运行它。 请注意,位于../TokenLib中的文件需要被包括:

g++ -I../TokenLib TokenBank.cpp
./a.out

属性表达式中的用户自定义Token函数

属性类型

函数签名

string or string compress

extern "C" void funcName (const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum, char* const oToken, uint32_t& oTokenLen)

bool

extern "C" bool funcName (const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum)

uint

extern "C" uint64_t funcName (const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum)

int

extern "C" int64_t funcName (const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum)

float

extern "C" float funcName (const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum)

double

extern "C" double funcName (const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum)

参数如下:iToken是由字符串token组成的数组,iTokenLen是字符串token的长度组成的数组,而iTokenNum是token的数量。 请注意,输入token须始终采用字符串(char *)格式。

如果属性类型不是字符串也不是字符串压缩,则返回类型的对应关系为:bool 返回 bool; uint 返回uint64_t ; int 返回int64_t ; float 返回float ;double 返回 double。 如果属性类型是字符串或字符串压缩,则返回类型应为void,并使用额外参数(char *const oToken, uint32_t& oTokenLen)来存储返回的字符串。 oToken是返回的字符串值,oTokenLen是此字符串的长度。

内置的Token函数gsql_concat的用法见下例。 它需要多token参数并返回一个字符串:

gsql_concat
extern "C" void gsql_concat(const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum, char* const oToken, uint32_t& oTokenLen) {
 int k = 0;
 for (int i=0; i < iTokenNum; i++) {
   for (int j =0; j < iTokenLen[i]; j++) {
          oToken[k++] = iToken[i][j];
   }
 }
 oTokenLen = k;
}

WHERE子句中的用户自定义Token函数

用户定义的Token函数(如上所述)也可用于在WHERE子句中构造布尔条件表达式。 但是,该方式在WHERE子句中存在一些限制:

在子句"WHERE conditions "中

• 只允许返回布尔值的用户自定义Token函数。

• 如果在WHERE子句中使用用户自定义token函数,则它必须能构成一整套完整的条件; 它不能与另一个函数或运算符共同工作以提供后续运算的值。 与此相反,UDF的参数则可以包括其他函数。

内置的Token函数gsql_token_equal的源代码,展示了如何编写用户自定义Token函数。

gsql_token_equal
extern "C" bool gsql_token_equal(const char* const iToken[], uint32_t iTokenLen[], uint32_t iTokenNum) {
 if (iTokenNum != 2) {
   return false;
 }
 if (iTokenLen[0] != iTokenLen[1]) {
   return false;
 }
 for (int i =0; i < iTokenLen[0]; i++) {
   if (iToken[0][i] != iToken[1][i]) {
     return false;
   }
 }
 return true;
}

其他可选LOAD子句

OPTION clause

目前对于OPTION子句没有支持的选项

USING clause

一个USING子句中包含一个或多个参数值对:

USING parameter=value [,parameter=value]*

在2.0版本中,USING子句仅出现在LOAD语句的末尾。

在早期版本中,USING子句的位置以及哪些参数有效,具体要看该作业是v1.x在线加载作业还是v1.x离线加载作业。

如果多个LOAD语句使用相同的数据源(相同的文件路径,相同的TEMP_TABLE或相同的文件变量),则这些LOAD语句中的USING子句必须相同。 因此,我们建议如果多个目标子句共享相同的数据源,请将所有这些目标子句放在同一个LOAD语句中。

下面列出的USING参数是支持的。缩写的含义如下 "--" = "不适用", "Opt" = "可选", "REQ" = "必须".

参数

v2.0 LOAD描述

值的含义

允许的值

SEPARATOREOL

Opt

定义用于在数据文件中分隔行与行(即token)的字符

任何单个 ASCII 字符.

默认为逗号 ","

"\t" 为tab

"\xy" 为ASCII 十进制源码 xy

EOL

Opt

行末(end-of-line)字符

任何ASCII 序列

默认值为 "\n" (系统定义的新一行标识符或者字符序列)

QUOTE (详见标注)

Opt

标记字符串类的token,可以是单引号或双引号。详见下文说明。

"single" 为单引号 ' "double" 为双引号"

USER_DEFINED_HEADER

Opt

当标头不是在数据文件中定义,而是在加载作业中定义时,用于定义标头变量的名字。

先前DEFINE HEADER语句中的变量名

REJECT_LINE_RULE

Opt

如果过滤器表达式的输出为true,则不输入本数据行。

先前DEFINE INPUT_LINE_FILTER语句中的过滤器名

JSON_FILE (详见标注)

Opt

是否在每行都有一个json对象(详见JSON加载器章节)

"true", "false"

默认为 "false"

HEADER

Opt

LOAD语句必须引用具有有效标头的实际文件。

"true", "false"

默认为 "false"

QUOTE 参数

解析器不会将引号中的分隔符作为分隔符处理。 例如,如果解析条件是QUOTE="double", SEPARATOR=",",则“Leonard,Euler”中的逗号不会将Leonard和Euler分开。

  • 如果未声明QUOTE,则引号将被视为普通字符。

  • 如果声明了QUOTE,但字符串不包含匹配的引号对,则将字符串视为未声明QUOTE。

  • 仅加载第一对引号(从左到右)内的字符串。 例如QUOTE="double",字符串a"b"c"d"e将被加载为b。

  • 加载器中没有转义字符,因此在字符串中包含引号的唯一方法是在字符串内容中只使用某一种类型的引号(单引号或双引号)并将另一种引号声明为字符串的边界标记。

加载JSON 数据

当使用USING选项的JSON_FILE="true"时,加载程序加载的是JSON对象而不是表格数据。 JSON对象是一组无序的键/值对,其中每个值本身可以是一个数组或对象,从而使得具体的数据可能是嵌套结构。 每个键值对用冒号将每个键与其值分开,逗号分隔集合中的元素。 有关JSON格式的更完整描述,请访问www.json.org。 JSON加载器要求每个输入行只有一个JSON对象。 JSON加载器不使用列值作为token,而使用JSON值作为token,即每个JSON键/值对的第二部分。 在GSQL加载作业中,JSON字段由美元符号$标识,后跟冒号分隔的一系列嵌套的键名序列,自上而下取值。 例如,给定JSON对象{"abc":{"def": "this_value"}},表示标识符$"abc":"def"可用于访问“this_value”。 双引号必须使用。

例:

USING JSON_FILE 测试场景和加载作业
CREATE VERTEX encoding (PRIMARY_ID id STRING, length FLOAT default 10)
CREATE UNDIRECTED EDGE encoding_edge (FROM encoding, TO encoding)
CREATE GRAPH encoding_graph (*)
 
CREATE LOADING JOB json_load FOR GRAPH encoding_graph {
 LOAD "encoding.json" TO VERTEX encoding
   VALUES ($"encoding", $"indent":"length") USING JSON_FILE="true";
}
RUN JOB json_load
encoding.json
{"encoding": "UTF-7","plug-ins":["c"],"indent" : { "length" : 30, "use_space": true }}
{"encoding":"UTF-1","indent":{"use_space": "dontloadme"}, "plug-ins" : [null, true, false] }
{"plug-ins":["C","c++"],"indent":{"length" : 3, "use_space": false},"encoding":"UTF-6"}

在上面的数据encoding.json中,字段的顺序不固定,并且缺少某些字段。 JSON加载程序忽略顺序并通过嵌套的键名访问字段。 缺少的字段加载了默认值。 结果输出的顶点为:

id

attr1

"UTF-7"

30

"UTF-1"

10

"UTF-6"

3

临时表和展开函数(Flatten Function)

关键字TEMP_TABLE触发临时数据表的使用,该临时数据表用于存储由一个LOAD语句生成的数据,供之后的LOAD语句使用。 之前我们研究介绍了将数据加载到TEMP_TABLE的语法:

TEMP_TABLE 目标子句
TO TEMP_TABLE table_name (id_name [, attr_name]*) VALUES (id_expr [, attr_expr]*)
   [WHERE conditions] [OPTION (options)]

此子句旨在与某一个attr_expr表达式中的flatten或flatten_json_array函数一起使用。 展开函数将多值字段拆分为一组记录。 可以首先将这些记录存储到临时表中,然后可以将临时表加载到顶点和/或边中。 一个临时表目标子句中只允许使用一个展开函数。

展开函数有两个版本:一个用于解析单层组,另一个用于解析两层组。 flatten_json_array函数也有两个版本:一个用于分割原语值数组(array of primitive values),另一个分割一个JSON对象数组。

单层展开函数

flatten( column_to_be_split, separator, 1 )用于将一个单层级的组解析为数个单个元素。 如下例所示:

book1.dat
101|"Harry Potter and the Philosopher's Stone"|"fiction,fantasy,young adult"
102|"The Three-Body Problem"|"fiction,science fiction,Chinese"
用单层展开函数加载 (load_book_flatten1.gsql)
CREATE LOADING JOB load_books_flatten1 FOR GRAPH Book_rating {
 DEFINE FILENAME f;
 LOAD f
     TO VERTEX Book VALUES ($0, $1, _),
     TO TEMP_TABLE t1(bookcode,genre) VALUES ($0, flatten($2,",",1))
     USING QUOTE="double", SEPARATOR="|";
 
 LOAD TEMP_TABLE t1
     TO VERTEX Genre VALUES($"genre", $"genre"),
     TO EDGE book_genre VALUES($"bookcode", $"genre");
}
RUN LOADING JOB load_books_flatten1 USING f="../data/book1.dat"

加载作业包含两个LOAD语句。 第一个将输入数据加载到Book 顶点和一张TEMP_TABLE临时表中。 第二个将TEMP_TABLE数据加载到Genre顶点和book_genre边中。

bookcode

Genre

101

Fiction

101

Fantasy

101

young_adult

102

fiction

102

science_fictionChinese

102

第5行表示每个输入行的第三列($2)应被分成单独的token,以逗号","作为分隔符。 每个token在表t1中都有自己的行。 第一列被标记为“bookcode”,值为$0,第二列为“genre”,包含一个为$2 token。 TEMP_TABLE t1的内容如下所示:

然后,第8行到第10行表示要读取TEMP_TABLE t1并为每行执行以下操作:

  • • 为“genre”的每个新值创建一个Genre顶点。

  • 从“bookcode”到“genre”创建一条book_genre边。 在这种情况下,TEMP_TABLE t1的每一行都会生成一个book_genre边。

最终图形将包含两个Book顶点(101和102),五个Genre顶点和六个book_genre边。

所有的book_genre 的边加载后的列表
{
 "results": [{"@@edgeSet": [
   {
     "from_type": "Book",
     "to_type": "Genre",
     "directed": false,
     "from_id": "101",
     "to_id": "fiction",
     "attributes": {},
     "e_type": "book_genre"
   },
   {
     "from_type": "Book",
     "to_type": "Genre",
     "directed": false,
     "from_id": "101",
     "to_id": "fantasy",
     "attributes": {},
     "e_type": "book_genre"
   },
   {
     "from_type": "Book",
     "to_type": "Genre",
     "directed": false,
     "from_id": "102",
     "to_id": "sciencevfiction",
     "attributes": {},
     "e_type": "book_genre"
   },
   {
     "from_type": "Book",
     "to_type": "Genre",
     "directed": false,
     "from_id": "101",
     "to_id": "young adult",
     "attributes": {},
     "e_type": "book_genre"
   },
   {
     "from_type": "Book",
     "to_type": "Genre",
     "directed": false,
     "from_id": "102",
     "to_id": "fiction",
     "attributes": {},
     "e_type": "book_genre"
   },
   {
     "from_type": "Book",
     "to_type": "Genre",
     "directed": false,
     "from_id": "102",
     "to_id": "Chinese",
     "attributes": {},
     "e_type": "book_genre"
   }
 ]}]
} 

二层展开函数

flatten( column_to_be_split, group_separator, sub_field_separator, number_of_sub_fields_in_one_group )用于将一个二层组解析为数个单独的元素。 主组中的每个token本身可以也可以是一个组,因此就会有两个分隔符:一个用于顶层,一个用于第二层。 下面是一个二层组的例子:

book2.dat
101|"Harry Potter and the Philosopher's Stone"|"FIC:fiction,FTS:fantasy,YA:young adult"
102|"The Three-Body Problem"|"FIC:fiction,SF:science fiction,CHN:Chinese"

本例中的展开函数有四个参数而不是三个。 多出的参数用于在Genre顶点中记录genre_name。

使用二层展开函数加载book_flatten2_load.gsql
CREATE LOADING JOB load_books_flatten2 FOR GRAPH Book_rating {
 DEFINE FILENAME f;
 LOAD f
     TO VERTEX Book VALUES ($0, $1, _),
     TO TEMP_TABLE t2(bookcode,genre_id,genre_name) VALUES ($0, flatten($2,",",":",2))
     USING QUOTE="double", SEPARATOR="|";
 
 LOAD TEMP_TABLE t2
     TO VERTEX Genre VALUES($"genre_id", $"genre_name"),
     TO EDGE book_genre VALUES($"bookcode", $"genre_id");
}
RUN LOADING JOB load_books_flatten2 USING f="book2.dat"

在此示例中,在genres列($2)中有多个组,每个组有两个子字段genre_id和genre_name。 运行加载作业后,文件book2.dat将加载到临时表TEMP_TABLE t2中,如下所示。

bookcode

genre_id

genre_name

101

FIC

fiction

101

FTS

fantasy

101

YA

young adult

102

FIC

fiction

102

SF

science fiction

102

CHN

Chinese

展开一个包含原语值的JSON数组

flatten_json_array($" array_name ")用于解析原语(字符串,数字或布尔)值的JSON数组,其中“array_name”是数组的名称。 数组中的每个值都会创建一条记录。 以下是一个例子:

flatten_json_array_values 的加载
CREATE VERTEX encoding (PRIMARY_ID id STRING, length FLOAT default 10)
CREATE UNDIRECTED EDGE encoding_edge (FROM encoding, TO encoding)
CREATE GRAPH encoding_graph (*)
 
CREATE LOADING JOB json_flatten FOR GRAPH encoding_graph {
 LOAD "encoding2.json" TO TEMP_TABLE t2 (name, length)
   VALUES (flatten_json_array($"plug-ins"), $"indent":"length") USING JSON_FILE ="true";
 LOAD TEMP_TABLE t2
   TO VERTEX encoding VALUES ($"name", $"length");
}
RUN LOADING JOB json_flatten
encoding2.json
{"plug-ins" : ["C", "c++"],"encoding" : "UTF-6","indent" : { "length" : 3, "use_space": false}}

上述数据和加载作业会创建下面的临时表:

id

length

C

3

c++

3

展开一个包含JSON对象的JSON数组

flatten_json_array ( $"array_name", $"sub_obj_1", $"sub_obj_2", ..., $"sub_obj_n" )解析包含JSON对象的JSON数组。 “array_name”是数组的名称,以下参数$"sub_obj_1", $"sub_obj_2", ..., $"sub_obj_n"是数组中每个对象的字段键名。 详见完整示例:

encoding3.json
{"encoding":"UTF-1","indent":{"use_space": "dontloadme"}, "plug-ins" : [null, true, false, {"lang":"golang","prop":{"age":"noidea"}}]}
{"encoding": "UTF-8", "plug-ins" : [{"lang": "pascal", "score":"1.0", "prop":{"age":"old"}}, {"lang":"c++", "score":2.0}],"indent":{"length" :12,"use_space": true}}
{"encoding": "UTF-7",  "plug-ins" : [{"lang":"java", "score":2.22}, {"lang":"python", "score":3.0},{"lang":"go", "score":4.0, "prop":{"age":"new"}}],"indent" : { "length" : 30, "use_space": true }}
{"plug-ins" : ["C", "c++"],"encoding" : "UTF-6","indent" : { "length" : 3, "use_space": false}}
json_flatten_array_test.gsql
CREATE VERTEX encoding3 (PRIMARY_ID id STRING, score FLOAT default -1.0, age STRING default "Unknown", length INT default -1)
CREATE UNDIRECTED EDGE encoding3_edge (FROM encoding3, TO encoding3)
CREATE GRAPH encoding_graph (*)
 
CREATE LOADING JOB json_flatten_array FOR GRAPH encoding_graph {
 LOAD "encoding3.json" TO TEMP_TABLE t3 (name, score, prop_age, indent_length )
   VALUES (flatten_json_array($"plug-ins", $"lang", $"score", $"prop":"age"), $"indent":"length")
   USING JSON_FILE="true";
 LOAD TEMP_TABLE t3
   TO VERTEX encoding3 VALUES ($"name", $"score", $"prop_age", $"indent_length");
}
RUN LOADING JOB json_flatten_array

拆分包含JSON对象的JSON数组时,将跳过原语值并仅处理JSON对象。 如上例所示,第4行的"plug-ins"字段不会生成任何记录,因为"plug-ins"数组不包含任何JSON对象。任何字段,只要在对象中不存在,都将被赋予默认值。 上面的例子会生成如下所示的临时表:

id

score

age

length

"golang"

default

"noidea"

default

"pascal"

1.0

"old"

12

"c++"

2.0

default

12

"java"

2.22

default

30

"python"

3.0

default

30

"go"

4.0

"new"default

30

展开一个CSV文件中的JSON数组

flatten_json_array() 也可以被用于将一个表格文件分割为列,列中包含JSON数组。下面是一个示例:

encoding.csv
golang|{"prop":{"age":"noidea"}}
pascal|{"score":"1.0", "prop":{"age":"old"}}
c++|{"score":2.0, "indent":{"length":12, "use_space": true}}
java|{"score":2.22, "prop":{"age":"new"}, "indent":{"use_space":"true", "length":2}}
python|{ "prop":{"compiled":"false"}, "indent":{"length":4}, "score":3.0}
go|{"score":4.0, "prop":{"age":"new"}}

csv文件中的第二列是我们要拆分的JSON数组。 这种情况下可以用split. flatten_json_array() 函数,而不需要USING JSON_FILE="true" clause子句:

json_flatten_cvs.gsql
CREATE VERTEX encoding3 (PRIMARY_ID id STRING, score FLOAT default -1.0, age STRING default "Unknown", length INT default -1)
CREATE UNDIRECTED EDGE encoding3_edge (FROM encoding3, TO encoding3)
CREATE GRAPH encoding_graph (*)
 
CREATE LOADING JOB json_flatten_cvs FOR GRAPH encoding_graph {
 LOAD "encoding.csv" TO TEMP_TABLE t4 (name, score, prop_age, indent_length )
   VALUES ($0,flatten_json_array($1, $"score", $"prop":"age", $"indent":"length"))
   USING SEPARATOR="|";
 LOAD TEMP_TABLE t4
   TO VERTEX encoding3 VALUES ($"name", $"score", $"prop_age", $"indent_length");
}
RUN LOADING JOB json_flatten_cvs

上面的代码会生成下面的临时表:

id

score

age

length

golang

-1 (default)

noidea

-1 (default)

pascal

1

old

-1 (default)

c++

2

unknown (default)

12

java

2.22

new

2

python

3

unknown (default)

4

go

4

new

-1 (default)

flatten_json_array in csv

如果json数组列中也包含分隔符,则flatten_json_array()会出错。 例如,如果分隔符是逗号,则csv加载程序会错误地将json数组划分为多个列。 因此,建议csv文件使用不同的列分隔符,例如上面的例子中的竖线"|"。

DELETE 的语法

除了加载数据外,还可以使用LOADING JOB执行相反的操作:即使用DELETE语句删除顶点和边。 DELETE不能在离线加载中使用。 正如LOAD语句使用来自每个输入行的token来设置要创建的顶点或边的id和属性值一样,DELETE语句使用来自每个输入行的token来删除指定项的id值。

在v2.0的语法中,在WHERE子句之前有一个" FROM (filepath_string | filevar) "子句。

DELETE语句有四种变体。 这四种情况的语法如下所示。

DELETE VERTEX | EDGE 语法
CREATE LOADING JOB abc FOR GRAPH graph_name {
 DEFINE FILENAME f;
 # 1. Delete each vertex which has the given vertex type and primary id.
 DELETE VERTEX vertex_type_name (PRIMARY_ID id_expr) FROM f [WHERE condition] ;
 
 # 2. Delete each edge which has the given edge type, source vertex id, and destination vertex id.
 DELETE EDGE edge_type_name (FROM id_expr, TO id_expr) FROM f [WHERE condition] ;
 
 # 3. Delete all edges which have the given edge type and source vertex id. (Destination vertex id is left open.)
 DELETE EDGE edge_type_name (FROM id_expr) FROM f [WHERE condition] ;
 
 # 4. Delete all edges which have the given source vertex id. (Edge type and destination vertex id are left open.)
 DELETE EDGE * (FROM id_expr vertex_type_name) FROM f [WHERE condition] ;
}

一个例子using book_rating data:

DELETE 的例子
# Delete all user occupation edges if the user is in the new files, then load the new files
CREATE LOADING JOB clean_user_occupation FOR GRAPH Book_rating {
 DEFINE FILENAME f;
 DELETE EDGE user_occupation (FROM $0) FROM f;
}
CREATE LOADING JOB load_user_occupation FOR GRAPH Book_rating {
 DEFINE FILENAME f;
 LOAD f TO EDGE user_occupation VALUES ($0,$1);
}
RUN LOADING JOB clean_user_occupation USING f="./data/user_occupation_update.dat"
RUN LOADING JOB load_user_occupation USING f="./data/user_occupation_update.dat"

GSQL查询语言中另有一个单独的DELETE语句。 该语句可以利用查询语言探索图形的能力,并使用复杂条件来确定要删除的项目。 与之相反,加载作业删除语句要求必须在输入文件中预先指定要删除的项的id值。

离线Job转在线Job(不推荐使用)

offline2online <offline_job_name>

gsql命令offline2online可以将已安装的离线加载任务转换为等效的一个或一组在线加载任务。

在线renwu 名

每个脱机加载作业包含一个或多个LOAD语句,每个语句都需要指定输入数据文件的名称。 offline2online将每个LOAD语句都转换为在线加载作业。 新的在线加载作业名,由离线加载作业名和数据文件名组成。 例如,如果离线作业具有以下格式:

CREATE LOADING JOB loadEx FOR GRAPH graphEx {
 LOAD "fileA" TO ...
 LOAD "fileB" TO ...
}

然后,GSQL命令offline2online loadEx 将创建两个新的在线加载作业,称为loadEx_fileA和loadEx_fileB。 转换后的加载作业安装在GSQL系统中; 它们不是文本文件。如果已经存在相同名称的作业,则会在文件名后附加版本号:首先是“_1”,然后是“_2”等。

例如,如果执行offline2online loadEx三次,则会生成以下在线作业:

  • 第一次: loadEx_fileA, loadEx_fileB

  • 第二次: loadEx_fileA_1, loadEx_fileB_1

  • 第三次: loadEx_fileA_2, loadEx_fileB_2

转换和RUN JOB的细节

内置于离线加载作业中的加载作业的某些参数,不能被包含在在线作业中:

• 输入数据文件名

• SEPARATOR

• HEADER

它们应该在执行加载作业时再提供。 不过在线作业并不完全支持HEADER。

运行任何在线加载作业时,必须提供输入数据文件名和分隔符。 有关更多详细信息,请参阅有关USING 子句和执行加载作业章节。

如果在执行在线加载作业时,设置HEADER =“true”,它将跳过数据文件中的第一行但不会读取该行以获取列名称。 因此,必须手动将读取和使用列标题名称的离线作业转换为在线作业。

以下示例取自GSQL Tutorial with Real-Life Examples 中的社会网络案例。 在该教程的0.2版本中,我们使用了离线加载。下面的工作业使用了与v0.2相同的语法,但某些名称已更新:

基于0.2版本的social_load.gsql的离线加载示例
CREATE LOADING JOB load_social FOR GRAPH gsql_demo
{
 LOAD "data/social_users.csv"
   TO VERTEX SocialUser VALUES ($0,$1,$2,$3)
 USING QUOTE="double", SEPARATOR=",", HEADER="true";
 
 LOAD "data/social_connection.csv"
   TO EDGE SocialConn VALUES ($0, $1)
   USING SEPARATOR=",", HEADER="false";
}

运行以下命令执行该作业:

RUN LOADING JOB load_social

请注意,第一个LOAD语句设置了HEADER =“true”,但不使用列名,而只使用列索引$0,$1,$2和$3。 因此,HEADER选项仍可用于已转换的作业。 运行offline2online load_social1 可以创建两个名为load_social_social_users.csv和load_social_social_connection.csv的新作业。

该作业的等效命令如下:

RUN LOADING JOB load_social_social_users.csv USING FILENAME="data/social_users.csv", SEPARATOR=",", EOL="\n", HEADER="true"
RUN LOADING JOB load_social_social_connection.csv USING FILENAME="data/social_connection.csv", SEPARATOR=",", EOL="\n"

为了与之比较,下面是当前版本教程中,加载命令里的在线加载作业:

social_load.gsql, version 0.8.1
CREATE LOADING JOB load_social1 FOR GRAPH gsql_demo
{
 LOAD
   TO VERTEX SocialUser VALUES ($0,$1,$2,$3) USING QUOTE="double";
}
CREATE LOADING JOB load_social2 FOR GRAPH gsql_demo {
 LOAD
   TO EDGE SocialConn VALUES ($0, $1);
}
# load the data
RUN JOB load_social1 USING FILENAME="../social/data/social_users.csv", SEPARATOR=",", EOL="\n", HEADER="true"
RUN JOB load_social2 USING FILENAME="../social/data/social_connection.csv", SEPARATOR=",", EOL="\n"

 

Last updated