输出语句以及文件对象

PRINT语句用于输出数据。 每次执行PRINT语句,系统都会向结果数组添加一个JSON对象,该结果数组将成为查询输出的一部分。 任何允许存在查询主体层语句的地方都可以使用PRINT语句。

PRINT语句并不即时输出结果,而是在查询结束时,将PRINT语句收集的所有信息一次性完整输出。

EBNF范式
printStmt := PRINT printExpr {,printExpr} [WHERE condition][TO_CSV (filePath | fileVar)]
printExpr := (expr | vExprSet) [ AS name]
vExprSet  := expr "[" vSetProj {, vSetProj} "]"
vSetProj  := expr [ AS name]

每个PRINT语句内都是一系列表达式,用于定义输出的内容。 我们还可以选择WHERE子句过滤输出结果。 如果某个WHERE子句的条件检查为假,则在最终结果中排除对应的内容。

每个printExpr为PRINT语句的JSON对象提供一个键值对。 我们还可以选择AS子句来配置对应的键,从而覆盖默认键(下例中详述)。

JSON输出格式的简单示例
STRING str = "first statement";
INT number = 5;
PRINT str, number;
 
str = "second statement";
number = number + 1;
PRINT str, number;
 
# The statements above produce the following output
{
 "version": {"api": "v2","schema": 0},
 "error": false,
 "message": "",
 "results": [
   {
     "str": "first statement",
     "number": 5
   },
   {
     "str": "second statement",
     "number": 6
   }
 ]
}

printExpr可能是下列内容之一:

  1. 文字值

  2. 全局或局部变量(包括VERTEX和EDGE变量)

  3. 顶点变量的一个属性,例如Person.name

  4. 全局累积器

  5. 包含上述所有项目的一个表达式。以下运算符可以被使用:

    数学运算符

    算术: + - * / . % 比特: << >> & |

    字符串

    字符串连接: +

    集合

    并集 交集 集合相减

  6. 括号可用于设定运算优先顺序。

  7. 一个顶点集变量

  8. 一个顶点表达式的集合vExprSet(仅当输出API版本设定为v2的时候有效。顶点表达式的集合下文有详述。)

在输出API v2中,PRINT包含的表达式可以混搭任意类型。

在输出API v1中,顶点集变量不能与其他类型的表达式位于同一PRINT语句中。

JSON 格式: 键

如果选择在printExpr中添加AS name子句,则用name命名JSON输出的键名。 否则,则以下述规则确定键名:如果表达式只是单个变量(如局部变量,全局变量,全局累加器或顶点集变量),则键名就是变量名称。 若该键是顶点表达式集,则键名是顶点集变量名。 若键是整个表达式,则该键为一个字符串。

JSON 格式: 值

每种数据类型都有不同的输出格式。

  • 简单的数字,字符串和布尔数据类型遵循JSON标准。

  • 列表,集合,包和数组打印为JSON数组(例如方括号中的一组数据)。

  • 映射和元组作为JSON对象打印(例如,用大括号括起来一组的键值对)。

  • 顶点和边包含自定义JSON对象,如下所示。

  • 顶点集变量被视为一系列顶点。

  • 累加器输出格式由累加器的返回值类型决定。 例如,AvgAccum输出DOUBLE值,BitwiseAndAccum输出INT值。 对于容器累加器,只需考虑输出是列表,集合,包还是映射。

    • ListAccum,SetAccum,BagAccum,ArrayAccum:列表

    • MapAccum:映射

    • HeapAccum,GroupByAccum:一系列元组

仅当我们只选择了顶点集变量或顶点表达式集合中的某一部分的时候,PRINT才会打印出顶点的详细信息。 而当打印单个顶点时(即从一个变量或累加器获得数据,且它的数据类型是VERTEX),只打印顶点id。

例:只有顶点ID被打印的场景
ListAccum<VERTEX> @@vList;  // not a vertex set variable
VERTEX v;                   // not a vertex set variable
...
PRINT @@vList, v;           // output will contain only vertex ids

