Output Statements and FILE Objects
PRINT
Statement (API v2)
The PRINT
statement specifies output data.
Each execution of a PRINT
statement adds a JSON object to the results array which will be part of the query output.
A PRINT
statement can appear wherever query-body statements are permitted.
A A query can print a maximum of 2GB of data. If the output is to a |
printStmt := PRINT printExpr ("," printExpr)* [WHERE condition] [TO_CSV (filePath | fileVar)]
printExpr := (expr | vExprSet) [ AS jsonKey]
| tableName
vExprSet := expr "[" vSetProj ("," vSetProj)* "]"
vSetProj := expr [ AS jsonKey]
jsonKey := name
Each PRINT
statement contains a list of expressions for output data. The optional WHERE
clause filters the output to exclude any items for which the condition is false.
Each printExpr
contributes one key-value pair to the PRINT
statement’s JSON object result. The optional AS
clause sets the JSON key for the expression, overriding the default key (explained below).
If the query includes one more tabular |
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": {"edition": "developer","api": "v2","schema": 0},
"error": false,
"message": "",
"results": [
{
"str": "first statement",
"number": 5
},
{
"str": "second statement",
"number": 6
}
]
}
PRINT
Expressions
Each printExpr
may be one of the following:
-
A literal value
-
A global or local variable (including
VERTEX
andEDGE
variables) -
An attribute of a vertex variable, e.g.,
Person.name
-
A global accumulator
-
An expression whose terms are among the types above. The following operators may be used:
Data type | Operators |
---|---|
String |
concatenation: |
Set |
|
Numeric |
Arithmetic: |
Parentheses can be used for controlling order of precedence.
-
A vertex set variable
-
A vertex expression set
vExprSet
(only available if the output API is set to"v2"
. Vertex expression sets are explained below in the Vertex Expression Set section.
JSON Format: Keys
If a printExpr
includes the optional AS
clause, then the name sets the key for that expression in the JSON output. Otherwise, the following rules determine the key: *name*
-
If the expression is simply a single variable (local variable, global variable, global accumulator, or vertex set variable), then the key is the variable name.
-
The key for a vertex expression set is the vertex set variable name.
-
Otherwise, the key is the entire expression, represented as a string.
JSON Format: Values
Each data type has a distinct output format.
-
Simple numeric, string, and boolean data types follow JSON standards.
-
Lists, sets, bags, and arrays are printed as JSON arrays (i.e., a list enclosed in square brackets).
-
Maps and tuples are printed as JSON objects (i.e., a list of key:value pairs enclosed in curly braces).
-
Vertices and edges have a custom JSON object, shown below.
-
A vertex set variable is treated as a list of vertices.
-
Accumulator output format is determined by the accumulator’s return type. For example, an
AvgAccum
outputs aDOUBLE
value, and aBitwiseAndAccum
outputs anINT
value. For container accumulators, simply consider whether the output is a list, set, bag, or map.-
ListAccum
,SetAccum
,BagAccum
,ArrayAccum
: list -
MapAccum
: map -
HeapAccum
,GroupByAccum
: list of tuples
-
Full details of vertices are printed only when part of a vertex set variable or vertex expression set. When a single vertex is printed (from a variable or accumulator whose data type happens to be Cases where only the vertex id will be printed
|
Examples of printing various data types
Vertex (when not part of a vertex set variable)
The output is just the vertex id as a string:
"<vertex_id>"
Vertex (as part of a vertex set variable)
{
"v_id": "<vertex_id>",
"v_type": "<vertex_type>",
"attributes": {
<list of key:value pairs,
one for each attribute
or vertex-attached accumulator>
}
}
Vertex Expression Set
A vertex expression set is a list of expressions applied to each vertex in a vertex set variable. The expression list is used to compute an alternative set of values to display in the "attributes" field of each vertex.
The easiest way to understand this is to consider examples containing only one term and then consider combinations.
In this example, C is a vertex set variable containing the set of all company vertices. Furthermore, each vertex has a vertex-attached accumulator @count.
// CREATE VERTEX company(PRIMARY_ID clientId STRING, id STRING, country STRING)
CREATE QUERY v_expr_set () FOR GRAPH Work_Net {
SumAccum<INT> @count;
C = {Company.*};
// include some print statements here
}
If we print the full vertex set, the "attributes" field of each vertex will contain 3 fields: "id", "country", and "@count". Now consider some simple vertex expression sets:
-
PRINT C[C.country]
prints the vertex set variable C, except that the "attributes" field will contain only "country", instead of 3 fields. -
PRINT C[C.@count]
prints the vertex set variable C, except that the "attributes" field will contain only "@count", instead of 3 fields. -
PRINT C[C.@count AS company_count]
prints the same as above, except that the "@count" accumulator is aliased as "company_count". -
PRINT C[C.id, C.@count]
prints the vertex set variable C, except that the "attributes" field will contain only "id" and "@count". -
PRINT C[C.id+"_ex", C.@count+1]
prints the vertex set variable C, except that the "attributes" field contains the following:-
One field consists of each vertex’s id value, with the string "_ex" appended to it.
-
Another field consists of the @count value incremented by 1. Note: the value of @count itself has not changed, only the displayed value is incremented.
-
The last example illustrates the general format for a vertex expression set:
vExprSet := expr "[" vSetProj {, vSetProj} "]"
vSetProj := expr [ AS name]
The vertex expression set begins with the name of a vertex set variable, followed by a list of attribute expressions enclosed in square brackets. Each attribute expression follows the same rules described earlier in the PRINT
Expressions section.
That is, each attribute expression may refer to one or more attributes or vertex-attached accumulators of the current vertices, as well as literals, local or global variables, and global accumulators. The allowed operators (for numeric, string, or set operations) are the same ones mentioned above.
The key for the vertex expression set is the vertex set variable name.
The value for the vertex expression set is a modified vertex set variable, where the regular "attributes" value for each vertex is replaced with a set of key:value pairs corresponding to the set of attribute expressions given in the print expression.
An example which shows all of the cases described above in combination is shown below.
CREATE QUERY print_example_v2(VERTEX<Person> v) FOR GRAPH Social_Net {
SetAccum<VERTEX> @@set_of_vertices;
SetAccum<EDGE> @posted_set;
MapAccum<VERTEX,ListAccum<VERTEX>> @@test_map;
FLOAT paper_width = 8.5;
INT paper_height = 11;
STRING Alpha = "ABC";
Seed = Person.*;
A = SELECT s
FROM Seed:s
WHERE s.gender == "Female"
ACCUM @@set_of_vertices += s;
B = SELECT t
FROM Seed:s - (Posted>:e) - Post:t
ACCUM s.@posted_set += e,
@@test_map += (s -> t);
// Numeric, String, and Boolean expressions, with renamed keys:
PRINT paper_height*paper_width AS paper_size, Alpha+"XYZ" AS Letters,
A.size() > 10 AS a_size_more_than_10;
// 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, @@set_of_vertices;
// Map of Person -> Posts posted by that person:
PRINT @@test_map;
// 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 v_set_var_women;
// Vertex Set Expression. The same set of vertices as above, but with only one attribute plus one computed attribute:
PRINT A[A.gender, A.@posted_set.size()] AS v_set_expr;
}
Note how the results of the six
|
GSQL > RUN QUERY print_example_v2("person1")
{
"error": false,
"message": "",
"version": {
"edition": "developer",
"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"
}
]}
]
}
Printing CSV to a FILE Object
Instead of printing output in JSON format, output can be written to a FILE
object in comma-separated values (CSV) format by appending the keyword TO_CSV
followed by the FILE
object name to the PRINT
statement:
The FILE object can be a local file or an S3 bucket, allowing flexibility in how and where the output is stored.
PRINT @@set_of_vertices TO_CSV file1;
Each execution of the PRINT
statement appends one line to the FILE
. If the PRINT
statement includes multiple expressions, then each printed value is separated from its neighbor by a comma. If an expression evaluates to a set or list, then the collection’s values are delimited by single spaces. Due to the simpler format of CSV vs. JSON, the TO_CSV
feature only supports data with a simple one- or two-dimension structure.
Limitations of
|
Writing to
|
CREATE QUERY print_example_file() FOR GRAPH Social_Net {
SetAccum<VERTEX> @@test_set, @@test_set2;
ListAccum<STRING> @@str_list;
int x = 3;
FILE file1 ("/home/tigergraph/print_example_file.txt");
FILE s3_object ("s3://bucket-name/paht/print_example_file.txt");
Seed = Person.*;
A = SELECT s
FROM Seed:s
WHERE s.gender == "Female"
ACCUM @@test_set += s, @@str_list += s.gender;
A = SELECT s
FROM Seed:s
WHERE s.gender == "Male"
ACCUM @@test_set2 += s;
PRINT @@test_set, @@test_set2 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 @@str_list 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
PRINT @@test_set, @@test_set2 TO_CSV s3_object; // 1st line: 2 4 5, 1 3 6 7 8 (order not guaranteed) upload to s3 bucket
}
FILE println
statement
The FILE println
statement writes data to a FILE
object. Unlike the PRINT
statement, which is a query-body level statement, the FILE println
statement can be either a query-body level statement or a DML-sub-statement:
printlnStmt := fileVar".println" "(" expr ("," expr)* ")"
println
is a method of a FILE object variable. The println
statement can be used either at the query-body or DML-sub-statement level, e.g., within the ACCUM
clause of a SELECT
block. Each time println
is called, it adds one new line of values to the FILE
object, and then to the corresponding file.
The println
function can print any expression that can be printed by a PRINT
statement with the exception of vertex set variables. Vertex expression sets are also not applicable to the println
function.
If the println
statement has a list of expressions to print, it will produce a comma-separated list of values. If an expression refers to a list or set, then the output will be a list of values separated by spaces.
The data from query-body level |
Example
CREATE QUERY file_ex (STRING file_location) FOR GRAPH Work_Net {
FILE f1 (file_location);
P = {Person.*};
PRINT "header" TO_CSV f1;
USWorkers = SELECT v FROM P:v
WHERE v.location_id == "us"
ACCUM f1.println(v.id, v.interest_list);
PRINT "footer" TO_CSV f1;
}
INSTALL QUERY file_ex
RUN QUERY file_ex("/home/tigergraph/files")
All of the PRINT
statements in this example use the TO_CSV
option, so there is no JSON output to the console.
GSQL > RUN QUERY file_ex("/home/tigergraph/file_ex.txt")
{
"error": false,
"message": "",
"version": {
"edition": "developer",
"schema": 0,
"api": "v2"
},
"results": []
}
All the output in this case goes to the FILE
object. In the query definition, the line "header"
is printed first, followed by the println
statements in the ACCUM
clause, and "footer"
is printed last. The output in the file follows this order because the order of query-body level statements is maintained in the output.
[tigergraph@localhost]$ more /home/tigergraph/file_ex.txt
header
person7,art sport
person10,football sport
person4,football
person9,financial teaching
person1,management financial
footer
However, within the ACCUM
clause itself, the order of the println
statements is not guaranteed.
Passing a FILE Object as a Parameter
A FILE
Object can be passed from one query to a subquery. The subquery can then also write to the FILE
object.
CREATE QUERY file_param_sub(FILE f, STRING label, INT num) FOR GRAPH Social_Net {
f.println(label, "header");
FOREACH i IN RANGE [1,2] DO
f.println(label, num+i);
END;
f.println(label, "footer");
}
CREATE QUERY file_param_main(STRING main_label) FOR GRAPH Social_Net {
FILE f ("/home/tigergraph/file_param.txt");
f.println(main_label, "header");
FOREACH i IN RANGE [1,2] DO
f.println(main_label, i);
file_param_sub(f, "sub", 10*i);
END;
f.println(main_label, "footer");
}
GSQL > RUN QUERY file_param_main("main")
GSQL > EXIT
a
$ cat /home/tigergraph/file_param.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
Statement
The LOG
statement is another means to output data. It works as a function that outputs information to a log file:
logStmt := LOG "(" condition "," argList ")"
The first argument of the LOG statement is a boolean condition that enables or disables logging. This allows logging to be easily turned on/off for debugging. After the condition, LOG
takes one or more expressions (separated by commas). These expressions are evaluated and output to the log file.
Unlike the PRINT
statement, which can only be used as a query-body statement, the LOG
statement can be used as both a query-body statement and a DML-sub-statement.
The values will be recorded in the GPE log. To find the log file after the query has completed, open a Linux shell and use the command gadmin log gpe
. It may return more than one log file name; use the one ending in "INFO"
. Search this file for "UDF_"
.
BOOLEAN debug = TRUE;
INT x = 10;
LOG(debug, 20);
LOG(debug, 10, x);
RETURN
Statement
returnStmt := RETURN expr
The RETURN
statement specifies data that a subquery passes back to an outer query that called the subquery. The return type for a RETURN
statement can be any base type or accumulator type, but must be the same type as indicated by the RETURNS
clause of the subquery.
For subqueries to return a HeapAccum
or GroupByAccum
, the accumulators must be defined at the catalog level. See the example below:
TYPEDEF TUPLE<name STRING, friends INT> My_Tuple
TYPEDEF HeapAccum<My_Tuple>(3, friends DESC) My_Heap
CREATE QUERY sub_query_1() FOR GRAPH Social_Net RETURNS (My_Heap){
My_Heap @@heap; // Define the heap accumulator at the global level
SumAccum<INT> @friends;
Start = {Person.*};
Start = SELECT s from Start:s-(Friend:e)-:t
ACCUM s.@friends += 1
POST-ACCUM @@heap += My_Tuple(s.id, s.@friends);
RETURN @@heap;
}
CREATE QUERY query_1() FOR GRAPH Social_Net {
PRINT sub_query_1();
}