Declaration and Assignment Statements

In GSQL, different types of variables and objects follow different rules when it comes to variable declaration and assignment. This section discusses the different types of declaration and assignment statements and covers the following subset of the EBNF syntax:

EBNF
## Declarations ##
accumDeclStmt :=
          accumType localAccumName ["=" constant]
                    ["," localASccumName ["=" constant]]*
        | [STATIC] accumType globaAccumName ["=" constant]
                    ["," GlobalAccumName ["=" constant]]*
localAccumName := "@"accumName;
globalAccumName := "@@"accumName;
               
baseDeclStmt    := baseType name ["=" constant] ["," name ["=" constant]]*
fileDeclStmt := FILE fileVar "(" filePath ")"
fileVar := name

localVarDeclStmt := baseType varName "=" expr

vSetVarDeclStmt := vertexSetName ["(" vertexType ")"] "=" (seedSet | simpleSet | selectBlock)

simpleSet := vertexSetName
           | "(" simpleSet ")" 
           | simpleSet (UNION | INTERSECT | MINUS) simpleSet

seedSet := "{" [seed ["," seed ]*] "}"
seed := '_' 
      | ANY
      | vertexSetName
      | globalAccumName
      | vertexType ".*"
      | paramName
      | "SelectVertex" selectVertParams

selectVertParams := "(" filePath "," columnId "," (columnId | name) ","
          stringLiteral "," (TRUE | FALSE) ")" ["." FILTER "(" condition ")"]

columnId := "$" (integer | stringLiteral)

## Assignment Statements ##
assignStmt := name "=" expr
            | name "." attrName "=" expr
            
attrAccumStmt := name "." attrName "+=" expr
            
lAccumAssignStmt := vertexAlias "." localAccumName ("+="| "=") expr

gAccumAssignStmt :=  globalAccumName ("+=" | "=") expr

loadAccumStmt := globalAccumName "=" "{" LOADACCUM loadAccumParams
                                  ["," LOADACCUM loadAccumParams]* "}"

loadAccumParams := "(" filePath "," columnId ["," columnId]* ","
          stringLiteral "," (TRUE | FALSE) ")" ["." FILTER "(" condition ")"]

## Function Call Statement ##
funcCallStmt := name ["<" type ["," type]* ">"] "(" [argList] ")"
              | globaAccumName ("." funcName "(" [argList] ")")+
              | "reset_collection_accum" "(" accumName ")"
            
argList := expr ["," expr]*

Declaration Statements

There are six types of variable declarations in a GSQL query:

  • Accumulator

  • Global base type variable

  • Local base type variable

  • Vertex set

  • File object

  • Vertex or edge aliases

The first five types each have their own declaration statement syntax and are covered in this section. Aliases are declared implicitly in a SELECT statement.

Accumulators

Accumulator declaration is discussed in Section 4: "Accumulators".

Global Variables

After accumulator declarations, base type variables can be declared as global variables. The scope of a global variable is from the point of declaration until the end of the query.

EBNF for global variable declaration
baseDeclStmt    := baseType name ["=" constant]["," name ["=" constant]]*

A global variable can be accessed (read) anywhere in the query; however, there are restrictions on where it can be updated. See the subsection below on "Assignment Statements".

Example:

Global Variable Example
# 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 Query Result
GSQL > RUN QUERY globalVariable("person1")
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "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"
    }
  ]
}

Multiple global variables of the same type can be declared and initialized at the same line, as in the example below:

Multiple variable declaration example
CREATE QUERY variableDeclaration() FOR GRAPH minimalNet {
   // Global accumulators defined first
   MaxAccum<INT> @@max1 = 3, @@max2 = 5, @@max3;
   MaxAccum<INT> @@max4, @@max5 = 2; 
   // Multiple global variables defined in the same line
   INT a=5,b=1;
   INT c,d=10;

   PRINT a,b,c,d;
   PRINT @@max1, @@max2, @@max3, @@max4, @@max5;
}
variableDeclaration.json Result
GSQL > RUN QUERY variableDeclaration()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {
      "a": 5,
      "b": 1,
      "c": 0,
      "d": 10
    },
    {
      "@@max3": -9223372036854775808,
      "@@max2": 5,
      "@@max1": 3,
      "@@max5": 2,
      "@@max4": -9223372036854775808
    }
  ]
}