输出值为顶点(当该顶点不是一个顶点集变量的成员时)

以字符串方式输出顶点id:

顶点(当该顶点不是一个顶点集变量的成员时)的输出值格式
"<vertex_id>"

顶点(当该顶点不是一个顶点集变量的成员时)的输出值格式

顶点(当该顶点是一个顶点集变量的成员时)的输出值格式
{
 "v_id":   "<vertex_id>",
 "v_type": "<vertex_type>",
 "attributes": {
   <list of key:value pairs,
    one for each attribute
    or vertex-attached accumulator>
 }
}

输出值为边

当输出值为边时的输出格式
{
 "e_type":    "<edge_type>",
 "directed":  <boolean_value>,
 "from_id":   "<source_vertex_id>",
 "from_type": "<source_vertex_type>",
 "to_id":     "<target_vertex_id>",
 "to_type":   "<target_vertex_type>",
 "attributes": {
   <list of key:value pairs,
    one for each attribute>
 }
}

当输出值为边时的输出格式

当输出值为列表, Set或者Bag时的输出格式
[
 <value1>,
 <value2>,
 ...,
 <valueN>
]

当输出值为列表, Set或者Bag时的输出格式

当输出值为映射时的输出格式
当输出值为列表, Set或者Bag时的输出格式

输出值为元组

当输出值为元组时的输出格式
{
 <fieldName1>: <value1>,
 <fieldName2>: <value2>,
 ...,
 <fieldNameN>: <valueN>
}

输出值为顶点集变量

当输出值为顶点集变量时的输出格式
[
 <vertex1>,
 <vertex2>,
 ...,
 <vertexN>
]

顶点表达式集合(Vertex Expression Set)

顶点表达式集合是作用于一个顶点集合的每一个顶点的一组表达式,它用于指明每个顶点的所需要显示的属性。

理解这一点的最简单方法如下:先考虑只有一项的集合,然后再考虑含有多项的集合。例如,下面的查询中 C是一个顶点集变量,它包含了所有的公司顶点(company vertex)组成的集合。 此外,每个顶点都有一个附属于顶点的累加器@count。

例:顶点表达式集查询示例
# CREATE VERTEX company(PRIMARY_ID clientId STRING, id STRING, country STRING)
 
CREATE QUERY vExprSet () FOR GRAPH workNet {
 SumAccum<INT> @count;
 C = {company.*};
 
 # include some print statements here
}

如果我们打印输出完整的顶点集,则每个顶点的“属性”会包含三个字段:“id”,“country”和“@count”。现在,假设我们带入一些简单的顶点表达式集:

PRINT C[C.country]

打印出顶点集变量C,但“属性”中仅包含“country”字段,其它两个省略

PRINT C[C.@count]

打印出顶点集变量C,但“属性”中仅包含“@count”字段,其它两个省略

PRINT C[C.id, C.@count]

打印出顶点集变量C,但“属性”中仅包含“id”和“@count”两个字段。

PRINT C[C.id+"_ex", C.@count+1]

打印出顶点集变量C,但“属性”被限制为:

  • 一个字段,字段由每个顶点的id加后缀“_ex”组成。

  • 另一个字段,该字段由@count的值加1得到。注意:@count本身的值并没有改变,只是显示的值递增而已。

最后一个示例演示了一个顶点表达式集的一般格式:

顶点表达式集的一般语法
vExprSet  := expr "[" vSetProj {, vSetProj} "]"
vSetProj  := expr [ AS name]

顶点表达式集以顶点集变量的名称开头,然后是一系列包含在方括号中的属性表达式。 每个属性表达式都遵循之前 “PRINT表达式”中的规则。 也就是说,每个属性表达式可以指代当前顶点的一个或多个属性,或附属于该顶点的累加器,或者是文字,或局部或全局的变量以及累加器。 所有可用的运算符(用于数字,字符串或集合操作)都与前文中提到的相同。

顶点表达式集的“键”是顶点集变量的名称。

