前面的章节重点介绍了构成一个查询最基础的组成部分:数据类型(第3章),运算符,函数和表达式(第5章),以及一个专门介绍累加器的章节(第4章)。现在开始,我们要介绍GSQL查询中的语句类型。 本节重点介绍声明和赋值语句。 后面的部分将详细介绍最为关键的SELECT语句,控制流语句和数据修改语句。 此外,某些类型的语句还可以嵌套在SELECT或UPDATE或控制流语句中。
Copy ## Declarations ##
accumDeclStmt := accumType "@"name ["=" constant][, "@"name ["=" constant]]*
| "@"name ["=" constant][, "@"name ["=" constant]]* accumType
| [STATIC] accumType "@@"name ["=" constant][, "@@"name ["=" constant]]*
| [STATIC] "@@"name ["=" constant][, "@@"name ["=" constant]]* accumType
baseDeclStmt := baseType name ["=" constant][, name ["=" constant]]*
fileDeclStmt := FILE fileVar "(" filePath ")"
fileVar := name
localVarDeclStmt := baseType name "=" expr
vSetVarDeclStmt := name ["(" vertexEdgeType ")"] "=" (seedSet | simpleSet | selectBlock)
simpleSet := name | "(" simpleSet ")" | simpleSet (UNION | INTERSECT | MINUS) simpleSet
seedSet := "{" [seed ["," seed ]*] "}"
seed := '_'
| ANY
| ["@@"]name
| name ".*"
| "SelectVertex" selectVertParams
selectVertParams := "(" filePath "," columnId "," (columnId | name) ","
stringLiteral "," (TRUE | FALSE) ")" [".".FILTER "(" condition ")"]
columnId := "$" (integer | stringLiteral)
## Assignment Statements ##
assignStmt := name "=" expr
| name "." name "=" expr
| name "." "@"name ("+="| "=") expr
gAccumAssignStmt := "@@"name ("+=" | "=") expr
loadAccumStmt := "@@"name "=" "{" "LOADACCUM" loadAccumParams ["," "LOADACCUM" loadAccumParams]* "}"
loadAccumParams := "(" filePath "," columnId "," [columnId ","]*
stringLiteral "," (TRUE | FALSE) ")" [".".FILTER "(" condition ")"]
## Function Call Statement ##
funcCallStmt := name ["<" type ["," type"]* ">"] "(" [argList] ")"
| "@@"name ("." name "(" [argList] ")")+
argList := expr ["," expr]*
声明语句
GSQL 查询中的变量声明有六种:
前五种类型都有自己的声明语句语法,本节将对它们进行详细介绍。 而别名的声明则包含在SELECT语句声明的章节中。
累加器
Copy vertexEdgeType := "_" | ANY | name | ( "(" name ["|" name]* ")" )
有关累加器的声明请详见第四章“累加器 ”。
全局变量
在累加器声明之后,基本类变量可以声明为全局变量。 全局变量的作用域是从声明处开始,直到查询结束。
Copy baseDeclStmt := baseType name ["=" constant][, name ["=" constant]]*
用户可以在查询中的任何位置访问(读取)全局变量,但并不是在任何地方都能更新该变量。 细节请参阅下文中有关“赋值”的小节。
Copy # Assign global variable at various places
CREATE QUERY globalVariable(VERTEX<person> m1) FOR GRAPH socialNet {
SetAccum<VERTEX<person>> @@personSet;
SetAccum<Edge> @@edgeSet;
# Declare global variables
STRING gender;
DATETIME dt;
VERTEX v;
VERTEX<person> vx;
EDGE ee;
allUser = {person.*};
allUser = SELECT src
FROM allUser:src - (liked:e) -> post
ACCUM dt = e.actionTime,
ee = e, # assignment does NOT take effect yet
@@edgeSet += ee # so ee is null
POST-ACCUM @@personSet += src;
PRINT @@edgeSet; # EMPTY because ee was frozen in the SELECT statement.
PRINT dt; # actionTime of the last edge e processed.
v = m1; # assign a vertex value to a global variable.
gender = m1.gender; # assign a vertex's attribute value to a global variable.
PRINT v, gender;
FOREACH m IN @@personSet DO
vx = m; # global variable assignment inside FOREACH takes place.
gender = m.gender; # global variable assignment inside FOREACH takes place.
PRINT vx, gender; # display the values for each iteration of the loop.
END;
}
Copy GSQL > RUN QUERY globalVariable("person1")
{
"error": false,
"message": "",
"version": {
"schema": 0,
"api": "v2"
},
"results": [
{"@@edgeSet": [{}]},
{"dt": "2010-01-12 21:12:05"},
{
"gender": "Male",
"v": "person1"
},
{
"vx": "person3",
"gender": "Male"
},
{
"vx": "person7",
"gender": "Male"
},
{
"vx": "person1",
"gender": "Male"
},
{
"vx": "person5",
"gender": "Female"
},
{
"vx": "person6",
"gender": "Male"
},
{
"vx": "person2",
"gender": "Female"
},
{
"vx": "person8",
"gender": "Male"
},
{
"vx": "person4",
"gender": "Female"
}
]
}
用户可以在同一行声明和初始化多个相同类型的全局变量,如下例所示:
Copy CREATE QUERY variableDeclaration() FOR GRAPH minimalNet {
INT a=5,b=1;
INT c,d=10;
MaxAccum<INT> @@max1 = 3, @@max2 = 5, @@max3;
MaxAccum<INT> @@max4, @@max5 = 2;
PRINT a,b,c,d;
PRINT @@max1, @@max2, @@max3, @@max4, @@max5;
}
variableDeclaration.json 的结果
Copy GSQL > RUN QUERY variableDeclaration()
{
"error": false,
"message": "",
"version": {
"schema": 0,
"api": "v2"
},
"results": [
{
"a": 5,
"b": 1,
"c": 0,
"d": 10
},
{
"@@max3": -9223372036854775808,
"@@max2": 5,
"@@max1": 3,
"@@max5": 2,
"@@max4": -9223372036854775808
}
]
}
局部变量
局部变量只能在ACCUM,POST-ACCUM或UPDATE SET子句中声明,并且其作用域也仅限于该子句。 局部变量只能是基本类(例如INT,FLOAT,DOUBLE,BOOL,STRING,VERTEX),且必须在同一语句中声明的同时初始化局部变量。
Copy localVarDeclStmt := baseType name "=" expr
在局部变量的作用域内,同一级的子句中不能声明另一个相同名称的局部变量。 但是,一个新的与该变量同名的变量可以在低级别子句中(例如又嵌套了一层SELECT或UPDATE语句)使用。 即低级别的声明只在该级别工作。
在POST-ACCUM子句中,每个局部变量只能用于初始顶点或目标顶点两者之一,而不能同时用于两者。
Copy # An example showing a local variable succeeded where a global variable fails
CREATE QUERY localVariable(vertex<person> m1) FOR GRAPH socialNet {
MaxAccum<INT> @@maxDate, @@maxDateGlob;
DATETIME dtGlob;
allUser = {person.*};
allUser = SELECT src
FROM allUser:src - (liked:e) -> post
ACCUM
DATETIME dt = e.actionTime, # Declare and assign local dt
dtGlob = e.actionTime, # dtGlob doesn't update yet
@@maxDate += datetime_to_epoch(dt),
@@maxDateGlob += datetime_to_epoch(dtGlob);
PRINT @@maxDate, @@maxDateGlob, dtGlob; # @@maxDateGlob will be 0
}
Copy GSQL > RUN QUERY localVariable("person1")
{
"error": false,
"message": "",
"version": {
"schema": 0,
"api": "v2"
},
"results": [{
"dtGlob": "2010-01-11 03:26:05",
"@@maxDateGlob": 0,
"@@maxDate": 1263618953
}]
}
顶点集变量的声明和赋值
顶点集变量在GSQL查询中起着特殊的作用—— 它们用于SELECT语句的输入和输出。 因此,声明并初始化顶点集变量必须在SELECT语句之前。 该初始顶点集称为种子集(seed set)。
Copy vSetVarDeclStmt := name ["(" vertexEdgeType ")"] "=" (seedSet | simpleSet | selectBlock)
## Seed Sets ##
seedSet := "{" [seed ["," seed ]*] "}"
seed := '_'
| ANY
| ["@@"]name
| name ".*"
| "SelectVertex" selectVertParams
selectVertParams := "(" filePath "," columnId "," (columnId | name) ","
stringLiteral "," (TRUE | FALSE) ")" [".".FILTER "(" condition ")"]
columnId := "$" (integer | stringLiteral)
simpleSet := name | "(" simpleSet ")" | simpleSet (UNION | INTERSECT | MINUS) simpleSet
下面的列出了为顶点集变量分配初始顶点(即形成种子集)的所有方法:
一个顶点参数,不包含类型(S1)或包含类型(S2)
一个顶点集参数,不包含类型(S3)或包含类型(S4)
一个全局的SetAccum <VERTEX>累加器,不包含类型(S5)或包含类型(S6)
全部类型中的所有顶点(S7,S9)或某一种类型的所有顶点(S8)
外部文件中的顶点id列表(S10)
Copy CREATE QUERY seedSetExample(VERTEX v1, VERTEX<person> v2, SET<VERTEX> v3, SET<VERTEX<person>> v4) FOR GRAPH socialNet {
SetAccum<VERTEX> @@testSet;
SetAccum<VERTEX<person>> @@testSet2;
S1 = { v1 };
S2 = { v2 };
S3 = v3;
S4 = v4;
S5 = @@testSet;
S6 = @@testSet2;
S7 = ANY; # All vertices
S8 = person.*; # All person vertices
S9 = _; # Equivalent to ANY
S10 = SelectVertex("absolute_path_to_input_file", $0, post, ",", false); # See Section "SelectVertex()" function
S11 = S1;
S12 = {@@testSet, v2, v3}; # S1 is not allowed to be in {}
S13 = S11 UNION S12; # but we can use UNION to combine S1
}
声明顶点集变量时,可以选择将一组顶点类型指定给顶点集变量。 如果未明确指定顶点集变量的类型,则系统通过顶点集值隐含的类型来输出。 选择的类型可以是ANY(表示所有类型),下划线“_” (等效于ANY)或任何被明确说明的顶点类型。 请参阅EBNF范式规则中的vertexEdgeType。
声明语法的差异:顶点集变量vs基本类变量
在顶点集变量声明中,类型说明符跟在变量名后面,并且应该用括号括起来,如:vSetName(type)
基本类变量声明则不同,它的类型说明符位于基本变量名称之前:type varName
声明顶点集变量后,顶点集变量的顶点类型是不可变的。 此顶点集变量的每个赋值动作(例如SELECT语句)都必须与该数据类型匹配。 以下的示例中,我们必须声明顶点集变量的类型。
Copy CREATE QUERY vertexSetVariableTypeExample(vertex<person> m1) FOR GRAPH socialNet {
INT ite = 0;
S (ANY) = {m1}; # ANY is necessary
WHILE ite < 5 DO
S = SELECT t
FROM S:s - (ANY:e) -> ANY:t;
ite = ite + 1;
END;
PRINT S;
}
在上面的示例中,查询操作从“person”顶点开始进行5个步的遍历后获得一个顶点集,然后输出该顶点集。 如果我们声明了顶点集变量S,但没有指定类型,则因为顶点参数m1的类型是“person”,GSQL引擎会把S也设定为“person”类型。 但是,如果已经对S指定了“person”的类型,则WHILE循环内的SELECT语句会产生类型核对错误,因为SELECT语句会选择出所有与之前顶点相连的顶点,包括类型不是“person”的顶点。 因此,我们必须将S声明为ANY型顶点集变量。
文件对象声明
文件对象是一个存储有序文本的对象,与本地计算机上的某个文本文件相关联。
当引用文件对象时,我们总是将单词FILE大写,以将其与普通的文件区分开来。
Copy fileDeclStmt := FILE fileVar "(" filePath ")"
fileVar := name
当用户声明一个关联某个文本文件的文件对象时,文本文件中的任何现有内容都会被删除。 在执行查询期间,输出的内容会写到该文件对象的最后。 查询完成运行后,文件对象的内容将保存到文本文件中。
请注意,声明语句会调用文件对象构造函数。 构造函数的语法包含文件地址变量两侧的括号。
目前,文件地址变量(filePath)必须是绝对路径。
Copy CREATE QUERY fileEx (STRING fileLocation) FOR GRAPH workNet {
FILE f1 (fileLocation);
P = {person.*};
PRINT "header" TO_CSV f1;
USWorkers = SELECT v FROM P:v
WHERE v.locationId == "us"
ACCUM f1.println(v.id, v.interestList);
PRINT "footer" TO_CSV f1;
}
INSTALL QUERY fileEx
RUN QUERY fileEx("/home/tigergraph/fileEx.txt")
赋值与累加器语句
赋值语句用于在声明变量后,设置或更新变量的值。 它适用于基本类变量,顶点集变量和累加器。 累加器也是一种特殊的+=累积语句,这个内容已经在累加器部分讨论过。 赋值语句可以使用表达式(expr)来定义变量的新值。
Copy ## Assignment Statement ##
assignStmt := name "=" expr # baseType variable, vertex set variable
| name "." name "=" expr # attribute of a vertex or edge
| name "." "@"name ("+="| "=") expr # vertex.attached accumulator
gAccumAssignStmt := "@@"name "=" expr # global accumulator
| loadAccumStmt
loadAccumStmt := "@@"name "=" "{" "LOADACCUM" loadAccumParams ["," "LOADACCUM" loadAccumParams]* "}"
赋值语句的限制
通常,赋值语句可以放在变量声明后的任何地方。然而,它仍然有两个限制。该限制适用于“内层”语句,即被包含在一个上层的语句块中的语句。
SELECT语句的ACCUM或POST-ACCUM子句
SELECT或UPDATE语句的正文内不允许使用全局累加器赋值
在ACCUM或POST-ACCUM子句中允许对全局变量赋值,但只有退出该子句后该值才生效。 因此,如果同一全局变量有多个赋值语句,则只有最后一个赋值语句才会生效。
ACCUM子句中不允许使用顶点属性赋值。 但是,允许对边属性赋值。 这是因为ACCUM子句是由边的集合迭代而来。
FOREACH循环中对循环变量有其他限制。 详见“数据修改”部分。
LOADACCUM 语句
Copy loadAccumStmt := "@@" name "=" "{" "LOADACCUM" loadAccumParams ("," "LOADACCUM" loadAccumParams)* "}"
loadAccumParams := "(" filePath "," columnId "," [columnId ","]*
stringLiteral "," (TRUE | FALSE) ")" [".".FILTER "(" condition ")"]
columnId := "$" (integer | stringLiteral)
LOADACCUM()可以通过从文件中加载数据来初始化全局累加器。 LOADACCUM()有3 + n个参数:(filePath,fieldColumn_1,....,fieldColumn_n,separator,header),其中n是累加器中的字段数。 一个这样的赋值语句可以有多个LOADACCUM()函数调用。 但是,引用同一赋值语句中相同文件的每个LOADACCUM()必须使用相同的分隔符和标头值。
使用通用的VERTEX作为元素类型的任何累加器都不能由LOADACCUM()初始化。
accumField1,....,
accumFieldN
若有标头,则为$num 或者$"column_name"
数据文件中,提供数据值到累加器每个字段的列的位置或列名。
下面是一个外部文件的示例
Copy person1,1,"test1",3
person5,2,"test2",4
person6,3,"test3",5
Copy CREATE QUERY loadAccumEx(STRING filename) FOR GRAPH socialNet {
TYPEDEF TUPLE<STRING aaa, VERTEX<post> ddd> yourTuple;
MapAccum<VERTEX<person>, MapAccum<INT, yourTuple>> @@testMap;
GroupByAccum<STRING a, STRING b, MapAccum<STRING, STRING> strList> @@testGroupBy;
@@testMap = { LOADACCUM (filename, $0, $1, $2, $3, ",", false)};
@@testGroupBy = { LOADACCUM ( filename, $1, $2, $3, $3, ",", true) };
PRINT @@testMap, @@testGroupBy;
}
Copy GSQL > RUN QUERY loadAccumEx("/file_directory/loadAccumInput.csv")
{
"error": false,
"message": "",
"version": {
"schema": 0,
"api": "v2"
},
"results": [{
"@@testGroupBy": [
{
"a": "3",
"b": "\"test3\"",
"strList": {"5": "5"}
},
{
"a": "2",
"b": "\"test2\"",
"strList": {"4": "4"}
}
],
"@@testMap": {
"person1": {"1": {
"aaa": "\"test1\"",
"ddd": "3"
}},
"person6": {"3": {
"aaa": "\"test3\"",
"ddd": "5"
}},
"person5": {"2": {
"aaa": "\"test2\"",
"ddd": "4"
}}
}
}]
}
函数调用语句
Copy funcCallStmt := name ["<" type ["," type"]* ">"] "(" [argList] ")"
| "@@"name ("." name "(" [argList] ")")+
argList := expr ["," expr]*
通常,调用一个函数会获得一个返回值,因此函数调用是表达式的一部分(参见第5节 - 运算符,函数和表达式)。 但是,在某些情况下,函数并不返回任何值(例如返回一个VOID)或返回的值可以被忽略。因此一个函数调用也可以看作一整个语句,即函数调用语句。
Copy ListAccum<STRING> @@listAcc;
BagAccum<INT> @@bagAcc;
...
# examples of function call statements
@@listAcc.clear();
@@bagAcc.removeAll(0);