Local Variables

A local variable can be declared only in an ACCUM, POST-ACCUM, or UPDATE SET clause, and its scope is limited to that clause. Local variables can only be of base types. A local variable must be declared and initialized together in the same statement.

EBNF for local variable declaration and initialization
localVarDeclStmt := baseType varName "=" expr

Within a local variable's scope, another local variable with the same name cannot be declared at the same level. However, a new local variable with the same name can be declared at a lower level (i.e., within a nested SELECT or UPDATE statement). The lower declaration takes precedence at the lower level.

In a POST-ACCUM clause, each local variable may only be used in source vertex statements or target vertex statements, not both.

Example:

Local Variable Example
# 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 Query Results
GSQL > RUN QUERY localVariable("person1")
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{
    "dtGlob": "2010-01-11 03:26:05",
    "@@maxDateGlob": 0,
    "@@maxDate": 1263618953
  }]
}

Vertex Set Variable Declaration and Assignment

Vertex set variables play a special role within GSQL queries. They are used for both the input and output of SELECT statements. Therefore, before the first SELECT statement in a query, a vertex set variable must be declared and initialized. This initial vertex set is called the seed set.

EBNF for Vertex Set Variable Declaration
vSetVarDeclStmt := vertexSetName ["(" vertexType ")"] "=" (seedSet | simpleSet | selectBlock)

simpleSet := vertexSetName
      | "(" simpleSet ")"
      | simpleSet (UNION | INTERSECT | MINUS) simpleSet

seedSet := "{" [seed ["," seed ]*] "}"
seed := '_' 
      | ANY
      | vertexSetName
      | globalAccumName
      | vertexType ".*"
      | paramName
      | "SelectVertex" selectVertParams

selectVertParams := "(" filePath "," columnId "," (columnId | name) ","
     stringLiteral "," (TRUE | FALSE) ")" ["." FILTER "(" condition ")"]

columnId := "$" (integer | stringLiteral)

The query below lists all ways of assigning a vertex set variable an initial set of vertices (that is, forming a seed set).

  • A vertex parameter, untyped or typed, enclosed in curly brackets

  • A vertex set parameter, untyped or typed

  • A global SetAccum<VERTEX> accumulator, untyped or typed

  • All vertices of any type or of one type

  • A list of vertex IDs in an external file

  • Copy of another vertex set

  • A combination of individual vertices, vertex set parameters, or global variables, enclosed in curly brackets

  • Union of vertex set variables

Seed Set Example
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 };    # untyped vertex parameter enclosed in curly brackets
  S2 = { v2 };    # typed vertex parameter enclosed in curly brackets
  S3 = v3;                       # untyped vertex set parameter
  S4 = v4;                       # typed vertex set parameter
  S5 = @@testSet;                # untyped global set accumulator
  S6 = @@testSet2;               # typed global set accumulator
  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;                      # copy of another vertex set
  S12 = {@@testSet, v2, v3};     # Individual vertex: v2
                                 # Vertex set parameter: v3
                                 # global variable: @@testSet
                                 # Inside curly brackets cannot be put another
                                 # seedset, e.g., S1 
  S13 = S11 UNION S12;           # but we can use UNION to combine S1
}

When declaring a vertex set variable, a set of vertex types can be optionally specified to the vertex set variable. If the vertex set variable set type is not specified explicitly, the system determines the type implicitly by the vertex set value. The type can be ANY, _ (equivalent to ANY), or any explicit vertex type(s). See the EBNF grammar rule vertexEdgeType.

Declaration syntax difference: vertex set variable vs. base type variable

In a vertex set variable declaration, the type specifier follows the variable name and should be surrounded by parentheses: vSetName(type) This is different than a base type variable declaration, where the type specifier comes before the base variable name: type varName