顶点表达式集的“值”是修改后的顶点集变量,其中每个顶点的默认的“属性”值被替换为一组键值对,这些键值对由PRINT表达式中的一系列属性表达式给出。

下面的示例包括了上述所有情况。

PRINT的简单示例
CREATE QUERY printExampleV2(VERTEX<person> v) FOR GRAPH socialNet {
 
 SetAccum<VERTEX> @@setOfVertices;
 SetAccum<EDGE> @postedSet;
 MapAccum<VERTEX,ListAccum<VERTEX>> @@testMap;
 FLOAT paperWidth = 8.5;
 INT paperHeight = 11;
 STRING Alpha = "ABC";
 
 Seed = person.*;
 A = SELECT s
     FROM Seed:s
     WHERE s.gender == "Female"
     ACCUM @@setOfVertices += s;
 
 B = SELECT t
     FROM Seed:s - (posted:e) -> post:t
     ACCUM s.@postedSet += e,
       @@testMap += (s -> t);
 
# Numeric, String, and Boolean expressions, with renamed keys:
 PRINT paperHeight*paperWidth AS PaperSize, Alpha+"XYZ" AS Letters,
   A.size() > 10 AS AsizeMoreThan10;
# Note how an expression is named if "AS" is not used:
 PRINT A.size() > 10;
 
# Vertex variables。Only the vertex id is included (no attributes):
 PRINT v, @@setOfVertices;
 
# Map of Person -> Posts posted by that person:
 PRINT @@testMap;
 
# Vertex Set Variable. Each vertex has a vertex-attached accumulator, which
# happens to be a set of edges (SetAccum<EDGE>), so edge format is shown also:
 PRINT A AS VSetVarWomen;
 
# Vertex Set Expression. The same set of vertices as above, but with only
# one attribute plus one computed attribute:
 PRINT A[A.gender, A.@postedSet.size()] AS VSetExpr;
}

请着重关注这六个PRINT语句的结果是如何分组的,在JSON的“results”字段中给出:

  1. 这六个PRINT语句中的每一个都是带有“results”数组的JSON对象。

  2. 当一个PRINT语句包含多个表达式时(比如第一个例子),输出的顺序可能不同于表达式在PRINT语句内部的顺序。

  3. 第2个PRINT语句演示了如何从表达式本身生成键。

  4. 第3个和第4个PRINT语句分别演示了一组顶点(不是顶点集变量)和一个映射。

  5. 第5个PRINT语句演示了顶点集变量A,以及附属于它顶点的累加器(PRINT A)。

  6. 第6个PRINT语句演示A的顶点集表达式,并被手动定义为仅包含一个静态属性加一个刚计算出来的新属性。

printExampleV2(带有注释)的结果
GSQL > RUN QUERY printExampleV2("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {
     "AsizeMoreThan10": false,
     "Letters": "ABCXYZ",
     "PaperSize": 93.5
   },
   {"A.size()>10": false},
   {
     "v": "person1",
     "@@setOfVertices": [ "person4", "person5", "person2" ]
   },
   {"@@testMap": {
     "person4": ["3"],
     "person3": ["2"],
     "person2": ["1"],
     "person1": ["0"],
     "person8": [ "7", "8" ],
     "person7": [ "9", "6" ],
     "person6": [ "10", "5" ],
     "person5": [ "4", "11" ]
   }},
   {"VSetVarWomen": [
     {
       "v_id": "person4",
       "attributes": {
         "gender": "Female",
         "id": "person4",
         "@postedSet": [{
           "from_type": "person",
           "to_type": "post",
           "directed": true,
           "from_id": "person4",
           "to_id": "3",
           "attributes": {},
           "e_type": "posted"
         }]
       },
       "v_type": "person"
     },
     {
       "v_id": "person5",
       "attributes": {
         "gender": "Female",
         "id": "person5",
         "@postedSet": [
           {
             "from_type": "person",
             "to_type": "post",
             "directed": true,
             "from_id": "person5",
             "to_id": "11",
             "attributes": {},
             "e_type": "posted"
           },
           {
             "from_type": "person",
             "to_type": "post",
             "directed": true,
             "from_id": "person5",
             "to_id": "4",
             "attributes": {},
             "e_type": "posted"
           }
         ]
       },
       "v_type": "person"
     },
     {
       "v_id": "person2",
       "attributes": {
         "gender": "Female",
         "id": "person2",
         "@postedSet": [{
           "from_type": "person",
           "to_type": "post",
           "directed": true,
           "from_id": "person2",
           "to_id": "1",
           "attributes": {},
           "e_type": "posted"
         }]
       },
       "v_type": "person"
     }
   ]},
   {"VSetExpr": [
     {
       "v_id": "person4",
       "attributes": {
         "A.@postedSet.size()": 1,
         "A.gender": "Female"
       },
       "v_type": "person"
     },
     {
       "v_id": "person5",
       "attributes": {
         "A.@postedSet.size()": 2,
         "A.gender": "Female"
       },
       "v_type": "person"
     },
     {
       "v_id": "person2",
       "attributes": {
         "A.@postedSet.size()": 1,
         "A.gender": "Female"
       },
       "v_type": "person"
     }
   ]}
 ]
}

