声明和赋值

前面的章节重点介绍了构成一个查询最基础的组成部分:数据类型(第3章),运算符,函数和表达式(第5章),以及一个专门介绍累加器的章节(第4章)。现在开始,我们要介绍GSQL查询中的语句类型。 本节重点介绍声明和赋值语句。 后面的部分将详细介绍最为关键的SELECT语句,控制流语句和数据修改语句。 此外,某些类型的语句还可以嵌套在SELECT或UPDATE或控制流语句中。

EBNF范式
## 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语句声明的章节中。

累加器

vertexEdgeType := "_" | ANY | name | ( "(" name ["|" name]* ")" )

有关累加器的声明请详见第四章“累加器”。

全局变量

在累加器声明之后,基本类变量可以声明为全局变量。 全局变量的作用域是从声明处开始,直到查询结束。

全局变量声明的EBNF范式
baseDeclStmt    := baseType name ["=" constant][, name ["=" constant]]*

用户可以在查询中的任何位置访问(读取)全局变量,但并不是在任何地方都能更新该变量。 细节请参阅下文中有关“赋值”的小节。

全局变量的示例
# 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;
}
globalVariable 查询 的结果
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"
   }
 ]
}

用户可以在同一行声明和初始化多个相同类型的全局变量,如下例所示:

多变量声明的示例
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 的结果
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),且必须在同一语句中声明的同时初始化局部变量。

局部变量的声明和初始化EBNF范式
localVarDeclStmt := baseType name "=" expr

在局部变量的作用域内,同一级的子句中不能声明另一个相同名称的局部变量。 但是,一个新的与该变量同名的变量可以在低级别子句中(例如又嵌套了一层SELECT或UPDATE语句)使用。 即低级别的声明只在该级别工作。

在POST-ACCUM子句中,每个局部变量只能用于初始顶点或目标顶点两者之一,而不能同时用于两者。

局部变量示例
# 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
}
localVariable 查询的结果
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)。

顶点集变量声明的EBNF范式
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)

  • 另一个顶点集的副本(S11)

  • 各个顶点,顶点集参数或全局变量的组合(S12)

  • 顶点集变量的并集(S13)

种子集的示例
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语句)都必须与该数据类型匹配。 以下的示例中,我们必须声明顶点集变量的类型。

顶点集变量类型
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大写,以将其与普通的文件区分开来。

文件对象声明的EBNF范式
fileDeclStmt := FILE fileVar "(" filePath ")"
fileVar := name

当用户声明一个关联某个文本文件的文件对象时,文本文件中的任何现有内容都会被删除。 在执行查询期间,输出的内容会写到该文件对象的最后。 查询完成运行后,文件对象的内容将保存到文本文件中。

请注意,声明语句会调用文件对象构造函数。 构造函数的语法包含文件地址变量两侧的括号。

目前,文件地址变量(filePath)必须是绝对路径。

文件对象查询的示例
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)来定义变量的新值。

赋值语句的EBNF范式
## 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子句

  • UPDATE语句的SET子句

  • FOREACH语句的正文

  • SELECT或UPDATE语句的正文内不允许使用全局累加器赋值

  • 在ACCUM或POST-ACCUM子句中允许对全局变量赋值,但只有退出该子句后该值才生效。 因此,如果同一全局变量有多个赋值语句,则只有最后一个赋值语句才会生效。

  • ACCUM子句中不允许使用顶点属性赋值。 但是,允许对边属性赋值。 这是因为ACCUM子句是由边的集合迭代而来。

  • FOREACH循环中对循环变量有其他限制。 详见“数据修改”部分。

LOADACCUM 语句

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()初始化。

下面是一个外部文件的示例

loadAccumInput.csv
person1,1,"test1",3
person5,2,"test2",4
person6,3,"test3",5
LoadAccum的例子
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;
}
查询 loadAccumEx的结果
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"
     }}
   }
 }]
}

函数调用语句

funcCallStmt := name ["<" type ["," type"]* ">"] "(" [argList] ")"
             | "@@"name ("." name "(" [argList] ")")+
         
argList := expr ["," expr]*

通常,调用一个函数会获得一个返回值,因此函数调用是表达式的一部分(参见第5节 - 运算符,函数和表达式)。 但是,在某些情况下,函数并不返回任何值(例如返回一个VOID)或返回的值可以被忽略。因此一个函数调用也可以看作一整个语句,即函数调用语句。

函数调用语句的例子
ListAccum<STRING> @@listAcc;
BagAccum<INT> @@bagAcc;
...
# examples of function call statements
@@listAcc.clear();
@@bagAcc.removeAll(0);

Last updated