After a vertex set variable is declared, the vertex type of the vertex set variable is immutable. Every assignment (e.g. SELECT statement) to this vertex set variable must match the type. The following is an example in which we must declare the vertex set variable type.

Vertex set variable type
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;
}

In the above example, the query returns the set of vertices after a 5-step traversal from the input person vertex. If we declare the vertex set variable S without explicitly giving a type, because the type of vertex parameter m1 is person, the GSQL engine will implicitly assign S to be person type. However, if S is assigned to person type, the SELECT statement inside the WHILE loop causes a type-checking error, because the SELECT block will generate all connected vertices, including non-person vertices. Therefore, S must be declared as an ANY-type vertex set variable.

FILE Object Declaration

A FILE object is a sequential text storage object, associated with a text file on the local machine.

EBNF for FILE object declaration
fileDeclStmt := FILE fileVar "(" filePath ")"
fileVar := name

When a FILE object is declared, associated with a particular text file, any existing content in the text file will be erased. During the execution of the query, content written to or printed to the FILE object will be appended to the FILE object. When the query where the FILE object is declared finishes running, the content of the FILE object is saved to the text file.

Example:

File object query example
CREATE QUERY getUSWorkerInterests (STRING fileLocation) FOR GRAPH workNet {
    // Declare FILE object f1
    FILE f1 (fileLocation);
    // Initialize a seed set of all person vertices
    P = {person.*};

    PRINT "header" TO_CSV f1;
  
    // Select workers located in the US and print their interests onto
    // the FILE object
    USWorkers = SELECT v FROM P:v
              WHERE v.locationId == "us"
              ACCUM f1.println(v.id, v.interestList);
    PRINT "footer" TO_CSV f1;
}
INSTALL QUERY getUSWorkers
RUN QUERY getUSWorkerInterests("/home/tigergraph/fileEx.txt")

Assignment and Accumulate Statements

Assignment statements are used to set or update the value of a variable after it has been declared. This applies to base type variables, vertex set variables, and accumulators. Accumulators also have the special += accumulate statement, which was discussed in the Accumulator section. Assignment statements can use expressions to define the new value of the variable.

EBNF for Assignment Statements
## Assignment Statement ##
assignStmt := name "=" expr         # baseType variable, vertex set variable
            | name "." name "=" expr      # attribute of a vertex or edge
            
attrAccumStmt := name "." attrName "+=" expr
            
lAccumAssignStmt := vertexAlias "." localAccumName ("+="| "=") expr 

gAccumAssignStmt :=  globalAccumName ("+=" | "=") expr  

loadAccumStmt := globalAccumName "=" "{" "LOADACCUM" loadAccumParam
                                  ["," "LOADACCUM" loadAccumParams]* "}"

Vertex and edge (non-accumulator) attributes can use the += operator in an ACCUM or POST-ACCUM clause to perform parallel accumulation.

attrAccumStmt := name "."attrName "+=" expr

Restrictions on Assignment Statements

In general, assignment statements can take place anywhere after the variable has been declared. However, there are some restrictions. These restrictions apply to "inner level" statements which are within the body of a higher-level statement:

  • The ACCUM or POST-ACCUM clause of a SELECT statement

  • The SET clause of an UPDATE statement

  • The body of a FOREACH statement

  • Global accumulator assignment is not permitted within the body of SELECT or UPDATE statements

  • Global variable assignment is permitted in ACCUM or POST-ACCUM clauses, but the change in value will not take place until exiting the clause. Therefore, if there are multiple assignment statements for the same variable, only the final one will take effect.

  • Vertex attribute assignment is not permitted in an ACCUM clause. However, edge attribute assignment is permitted. This is because the ACCUM clause iterates over an edge set.

  • There are additional restrictions within FOREACH loops for the loop variable. See the Data Modification section.

LOADACCUM Statement

loadAccumStmt := globalAccumName "=" "{" LOADACCUM loadAccumParams
                                  ["," LOADACCUM loadAccumParams]* "}"