打印成一个CSV格式的文件对象

除了以JSON格式打印输出我们想要的数据之外,还可以打印成以逗号作为分隔符的CSV格式文件(FILE对象)。 如果要这么做,请在PRINT语句的末尾添加关键字TO_CSV,并在它之后注明文件对象的名称:

例:打印成CSV文件的语句格式:
PRINT @@setOfVertices TO_CSV file1; 

不再支持用大于号“>”将结果输出为文件。 现在必须使用关键字TO_CSV。

每次执行PRINT语句都会在文件对象中附加一个新的行。 如果PRINT语句中包含多个表达式,则输出的值与值之间用逗号分隔。 如果表达式得到的结果值为一个集合或列表,则集合内部的值由单个空格分隔。 相较于JSON, CSV的格式更加简单,所以TO_CSV的功能仅支持具有简单一维或二维结构的数据。

打印到文件功能的限制

  • 不能够一次性打印完整的顶点集变量。

  • 如果打印的是一个顶点,则仅打印其ID值。

  • 如果打印的是一个顶点集的附属累加器或顶点集变量,则结果是一张由值组成的列表,每个顶点对应列表上的一个值,由换行符分隔。

  • 打印到文件时打印顶点集表达式的语法与打印到标准输出时的语法不同。 试比较:

    • PRINT A[A.gender]; # 带有括号

    • PRINT A.gender TO_CSV file1; #不带括号

写入文件对象的动作针对并行处理进行了优化。 因此,我们无法保证数据写入文件的顺序可控。 因此,我们强烈建议用户在设计查询逻辑的时候,满足其中以下一个条件:

  1. 本次查询只打印一组数据,且该组数据的先后顺序不重要。

  2. 要打印到文件的每行数据都包含一个标签,可用于标识数据。

PRINT WHERE 和PRINT TO_CSV FILE Object 的示例
CREATE QUERY printExampleFile() FOR GRAPH socialNet {
 SetAccum<VERTEX> @@testSet, @@testSet2;
 ListAccum<STRING> @@strList;
 int x = 3;
 FILE file1 ("/home/tigergraph/printExampleFile.txt");
 
 Seed = person.*;
 A = SELECT s
     FROM Seed:s
     WHERE s.gender == "Female"
     ACCUM @@testSet += s, @@strList += s.gender;
 A = SELECT s
     FROM Seed:s
     WHERE s.gender == "Male"
     ACCUM @@testSet2 += s;
 
 PRINT @@testSet, @@testSet2 TO_CSV file1;  # 1st line: 2 4 5, 1 3 6 7 8 (order not guaranteed)
 PRINT x WHERE x < 0 TO_CSV file1;   # 2nd line: <skipped because no content>
 PRINT x WHERE x > 0 TO_CSV file1;   # 3rd line: 3
 PRINT @@strList TO_CSV file1;       # 4th line: Female Female Female
 PRINT A.gender TO_CSV file1;     # 5th line: Male\n Male\n Male\n Male\n Male
}

