前面的章节重点介绍了构成一个查询最基础的组成部分:数据类型(第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 );