loadAccumParams := "(" filePath "," columnId ["," [columnId]* ","
          stringLiteral "," (TRUE | FALSE) ")" ["."FILTER "(" condition ")"]
columnId := "$"(integer | stringLiteral)

LOADACCUM() can initialize a global accumulator by loading data from a file. LOADACCUM() has 3+n parameters explained in the table below, where n is the number of fields in the accumulator.

Any accumulator using generic VERTEX as an element type cannot be initialized by LOADACCUM().

Parameters:

One assignment statement can have multiple LOADACCUM() function calls. However, every LOADACCUM() referring to the same file in the same assignment statement must use the same separator and header parameter values.

Example:

loadAccumInput.csv
person1,1,"test1",3
person5,2,"test2",4
person6,3,"test3",5
LoadAccum example
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;
}
Results of Query loadAccumEx
GSQL > RUN QUERY loadAccumEx("/file_directory/loadAccumInput.csv")
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "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"
      }}
    }
  }]
}

Function Call Statements

funcCallStmt := name ["<" type ["," type]* ">"] "(" [argList] ")"
              | globalAccumName ("." funcName "(" [argList] ")")+
              | "reset_collection_accum" "(" accumName ")"
           
argList := expr ["," expr]*

Typically, a function call returns a value and so is part of an expression. In some cases, however, the function does not return a value (i.e., returns VOID) or the return value can be ignored, so the function call can be used as an entire statement. This is a Function Call Statement.

Examples of Function Call statements
ListAccum<STRING> @@listAcc;
BagAccum<INT> @@bagAcc;
...
# examples of function call statements
@@listAcc.clear();
@@bagAcc.removeAll(0);

Clear Collection Accumulators

Collection accumulators (e.g., ListAccum, SetAccum, MapAccum) grow in size as data is added. Particularly for vertex-attached accumulators, if the number of vertices is large, their memory consumption can be significant. It can improve system performance to clear or reset collection accumulators during a query as soon as their data is no longer needed. Running the reset_collection_accum(accumName) function resets the collection(s) to be zero-length (empty). If the argument is a vertex-attached accumulator, then the entire set of accumulators is reset.

"reset_collection_accum" "(" accumName ")"

reset_collection_accum only works in DISTRIBUTED mode queries. If the query is not in distributed mode, the reset does not take place.

CREATE DISTRIBUTED QUERY reset_accum() 
FOR GRAPH workNet SYNTAX v2 { 
  ListAccum<STRING> @stuff;
  ListAccum<STRING> @@allStuff;

  Comp = SELECT c
    FROM    person:p -(worksFor:w)- company:c
    ACCUM   c.@stuff += p.id,
            @@allStuff += p.id,
            c.@stuff += p.locationId,
            @@allStuff += p.locationId,
            FOREACH interest IN p.interestList DO
              c.@stuff += interest,
              @@allStuff += interest
            END
  ;
  // display accum size: should be full
  PRINT Comp[Comp.@stuff.size()] AS stuffCount;
  PRINT @@allStuff.size() AS allStuffCount;
  
  reset_collection_accum(@stuff);
  reset_collection_accum(@@allStuff);
  // display accum size: should be empty
  PRINT Comp[Comp.@stuff.size()] AS stuffClear;
  PRINT @@allStuff.size() AS allStuffClear;
}
[{
    "stuffCount": [
      {"attributes": {"Comp.@stuff.size()": 23},
        "v_id": "company2",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 7},
        "v_id": "company4",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 12},
        "v_id": "company3",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 21},
        "v_id": "company1",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 4},
        "v_id": "company5",
        "v_type": "company"
      }]
  },
  {
    "allStuffCount": 67
  },
  {
    "stuffClear": [
      {"attributes": {"Comp.@stuff.size()": 0},
        "v_id": "company2",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 0},
        "v_id": "company4",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 0},
        "v_id": "company3",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 0},
        "v_id": "company1",
        "v_type": "company"
      },
      {"attributes": {"Comp.@stuff.size()": 0},
        "v_id": "company5",
        "v_type": "company"
      }]
  },
  {
    "allStuffClear": 0
  }]

Last updated