将CSV文件数据打印到一个常规文件(不建议使用)

除了可以将CSV输出打印到文件对象外,也可以直接将数据写入常规文件。

PRINT to CSV FILE 的语法示例
PRINT @@setOfVertices TO_CSV "/home/tigergraph/vset.csv"; 

不推荐使用此功能,因为它和打印到文件对象的功能相同。

下表详述了TO_CSV <文件对象>与TO_CSV <文件路径>之间的差异。

文件对象

文件路径

何时文件路径被指定

可以在运行时,也可以在编译时,用户可以在编写查询时二选一

只能是编译的时候

顶点的ID

正确显示

显示为顶点的内部ID

添加到末尾或覆写

添加到末尾,但文件对象声明时会重置文件对象本身

总是添加到末尾

文件地址可以是绝对路径或相对路径

目前只能是绝对路径

绝对路径或相对路径皆可

FILE println语句

有两种方法将运算结果打印到文件中,除了上文提到的TO_CSV方法外,还可以使用FILE println语句

FILE println 语句的EBNF范式
printlnStmt := fileVar".println" "(" expr {, expr} ")"

println是文件对象变量的一个方法(或称为函数)。 println语句可以在查询主体层语句中使用,也可以在DML子句中使用(例如在SELECT语句的ACCUM子句中使用)。 每次调用println时,它都会先向文件对象添加一行新的值,然后再将其更新到相应的文件中。

println函数可以完成PRINT语句的大多数功能。 但请注意,这其中不包括顶点表达式集(vExprSet)。 如果println语句有一系列表达式需要打印,则它会生成一个以逗号分隔的值的列表。 如果一个表达式引用了列表或集合,则输出将是由空格分隔的值的列表,这与TO_CSV生成的格式是相同的。

位于查询主体层语句中的文件打印语句(TO_CSV或println)会将数据以其原始顺序显示。 但是,由于ACCUM语句是并行的,因此无法保证DML子句级别的println语句也按照原始顺序排列。 甚至,ACCUM语句块中println语句的输出,可能会随着查询主体语句分散在任何地方。

文件对象查询的示例
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;
}

在这个示例中,所有的PRINT语句中都选用了TO_CSV选项,所以这个例子中并没有JSON的输出。

fileEx的结果
GSQL > RUN QUERY fileEx("/home/tigergraph/fileEx.txt")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": []
}

这种情况下,所有的输出都是文件对象。 在此查询的定义中,页脚行(footer)是最后一个文件对象语句。但是SELECT语句中的println语句有可能被延迟,在页脚行之后打印。

例:由fileEx产生内容的文件
[tigergraph@localhost]$more /home/tigergraph/fileEx.txt
header
person7,art sport
person10,football sport
person4,football
person9,financial teaching
person1,management financial
footer

以参数(Parameter)形式传递文件对象

一个文件对象可以从某个查询传递到子查询,同时该子查询也可以写入文件对象。

例:一个查询将文件对象传递给另一个查询
CREATE QUERY fileParamSub(FILE f, STRING label, INT num) FOR GRAPH socialNet {
   f.println(label, "header");
   FOREACH i IN RANGE [1,2] DO
       f.println(label, num+i);
   END;
   f.println(label, "footer");
}
 
CREATE QUERY fileParamMain(STRING mainlabel) FOR GRAPH socialNet {
   FILE f ("/home/tigergraph/fileParam.txt");
   f.println(mainlabel, "header");
   FOREACH i IN RANGE [1,2] DO
       f.println(mainlabel, i);
       fileParamSub(f, " sub", 10*i);
   END;
   f.println(mainlabel, "footer");
}

GSQL > RUN QUERY fileParamMain("main")
GSQL > EXIT
 
$cat /home/tigergraph/fileParam.txt
main,header
main,1
sub,header 
sub,11 
sub,12 
sub,footer 
main,2
sub,header 
sub,21 
sub,22 
sub,footer 
main,footer

LOG语句

LOG语句是另一种输出方法。它以一个函数的形式工作,并输出信息到一个日志文件。

LOG 语句的EBNF范式
logStmt := LOG "(" condition "," argList ")"

LOG语句的第一个参数是一个布尔值,用于启用或禁用日志记录功能。 通过这个参数,我们可以轻松地打开或关闭日志记录,以便进行调试等用途。 在该参数之后,是一个或多个表达式(以逗号分隔)。系统通过计算这个表达式并得到结果,最终输出到日志文件。

PRINT语句只能用作查询主体层,但LOG语句不同,它既可以用于查询主体语句中,也可以用于DML子句。

信息被记录在GPE的日志文件中。 如果想要在查询完成后找到日志文件,请打开Linux shell并使用命令“gadmin log gpe”。 输出的结果可能会包含多个日志文件名; 其中以“INFO”结尾的那个是我们要的文件。 随后,请在此文件中搜索“UDF_”。

例:
BOOLEAN debug = TRUE;
INT x = 10;
 
LOG(debug, 20);
LOG(debug, 10, x);

RETURN语句

RETURN 语句的EBNF范式
createQuery := CREATE QUERY name "(" [parameterList] ")" FOR GRAPH name [RETURNS "("  baseType | accumType ")"] "{" [typedefs][declStmts] queryBodyStmts "}"
 
returnStmt := RETURN expr

RETURN语句将子查询生成的数据传递回调用该子查询的母查询。为了将一个查询用作子查询,其初始CREATE QUERY语句必须包含RETURNS子句,并且其正文必须以RETURN语句结束。 RETURNS子句只允许包含一种类,因此RETURN语句只能返回一个结果。返回的结果必须与RETURNS子句指示的类型相同。必须在相应的超级查询之前创建子查询。必须在母查询的同一个INSTALL QUERY命令之前或之中安装子查询。

返回类型可以是任何基本类或任何累加器类,但GroupByAccum累加器和包含任何元组类型的累加器除外。出于确定返回类型的目的,SetAccum等效于Set,而BagAccum等同于Bag。如果在RETURNS子句中使用SET <VERTEX <type>>或SetAccum<VERTEX <type>>(<type>是可选的),则可以返回一个顶点集变量。

另请参见 - 查询和函数。

子查询的示例 1
CREATE QUERY subquery1 (VERTEX<person> m1) FOR GRAPH socialNet RETURNS(BagAccum<VERTEX<post>>)
{
 Start = {m1};
 L = SELECT t
     FROM Start:s - (liked:e) - post:t;
 RETURN L;
}
CREATE QUERY mainquery1 () FOR GRAPH socialNet
{
 BagAccum<VERTEX<post>> @@testBag;
 Start = {person.*};
 Start = SELECT s FROM Start:s
         ACCUM @@testBag += subquery1(s);
 PRINT @@testBag;
}
结果
GSQL > RUN QUERY mainquery1()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@testBag": [
   "6",
   "3",
   "8",
   "4",
   "4",
   "0",
   "0",
   "0",
   "10"
 ]}]
}
子查询的示例 2
CREATE QUERY subquery2 (VERTEX<person> m1) FOR GRAPH socialNet RETURNS(INT)
{
 int x;
 Start = {m1};
 Start = SELECT t FROM Start:t
         ACCUM CASE WHEN t.gender == "Male" THEN x = 5
                    WHEN t.gender == "Female" THEN x = 10
                    ELSE x = -1
               END;
 RETURN x;
}
CREATE QUERY mainquery2 (SET<VERTEX<person>> m1) FOR GRAPH socialNet
{
 SumAccum<INT> @@sum1;
 Start = {m1};
 Start = SELECT t FROM Start:t
         ACCUM @@sum1 += subquery2(t);
 PRINT @@sum1;
}
结果
GSQL > RUN QUERY mainquery2(["person1","person2"])
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@sum1": 15}]
}

Last updated