Operators, Functions, and Expressions

An expression is a combination of fixed values, variables, operators, function calls, and groupings which specify a computation, resulting in a data value. This section of the specification describes the literals (fixed values), operators, and functions available in the GSQL query language. It covers the subset of the EBNF definitions shown below. However, more so than in other sections of the specification, syntax alone is not an adequate description. The semantics (functionality) of the particular operators and functions are an essential complement to the syntax.

EBNF for Operations, Functions, and Expressions
constant := numeric | stringLiteral | TRUE | FALSE | GSQL_UINT_MAX
          | GSQL_INT_MAX | GSQL_INT_MIN | TO_DATETIME "(" stringLiteral ")"
  
mathOperator := "*" | "/" | "%" | "+" | "-" | "<<" | ">>" | "&" | "|"

comparisonOperator := "<" | "<=" | ">" | ">=" | "==" | "!="

condition := expr
           | expr comparisonOperator expr
           | expr [ NOT ] IN setBagExpr
           | expr IS [ NOT ] NULL
           | expr BETWEEN expr AND expr
           | "(" condition ")"
           | NOT condition
           | condition (AND | OR) condition
           | (TRUE | FALSE)
    
expr :=   ["@@"]name
		| name "." "type"
		| name "." ["@"]name
		| name "." "@"accumName ["\'"]
		| name "." name "." name "(" [argList] ")"
		| name "." name "(" [argList] ")" [ ".".FILTER "(" condition ")" ]
		| name ["<" type ["," type]* ">"] "(" [argList] ")"
		| name "." "@"accumName ("." name "(" [argList] ")")+ ["." name]
		| "@@"accumName ("." name "(" [argList] ")")+ ["." name]
		| COALESCE "(" [argList] ")"
		| ( COUNT | ISEMPTY | MAX | MIN | AVG | SUM ) "(" setBagExpr ")"
		| expr mathOperator expr
		| "-" expr
		| "(" expr ")"
		| "(" argList "->" argList ")"	// key value pair for MapAccum
		| "[" argList "]"               // a list
		| constant
		| setBagExpr
		| name "(" argList ")"          // function call or a tuple object 
		
setBagExpr := ["@@"]name 
    	    | name "." ["@"]name
		    | name "." "@"name ("." name "(" [argList] ")")+
		    | name "." name "(" [argList] ")" [ ".".FILTER "(" condition ")" ]
		    | "@@"name ("." name "(" [argList] ")")+
		    | setBagExpr (UNION | INTERSECT | MINUS) setBagExpr
		    | "(" argList ")"
		    | "(" setBagExpr ")"


argList := expr ["," expr]*  

Constants

constant := numeric | stringLiteral | TRUE | FALSE | GSQL_UINT_MAX
          | GSQL_INT_MAX | GSQL_INT_MIN | TO_DATETIME "(" stringLiteral ")"

Each primitive data type supports constant values:

Data Type

Constant

Examples

Numeric types (INT, UINT, FLOAT, DOUBLE)

numeric

123 -5 45.67 2.0e-0.5

UINT

GSQL_UINT_MAX

INT

GSQL_INT_MAX GSQL_INT_MIN

boolean

TRUE FALSE

string

stringLiteral

"atoz@com" "0.25"

GSL_UINT_MAX = 2 ^ 64 - 1 = 18446744073709551615

GSQL_INT_MAX = 2 ^ 63 - 1 = 9223372036854775807

GSQL_INT_MIN = -2 ^ 63 = -9223372036854775808

Operators

An operator is a keyword token which performs a specific computational function to return a resulting value, using the adjacent expressions (its operands) as input values. An operator is similar to a function in that both compute a result from inputs, but syntactically they are different. The most familiar operators are the mathematical operators for addition + and subtraction - .

Tip: The operators listed in this section are designed to behave like the operators in MySQL.

Mathematical Operators and Expressions

We support the following standard mathematical operators and meanings. The latter four ("<<" | ">>" | "&" | "|") are for bitwise operations. See the section below: "Bit Operators".

mathOperator := "*" | "/" | "%" | "+" | "-" | "<<" | ">>" | "&" | "|"

Operator precedences are shown in the following list, from highest precedence to the lowest. Operators that are shown together on a line have the same precedence:

Operator Precedence, highest to lowest
*, /, %
-, +
<<, >>
&
|
==, >=, >, <=, <, !=
Example 1. Math Operators + - * /
CREATE QUERY mathOperators() FOR GRAPH minimalNet api("v2")
{
    int x,y;
    int z1,z2,z3,z4,z5;
    float f1,f2,f3,f4;

    x = 7;
    y = 3;

    z1 = x * y;    # z = 21
    z2 = x - y;    # z = 4
    z3 = x + y;    # z = 10
    z4 = x / y;    # z = 2
    z5 = x / 4.0;  # z = 1
    f1 = x / y;    # v = 2
    f2 = x / 4.0;  # v = 1.75
    f3 = x % 3;    # v = 1
    f4 = x % y;    # z = 1
    
    PRINT x,y;
    PRINT z1 AS xTIMESy, z2 AS xMINUSy, z3 AS xPLUSy, z4 AS xDIVy, z5 AS xDIV4f;
    PRINT f1 AS xDIVy,   f2 AS xDIV4f,  f3 AS xMOD3,  f4 AS xMODy;
}
mathOperators.json Results
GSQL > RUN QUERY mathOperators()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {
      "x": 7,
      "y": 3
    },
    {
      "xTIMESy": 21,
      "xPLUSy": 10,
      "xMINUSy": 4,
      "xDIVy": 2,
      "xDIV4f": 1
    },
    {
      "xMODy": 1,
      "xMOD3": 1,
      "xDIVy": 2,
      "xDIV4f": 1.75
    }
  ]
}

Boolean Operators

We support the standard Boolean operators and standard order of precedence: AND, OR, NOT

Bit Operators

Bit operators (<<, >>, &, and |) operate on integers and return an integer.

Bit Operators
CREATE QUERY bitOperationTest() FOR GRAPH minimalNet{
  PRINT 80 >> 2;     # 20
  PRINT 80 << 2;     # 320
  PRINT 2 + 80 >> 4; # 5
  PRINT 2 | 3 ;      # 3 
  PRINT 2 & 3 ;      # 2
  PRINT 2 | 3 + 2;   # 7 
  PRINT 2 & 3 - 2;   # 0
}

String Operators

Operator + can be used for concatenating strings.

Tuple Fields

The fields of the tuple can be accessed using the dot operator.

Comparison Operators and Conditions

A condition is an expression which evaluates to a boolean value of either true or false. One type of condition uses the familiar comparison operators. A comparison operator compares two numeric values.

comparisonOperator := "<" | "<=" | ">" | ">=" | "==" | "!="

condition := expr
           | expr comparisonOperator expr
           | expr [ NOT ] IN setBagExpr
           | expr IS [ NOT ] NULL
           | expr BETWEEN expr AND expr
           | "(" condition ")"
           | NOT condition
           | condition (AND | OR) condition
           | (TRUE | FALSE)
           | expr NOT? LIKE expr (ESCAPE ESCAPE_CHAR)?  

BETWEEN expr AND expr

The expression expr1 BETWEEN expr2 AND expr3 is true if the value expr1 is in the range from expr2 to expr3, including the endpoint values. Each expression must be numeric.

" expr1 BETWEEN expr2 AND expr3 " is equivalent to " expr1 <= expr3 AND expr1 >= expr2".

BETWEEN AND example
CREATE QUERY mathOperatorBetween() FOR GRAPH minimalNet
{
    int x;
    bool b;
    x = 1;
    b = (x BETWEEN 0 AND 100); PRINT b;  # True
    b = (x BETWEEN 1 AND 2); PRINT b;    # True
    b = (x BETWEEN 0 AND 1); PRINT b;    # True
}

IS NULL, IS NOT NULL

IS NULL and IS NOT NULL can be used for checking whether an optional parameter is given any value.

IS NULL example
CREATE QUERY parameterIsNULL (INT p) FOR GRAPH minimalNet {
  IF p IS NULL THEN
    PRINT "p is null";
  ELSE
    PRINT "p is not null";
  END;
}
parameterIsNULL.json Results
GSQL > RUN QUERY parameterIsNULL(_)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"p is null": "p is null"}]
}
GSQL > RUN QUERY parameterIsNULL(3)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"p is not null": "p is not null"}]
}

Every attribute value stored in GSQL is a valid value, so IS NULL and IS NOT NULL is only effective for query parameters.

LIKE

expr [NOT] LIKE expr [ESCAPE escape_char]

The LIKE operator is used for string pattern matching and can only be used in WHERE clauses. The expression string1 LIKE string_patternevaluates to boolean true if string1 matches the pattern in string_pattern; otherwise, it is false.

Both operands must be strings and string_pattern must be a literal string or a string pattern. A string_pattern can contain characters as well as the following wildcard and other special symbols, in order to express a pattern (<char_list>indicates a placeholder):

character or syntax

meaning

%

matches zero or more characters.

Example : %abc% matches any string which contains the sequence "abc".

_ (underscore)

matches any single character. Example : _abc_ematches any 6-character string where the 2nd to 4th characters are "abc" and the last character is "e".

[charlist]

match any character in charlist. charlist is a concatenated character set, with no separators. Example : [Tiger]matches either T, i, g, e, or r.

[^charlist]

matches any character NOT in charlist. Example : [^qxz]matches any character other than q, x, or z.

[!charlist]

matches any character NOT in charlist.

special syntax within charlist

α-β

matches a character in the range from α to β. A charlist can have multiple ranges. Example : [a-mA-M0-3]matches a letter from a to m, upper or lower case, or a digit from 0 to 3.

special syntax within charlist

\\

matches the character \

special syntax within charlist

\\]

matches the character ] No special treatment is needed for [ inside a charlist. Example : %[\\]!]matches any string which ends with either ] or !

Mathematical Functions

There are a number of built-in functions which act on either an accumulator, a base type, or vertex variable. The accumulator function calls are discussed in detail in the "Accumulators" section.

Below is a list of built-in functions which act on either INT, FLOAT, or DOUBLE value(s).

function name and parameters(NUM means INT, FLOAT, or DOUBLE)

description

return type

abs (NUM num )

Returns the absolute value of num

Same as parameter type

sqrt ( NUM num )

Returns the square root of num

FLOAT

pow ( NUM base , NUM exp )

Returns base exp

If base and exp are both INT → INT; Otherwise → FLOAT

acos ( NUM num )

arc cosine

FLOAT

asin ( NUM num )

arc sine

FLOAT

atan ( NUM num )

arc tangent

FLOAT

atan2 (NUM y , NUM x )

arc tangent of y / x

FLOAT

ceil ( NUM num )

rounds upward

INT

cos ( NUM num )

cosine

FLOAT

cosh ( NUM num )

hyperbolic cosine

FLOAT

exp ( NUM num )

base-e exponential

FLOAT

floor ( NUM num )

rounds downward

INT

fmod (NUM numer , NUM denom )

floating-point remainder of numer / denom

FLOAT

ldexp (NUM x , NUM exp )

Returns x multiplied by 2 raised to the power of exp. x * (2^exp)

FLOAT

log ( NUM num )

natural logarithm

FLOAT

log10 ( NUM num )

common (base-10) logarithm

FLOAT

sin ( NUM num )

sine

FLOAT

sinh ( NUM num )

hyperbolic sine

FLOAT

tan ( NUM num )

tangent

FLOAT

tanh ( NUM num )

hyperbolic tangent

FLOAT

to_string (NUM num )

Converts num to a STRING value

STRING

float_to_int (FLOAT num )

Converts num to a INT value by truncating the floating part

INT

str_to_int (STRING str )

Converts str to a INT value. If str is a floating number, the floating part is truncated; If str is not a numerical value, returns 0.

INT

Type Conversion Functions

function name and parameters

description

return type

to_string (NUM num)

Converts num to a STRING

STRING

float_to_int (FLOAT num)

Converts num to a INT value by truncating the floating point

INT

str_to_int (STRING str)

Concerts str to a INT value. If str is a floating number, the floating part is truncated. If str is not a numerical value, returns 0.

INT

String Functions

The following built-in functions are provided for text processing. Note that these functions do not modify the input parameter. They each return a new string.

function name and parameters

description

return type

lower(STRING str )

Converts str to all lowercase letters

STRING

upper(STRING str )

Converts str to all uppercase letters

STRING

trim( [ [ LEADING | TRAILING | BOTH ] [STRING removal_char ] FROM ] STRING str )

Trims* characters from the leading and/or trailing ends of str

STRING

  • In the syntax for trim(), the words in bold ( LEADING, TRAILING, BOTH, and FROM ) are keywords which should appear exactly as shown.

  • STRING is just an indicator of the datatype; it is not an explicit keyword.

  • The trim() function have the following options:

    • By using one of the keywords LEADING, TRAILING, or BOTH, the user can specify that characters are to be removed from the left end, right end, or both ends of the string, respectively. If none of these keywords is used, the function will removed from both ends.

    • removal_char is a single character. The function will remove consecutive instances of removal_char , until it encounters a different character. If removal_char is not specified, then trim() removes whitespace (spaces, tabs, and newlines).

CREATE QUERY stringFuncEx() FOR GRAPH minimalNet {
    #Example strings
    string a = "  Abc   ";
    string b = "aa ABC aaa";
    string c = " a A   ";
    
    PRINT lower(a);                        # prints "  abc   "
    PRINT upper(b);                        # prints "AA ABCC AAA"
    PRINT trim(a);                         # prints "Abc"
    PRINT trim(BOTH a);                    # prints "Abc"
    PRINT trim(LEADING c);                 # prints "a A   "
    PRINT trim(TRAILING "a" FROM b);       # prints "aa ABC "
    
    #You can combine functions for more convenient calling:
    PRINT trim(BOTH trim(BOTH " " FROM c) FROM b);
    # prints "BC"
}

Notes about the trim() function:

Datetime Functions

The following functions convert from/to DATETIME to/from other types.

function name and parameters

description

return type

to_datetime (STRING str )

Converts str to a DATETIME value

DATETIME

epoch_to_datetime (INT int_value )

Converts int_value to a DATETIME value by epoch time conversion

DATETIME

datetime_to_epoch (DATETIME date )

Converts date to epoch time.

INT

The following function converts a DATETIME value into a string format specified by the user:

function name and parameters

description

return type

datetime_format( DATETIME date[, STRING str ] )

Prints date as the str indicates. The following specifiers may be used as the format of str . The “%” character is required before the format specifier characters. If str is not given, "%Y-%m-%d %H:%M:%S" is used. Specifier:

  • %Y: Year, numeric, four digits

  • %S: Seconds (0..59)

  • %m: Month, numeric (1..12)

  • %M: Minutes, numeric (0..59)

  • %H: Hour, numeric (0..23)

  • %d: Day of the month, numeric (1..31)

STRING

datetime_format example
# Show all posts's post time
CREATE QUERY allPostTime() FOR GRAPH socialNet api("v2") {
  start = {post.*};
  #PRINT datetime_format(start.postTime, "a message was posted at %H:%M:%S on %Y/%m/%d");
  PRINT start[datetime_format(start.postTime, "a message was posted at %H:%M:%S on %Y/%m/%d") as postTimeMsg]; // api v2
}

The followings are other functions related to DATETIME :

function name and parameters

description

return type

now()

Returns the current time in DATETIME type.

DATETIME

year( DATETIME date )

Extracts the year of date .

INT

month( DATETIME date )

Extracts the month of date.

INT

day( DATETIME date )

Extracts the day of month of date .

INT

hour( DATETIME date )

Extracts the hour of date .

INT

minute( DATETIME date )

Extracts the minute of date .

INT

second( DATETIME date )

Extracts the second of date .

INT

datetime_add( DATETIME date, INTERVAL int_value time_unit)

INTERVAL is a keyword; time_unit is one of the keywords YEAR, MONTH, DAY, HOUR, MINUTE, or SECOND. The function returns the DATETIME value which is int_value units later than date . For example, datetime_add( now() , INTERVAL 1 MONTH ) returns a DATETIME value which is 1 month from now.

DATETIME

datetime_sub( DATETIME date, INTERVAL int_value time_unit )

Same as datetime_add, except that the returned value is int_value units earlier than date .

DATETIME

datetime_diff( DATETIMEdate1 , DATETIME date2)

Returns the difference in seconds of these two DATETIME values: ( date1 - date2 ) .

INT

JSONOBJECT and JSONARRAY Functions

JSONOBJECT and JSONARRAY are base types, meaning they can be used as a parameter type, an element type for most accumulators, or a return type. This enables the input and output of complex, customized data structures. For input and output, a string representation of the JSON is used. Hence, the GSQL query language offers several functions to convert a formatted string into JSON and then to search and access the components of a JSON structure.

Data Conversion Functions

The following parsing functions convert a string into a JSONOBJECT or a JSONARRAY:

function name

description

return type

parse_json_object(STRING str )

Converts str into a JSON object

JSONOBJECT

parse_json_array( STRING str )

Converts str into a JSON array

JSONARRAY

Both functions generate a run-time error if the input string cannot be converted into a JSON object or a JSON array. To be properly formatted, besides having the proper nesting and matching of curly braces { } and brackets [ ], each value field must be one of the following: a string (in double quotes "), a number, a boolean ( true or false ), or a JSONOBJECT or JSONARRAY. Each key of a key:value pair must be a string in double quotes.

See examples below.

parse_json_object and parse_json_array example
CREATE QUERY jsonEx (STRING strA, STRING strB) FOR GRAPH minimalNet {
  JSONARRAY jsonA;
  JSONOBJECT jsonO;

  jsonA = parse_json_array( strA );
  jsonO = parse_json_object( strB );

  PRINT jsonA, jsonO;
}
jsonEx.json Result
GSQL > RUN QUERY jsonEx("[123]","{\"abc\":123}")
or curl -X GET 'http://localhost:9000/query/jsonEx?strA=\[123\]&strB=\{"abc":123\}' 
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{
    "jsonA": [123],
    "jsonO": {"abc": 123}
  }]
}
GSQL > RUN QUERY jsonEx("{123}","{\"123\":\"123\"}")
Runtime Error: {123} cannot be parsed as a json array.

Data Access Methods

JSONOBJECT and JSONARRAY are object classes, each class supporting a set of data access methods, using dot notation:

jsonVariable.functionName(parameter_list)

The following methods (class functions) can act on a JSONOBJECT variable:

method name

description

return type

containsKey(STRING keyStr )

Returns a boolean value indicating whether the JSON object contains the key keyStr .

BOOL

getInt(STRING keyStr )

Returns the numeric value associated with key keyStr as an INT.

INT

getDouble (STRING keyS tr )

Returns the numeric value associated with key keyStr as a DOUBLE.

DOUBLE

getString (STRING keyS tr )

Returns the string value associated with key keyStr .

STRING

getBool (STRING keyS tr )

Returns the bool value associated with key keyStr .

BOOL

getJsonObject (STRING keyS tr )

Returns the JSONOBJECT associated with key keyStr .

JSONOBJECT

getJsonArray (STRING keySt r )

Returns the JSONARRAY associated with key keyStr .

JSONARRAY

The above getType(STRING keyStr ) function generates a run-time error if

  1. The key keyStr doesn't exist, or

  2. The function's return type is different than the stored value type. See the next note about numeric data.

  3. Pure JSON stores "numbers" without distinguishing between INT and DOUBLE, but for TigerGraph, if the input value is all digits, it will be stored as INT. Other numeric values are stored as DOUBLE. The getDouble function can read an INT and return its equivalent DOUBLE value, but it is an error to call getINT for a DOUBLE value.

The following methods can act on a JSONARRAY variable:

method name

description

return type

size()

Returns the size of this array.

INT

getInt( INT idx )

Returns the numeric value at position idx as an INT.

INT

getDouble( INT idx )

Returns the numeric value at position idx as a DOUBLE.

DOUBLE

getString( INT idx )

Returns the string value at position idx .

STRING

getBool( INT idx )

Returns the bool value at position idx .

BOOL

getJsonObject( INT idx )

Returns the JSONOBJECT value at position idx .

JSONOBJECT

getJsonArray( INT idx )

Returns the JSONARRAY value at position idx .

JSONARRAY

Similar to the methods of JSONOBJECT, the above getType(INT idx ) function generates a run-time error if

  1. idx is out of bounds, or

  2. The function's return type is different than the stored value type. See the next note about numeric data.

  3. Pure JSON stores "numbers" without distinguishing between INT and DOUBLE, but for TigerGraph, if the input value is all digits, it will be stored as INT. Other numeric values are stored as DOUBLE. The getDouble function can read an INT and return its equivalent DOUBLE value, but it is an error to call getINT for a DOUBLE value.

Below is an example of using these functions and methods :

JSONOBJECT and JSONARRAY function example
CREATE QUERY jsonEx2 () FOR GRAPH minimalNet {
  
  JSONOBJECT jsonO, jsonO2;
  JSONARRAY jsonA, jsonA2;
  STRING str, str2;

  str = "{\"int\":1, \"double\":3.0, \"string\":\"xyz\", \"bool\":true, \"obj\":{\"obj\":{\"bool\":false}}, \"arr\":[\"xyz\",123,true] }";
  str2 = "[\"xyz\", 123, false, 5.0]";
  jsonO = parse_json_object( str ) ;
  jsonA = parse_json_array( str2 ) ;


  jsonO2 = jsonO.getJsonObject("obj");
  jsonA2 = jsonO.getJsonArray("arr");

  PRINT jsonO;
  PRINT jsonO.getBool("bool"), jsonO.getJsonObject("obj"), jsonO.getJsonArray("arr"), jsonO2.getJsonObject("obj"), jsonA2.getString(0) , jsonA.getDouble(3), jsonA.getDouble(1);
}
jsonEx2.json Result
GSQL > RUN QUERY jsonEx2()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [ {"jsonO": { "arr": [ "xyz", 123, true ],
      "bool": true,
      "string": "xyz",
      "double": 3,
      "obj": {"obj": {"bool": false}},
      "int": 1
    }},
    {
      "jsonO.getBool(bool)": true,
      "jsonA.getDouble(3)": 5,
      "jsonA.getDouble(1)": 123,
      "jsonO.getJsonObject(obj)": {"obj": {"bool": false}},
      "jsonO2.getJsonObject(obj)": {"bool": false},
      "jsonO.getJsonArray(arr)": [ "xyz", 123, true ],
      "jsonA2.getString(0)": "xyz"
    }
  ]
}

Vertex, Edge, and Accumulator Functions and Attributes

Accessing attributes

Attributes on vertices or edges are defined in the graph schema. Additionally, each vertex and edge has a built-in STRING attribute called type which represents the user-defined type of that edge or vertex. These attributes, including type , can be accessed for a particular edge or vertex with the dot operator.

For example, the following code snippet shows two different SELECT statements which produce equivalent results. The first uses the dot operator on the vertex variable v to access the "subject" attribute, which is defined in the graph schema. The FROM clause in the first SELECT statement necessitates that any target vertices will be of type "post" (also defined in the graph schema). The second SELECT schema checks that the vertex variable v's type is a "post" vertex by using the dot operator to access the built-in type attribute.

Accessing vertex variable attributes
CREATE QUERY coffeeRelatedPosts() FOR GRAPH socialNet
{
    allVertices = {ANY};
    results = SELECT v FROM allVertices:s -(:e)-> post:v WHERE v.subject == "coffee";
    PRINT results;
    results = SELECT v FROM allVertices:s -(:e)-> :v WHERE v.type == "post" AND v.subject == "coffee";
    PRINT results;
}
Results for Query coffeeRelatedPosts
GSQL > RUN QUERY coffeeRelatedPosts()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {"results": [{
      "v_id": "4",
      "attributes": {
        "postTime": "2011-02-07 05:02:51",
        "subject": "coffee"
      },
      "v_type": "post"
    }]},
    {"results": [{
      "v_id": "4",
      "attributes": {
        "postTime": "2011-02-07 05:02:51",
        "subject": "coffee"
      },
      "v_type": "post"
    }]}
  ]
}

Vertex Functions

Below is a list of built-in functions that can be accessed by vertex aliases, using the dot operator:

Syntax for vertex functions
vertex_alias.function_name(parameter)[.FILTER(condition)]

Currently, these functions are only available for vertex aliases (defined in the FROM clause); vertex variables do not have these functions.

Note that in order to calculate outdegree by edge type, the graph schema must be defined such that vertices keep track of their edge types using WITH STATS="OUTDEGREE_BY_EDGETYPE" (however, "OUTDEGREE_BY_EDGETYPE" is now the default STATS option).

function name

description

return type

outdegree ([STRING edgeType ])

Returns the recent number* [see footnote] of outgoing or undirected edges connected to the vertex. If the optional STRING argument edgeTypeis given, then count only edges of the given edgeType.

INT

neighbors ([ STRING edgeType ])

Returns the set of ids for the vertices which are out-neighbors or undirected neighbors of the vertex. If the optional STRING argument edgeType is given, then include only those neighbors reachable by edges of the given edgeType .

BagAccum<VERTEX>

neighborAttribute ( STRING edgeType,STRING targetVertexType, STRING attribute)

From the given vertex, traverses the given edgeType to the given targetVertexType , and return the set of values for the given attribute . edgeType can only be string literal.

BagAccum<attributeType>

edgeAttribute ( STRING edgeType, STRINGattribute )

From the given vertex, traverses the given edgeType , and return the set of values for the given edge attribute . edgeTypecan only be string literal.

BagAccum<attributeType>

Note on outdegree(): This function reads a metadata value stored with each vertex, to avoid traversing the graph and thus have a fast response. The snapshot transaction semantics means that outdegree() may sometimes read an old value if there are concurrent write transactions. To guarantee an accurate count, traverse the neighboring edges and count them with a SumAccum, or use a function like neighbors() and then use size() on the set.

Vertex function examples
CREATE QUERY vertexFunctionExample(vertex<person> m1) FOR GRAPH socialNet {

  SetAccum<Vertex> @neighborSet; 
  SetAccum<Vertex> @neighborSet2;
  SetAccum<DATETIME> @attr1;
  BagAccum<DATETIME> @attr2;

  int deg1, deg2, deg3, deg4;

  S = {m1};
  S2 = SELECT S
       FROM S - (posted:e) -> post:t
       ACCUM deg1 = S.outdegree(), 
             deg2 = S.outdegree("posted"),
             deg3 = S.outdegree(e.type),  # same as deg2 
             STRING str = "posted",
             deg4 = S.outdegree(str);     # same as deg2 
  PRINT deg1, deg2, deg3, deg4; 

  S3 = SELECT S
       FROM S:s
       POST-ACCUM s.@neighborSet += s.neighbors(),
                  s.@neighborSet2 += s.neighbors("posted"),
                  s.@attr1 += s.neighborAttribute("posted", "post", "postTime"),
                  s.@attr2 += s.edgeAttribute("liked", "actionTime"); 
  PRINT S3;
}
vertexFunctionExample Result
GSQL > RUN QUERY vertexFunctionExample("person5")
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {
      "deg4": 2,
      "deg2": 2,
      "deg3": 2,
      "deg1": 5
    },
    {"S3": [{
      "v_id": "person5",
      "attributes": {
        "@attr2": [1263330725],
        "@attr1": [
          1297054971,
          1296694941
        ],
        "gender": "Female",
        "@neighborSet": [
          "6",
          "11",
          "4",
          "person7",
          "person4"
        ],
        "id": "person5",
        "@neighborSet2": [
          "4",
          "11"
        ]
      },
      "v_type": "person"
    }]}
  ]
}

FILTER

The optional .FILTER(condition) clause offers an additional filter for selecting which elements are added to the output set of the neighbor, neighborAttribute and edgeAttribute functions. The condition is evaluated for each element . If the condition is true, the element is added to the output set; if false, it is not. An example is shown below:

Example: vertex functions with optional filter
CREATE QUERY filterEx (SET<STRING> pIds, INT yr) FOR GRAPH workNet api("v2") {

  SetAccum<vertex<company>> @recentEmplr, @allEmplr;
  BagAccum<string> @diffCountry, @allCountry;

  Start = {person.*};

  L0 = SELECT v
       FROM  Start:v
       WHERE v.id IN pIds
       ACCUM
         # filter using edge attribute
         v.@recentEmplr += v.neighbors("worksFor").filter(worksFor.startYear >= yr),
         v.@allEmplr += v.neighbors("worksFor").filter(true),

        # vertex alias attribute and neighbor type attribute
        v.@diffCountry += v.neighborAttribute("worksFor", "company", "id")
                          .filter(v.locationId != company.country),
        v.@allCountry += v.neighborAttribute("worksFor", "company", "id")
       ;

  PRINT yr, L0[L0.@recentEmplr, L0.@allEmplr, L0.@diffCountry, L0.@allCountry]; // api v2
}
Results in filterEx.json
GSQL > RUN QUERY filterEx(["person1","person2"],2016)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{
    "L0": [
      {
        "v_id": "person1",
        "attributes": {
          "L0.@diffCountry": ["company2"],
          "L0.@recentEmplr": ["company1"],
          "L0.@allCountry": [ "company1", "company2" ],
          "L0.@allEmplr": [ "company2", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person2",
        "attributes": {
          "L0.@diffCountry": ["company1"],
          "L0.@recentEmplr": [],
          "L0.@allCountry": [ "company1", "company2" ],
          "L0.@allEmplr": [ "company2", "company1" ]
        },
        "v_type": "person"
      }
    ],
    "yr": 2016
  }]
}

Edge Functions

Below are the built-in functions that can be accessed by edge aliases, using the dot operator. Edge functions follow the same general rules as vertex functions (see above).

function name

description

return type

isDirected ()

Returns a boolean value indicating whether this edge is directed or undirected.

BOOL

Accumulator Functions

This section describes functions which all to all or most accumulators. Other accumulator functions for each accumulator type are illustrated at the "Accumulator Type" section.

Previous value of accumulator

The tick operator ( ' ) can be used to read the value of an accumulator as it was at the start an ACCUM clause, before any changes that took place within the ACCUM clause. It can only be used in the POST-ACCUM clause. A typical use is to compare the value of the accumulator before and after the ACCUM clause. The PageRank algorithm provides a good example:

v = SELECT s
    FROM start:s - (e_type:e) -> :t
    ACCUM t.@received_score += s.@score/(s.outdegree(e_type))
    POST-ACCUM s.@score = (1.0 - damping) + damping * s.@received_score,
               s.@received_score = 0,
               @@max_diff += abs(s.@score - s.@score'); 

In the last line, we compute @@max_diff as the absolute value of the difference between the post-ACCUM score (s.@score) and the pre-ACCUM score (s.@score').

Set/Bag Expression and Operators

SELECT blocks take an input vertex set and perform various selection and filtering operations to produce an output set. Therefore, set/bag expressions and their operators are a useful and powerful part of the GSQL query language. A set/bag expression can use either SetAccum or BagAccum.

BNF
setBagExpr := ["@@"] name 
		    | name "." ["@"] name
		    | name "." "@" name ("." name "(" [argList] ")")+
		    | name "." name "(" [argList] ")" [ ".".FILTER "(" condition ")" ]
		    | "@@" name ("." name "(" [argList] ")")+
		    | setBagExpr (UNION | INTERSECT | MINUS) setBagExpr
		    | "(" argList ")"
		    | "(" setBagExpr ")"

Set/Bag Expression Operators - UNION, INTERSECT, MINUS

The operators are straightforward, when two operands are both sets, the result expression is a set. When at least one operant is a bag, the result expression is a bag. If one operant is a bag and the other is a set, the operator treats the set operant as a bag containing one of each value.

Set/Bag Operator Examples
# Demonstrate Set & Bag operators
CREATE QUERY setOperatorsEx() FOR GRAPH minimalNet   {
  SetAccum<INT> @@setA, @@setB, @@AunionB, @@AintsctB, @@AminusB;
  BagAccum<INT> @@bagD, @@bagE, @@DunionE, @@DintsctE, @@DminusE;
  BagAccum<INT> @@DminusA, @@DunionA, @@AunionBbag;

  BOOL x;

  @@setA = (1,2,3,4);      PRINT @@setA;
  @@setB = (2,4,6,8);      PRINT @@setB;

  @@AunionB = @@setA UNION @@setB ;      PRINT @@AunionB;   // (1, 2, 3, 4, 6, 8)
  @@AintsctB = @@setA INTERSECT @@setB;  PRINT @@AintsctB;   // (2, 4)
  @@AminusB = @@setA MINUS @@setB ;      PRINT @@AminusB;   // C = (1, 3)

  @@bagD = (1,2,2,3);      PRINT @@bagD;
  @@bagE = (2,3,5,7);      PRINT @@bagE;

  @@DunionE = @@bagD UNION @@bagE;     PRINT @@DunionE;   // (1, 2, 2, 2, 3, 3, 5, 7)
  @@DintsctE = @@bagD INTERSECT @@bagE; PRINT @@DintsctE; // (2, 3)
  @@DminusE = @@bagD MINUS @@bagE;     PRINT @@DminusE;   // (1, 2)
  @@DminusA = @@bagD MINUS @@setA;     PRINT @@DminusA;   // (2)
  @@DunionA = @@bagD UNION @@setA;     PRINT @@DunionA;   // (1, 1, 2, 2, 2, 3, 3, 4)
                                                          // because bag UNION set is a bag
  @@AunionBbag = @@setA UNION @@setB;  PRINT @@AunionBbag;  // (1, 2, 3, 4, 6, 8)
                                                          // because set UNION set is a set
}
setOperatorsEx Query Results
GSQL > RUN QUERY setOperatorsEx()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [ {"@@setA": [ 4, 3, 2, 1 ]},
    {"@@setB": [ 8, 6, 4, 2 ]},
    {"@@AunionB": [ 4, 3, 2, 1, 8, 6 ]},
    {"@@AintsctB": [ 4, 2 ]},
    {"@@AminusB": [ 3, 1 ]},
    {"@@bagD": [ 1, 2, 2, 3 ]},
    {"@@bagE": [ 2, 7, 3, 5 ]},
    {"@@DunionE": [ 1, 2, 2, 2, 3, 3, 7, 5 ]},
    {"@@DintsctE": [ 2, 3 ]},
    {"@@DminusE": [ 1, 2 ]},
    {"@@DminusA": [2]},
    {"@@DunionA": [ 1, 1, 2, 2, 2, 3, 3, 4 ]},
    {"@@AunionBbag": [ 6, 8, 1, 2, 3, 4 ]}
  ]
}

The result of these operators is another set or bag, so these operations can be nested and chained to form more complex expressions, such as

(setBagExpr_A INTERSECT (setBagExpr_B UNION setBagExpr_C) ) MINUS setBagExpr_D

Set/Bag Expression Membership Operators

For example , suppose setBagExpr_A is ("a", "b", "c")

"a" IN setBagExpr_A            => true
"d" IN setBagExpr_A            => false
"a" NOT IN setBagExpr_A        => false
"d" NOT IN setBagExpr_A        => true

The IN and NOT IN operators support all base types on the left-hand side, and any set/bag expression on the right-hand side. The base type must be the same as the accumulator's element type. IN and NOT IN return a BOOL value.

The following example uses NOT IN to exclude neighbors that are on a blocked list.

Set Membership example
CREATE QUERY friendsNotInblockedlist (VERTEX<person> seed, SET<VERTEX<person>> blockedList) FOR GRAPH socialNet `{
  Start = {seed};
  Result = SELECT v
      FROM Start:s-(friend:e)-person:v
      WHERE v NOT IN blockedList;
  PRINT Result;
}
Results for Query friendsNotInblockedlist
GSQL > RUN QUERY friendsNotInblockedlist("person1", ["person2"])
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"Result": [{
    "v_id": "person8",
    "attributes": {
      "gender": "Male",
      "id": "person8"
    },
    "v_type": "person"
  }]}]
}

Aggregation Functions - COUNT, SUM, MIN, MAX, AVG

The aggregation functions take a set/bag expression as its input parameter and return one value or element.

  • count() : Returns the size (INT) of the set.

  • sum() : Returns the sum of all elements. This is only applicable to a set/bag expression with numeric type.

  • min() : Returns the member with minimum value. This is only applicable to a set/bag expression with numeric type.

  • max() : Returns the member with maximum value. This is only applicable to a set/bag expression with numeric type.

  • avg() : Returns the average of all elements. This is only applicable to a set/bag expression with numeric type. The average is INT if the element type of the set/bag expression is INT.

Aggregation function example
CREATE QUERY aggregateFuncEx(BAG<INT> x) FOR GRAPH minimalNet   {
  BagAccum<INT> @@t;
  @@t += -5; @@t += 2; @@t+= -1;
  PRINT max(@@t), min(@@t), avg(@@t), count(@@t), sum(@@t);
  PRINT max(x), min(x), avg(x), count(x), sum(x);
}
aggregateFuncEx.json Result
GSQL > RUN QUERY aggregateFuncEx([1,2,5])
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {
      "sum(@@t)": -4,
      "count(@@t)": 3,
      "max(@@t)": 2,
      "avg(@@t)": -1,
      "min(@@t)": -5
    },
    {
      "avg(x)": 2,
      "count(x)": 3,
      "max(x)": 5,
      "min(x)": 1,
      "sum(x)": 8
    }
  ]
}

Miscellaneous Functions

SelectVertex()

SelectVertex() reads a data file which lists particular vertices of the graph and returns the corresponding vertex set. This function can only be used in a vertex set variable declaration statement as a seed set. The data file must be organized as a table with one or more columns. One column must be for vertex id. Optionally, another column is for vertex type. SelectVertex() has five parameters explained in the below table: filePath, vertexIdColumn, vertexTypeColumn, separator, and header. The rules for column separators and column headings are the same as for the GSQL Loader.

parameter name

type

description

filePath

string

The absolute file path of the input file to be read. A relative path is not supported.

vertexIdColumn

$ num , or $ "column_name" if header is true.

The vertex id column position.

vertexTypeColumn

$ num , $ "column_name" if header is true, or a vertex type

The vertex type column position or a specific vertex type.

separator

single-character string

The column separator character.

header

bool

Whether this file has a header.

One vertex set variable declaration statement can have multiple SelectVertex() function calls. However, if a declaration statement has multiple SelectVertex() calls referring to the same file, they must use the same separator and header parameters. If any row of the file contains an invalid vertex type, a run time error occurs; if any row of the file contains an nonexistent vertex id, a warning message is shown with the count of nonexistent ids.

Below is a query example using SelectVertex calls, reading from the data file selectVertexInput.csv.

selectVertexInput.csv
c1,c2,c3
person1,person,3
person5,person,4
person6,person,5
selectVertex example
CREATE QUERY selectVertexEx(STRING filename) FOR GRAPH socialNet {
  S = {SelectVertex(filename, $"c1", $1, ",", true),
       SelectVertex(filename, $2, post, ",", true)
  };
  PRINT S;
}
Result
GSQL > RUN QUERY selectVertexEx("/file_directory/selectVertexInput.csv")
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"S": [
    {
      "v_id": "4",
      "attributes": {
        "postTime": "2011-02-07 05:02:51",
        "subject": "coffee"
      },
      "v_type": "post"
    },
    {
      "v_id": "person1",
      "attributes": {
        "gender": "Male",
        "id": "person1"
      },
      "v_type": "person"
    },
    {
      "v_id": "person5",
      "attributes": {
        "gender": "Female",
        "id": "person5"
      },
      "v_type": "person"
    },
    {
      "v_id": "3",
      "attributes": {
        "postTime": "2011-02-05 01:02:44",
        "subject": "cats"
      },
      "v_type": "post"
    },
    {
      "v_id": "5",
      "attributes": {
        "postTime": "2011-02-06 01:02:02",
        "subject": "tigergraph"
      },
      "v_type": "post"
    },
    {
      "v_id": "person6",
      "attributes": {
        "gender": "Male",
        "id": "person6"
      },
      "v_type": "person"
    }
  ]}]
}

to_vertex() and to_vertex_set()

to_vertex() and to_vertex_set() convert a string or a string set into a vertex or a vertex set, respectively, of a given vertex type. These two functions are useful when the vertex id(s) are obtained and only known at run-time.

Running these functions requires real-time conversion of an external id to a GSQL internal id, which is a relatively slow process. Therefore,

  1. If the user can always know the id before running the query, define the query with VERTEX or SET<VERTEX> parameters instead of STRING or SET<STRING> parameters, and avoid calling to_vertex() or to_vertex_set().

  2. Calling to_vertex_set() one time is much faster than c alling to_vertex() multiple times . Use to_vertex_set() instead of to_vertex() as much as possible.

The first parameter of to_vertex() is the vertex id string. The first parameter of to_vertex_set() is a string set representing vertex ids. The second parameter of both functions is the vertex type string.

Function signatures for to_vertex() and to_vertex_set()
 VERTEX to_vertex(STRING id, STRING vertex_type)
SET<VERTEX> to_vertex_set(SET<STRING>, STRING vertex_type)
SET<VERTEX> to_vertex_set(BAG<STRING>, STRING vertex_type)

to_vertex_set can accept a bag of vertices as input, but the function will reduce the bag to a set by eliminating duplicate items.

If the vertex id or the vertex type doesn't exist, to_vertex() will have a run-time error, as shown below. However, to_vertex_set() will have a run-time error only if the vertex type doesn't exist. If one or more vertex ids are nonexistent, to_vertex_set() will display a warning message but will still run, converting all valid ids and skipping nonexistent vertex ids. If the user wants an error instead of a warning if a nonexistent id is given when converting a string set to a vertex set, the user can use to_vertex() inside a FOREACH loop, instead of to_vertex_set(). See the example below .

to_vertex() and to_vertex_set() example
CREATE QUERY to_vertex_setTest (SET<STRING> uids, STRING uid, STRING vtype) FOR GRAPH workNet {
  SetAccum<VERTEX> @@v2, @@v3;
  SetAccum<STRING> @@strSet;
  VERTEX v;
 
  v = to_vertex (uid, vtype);			# to_vertex assigned to a vertex variable
  PRINT v;								# vertex variable -> only vertex id is printed
 
  @@v2 += to_vertex (uid, vtype);	# to_vertex accumulated to a SetAccum<VERTEX>
  PRINT @@v2;							# SetAccum of vertex -> only vertex ids are printed

  S2 = to_vertex_set (uids, vtype); # to_vertex_set assigned to a vertex set variable
  PRINT S2;								# vertex set variable-> full details printed
  
  @@strSet = uids;						# Show SET<STRING> & SetAccumm<STRING> are the same
  S3 = to_vertex_set(@@strSet, vtype); # Input to to_vertex_set is SetAccum<STRING>
  SDIFF = S2 MINUS S3;					# Now S2 = S3, so SDIFF2 is empty
  PRINT  SDIFF.size();
	
  #FOREACH vid in uids DO    # In this case non-existing ids in uids causes run-time error
  #  @@v3 += to_vertex( vid, vtype );
  #END;
  #L3 = @@v3;
  #PRINT L3;
}
to_vertex_set.json Results
GSQL > RUN QUERY to_vertex_setTest(["person1","personx","person2"], "person3", "person")
{
  "error": false,
  "message": "Runtime Warning: 1 ids are invalid person vertex ids.",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {"v": "person3"},
    {"@@v2": ["person3"]},
    {"S2": [
      {
        "v_id": "person1",
        "attributes": {
          "interestList": [ "management", "financial" ],
          "skillSet": [ 3, 2, 1 ],
          "skillList": [ 1, 2, 3 ],
          "locationId": "us",
          "interestSet": ["financial", "management" ],
          "id": "person1"
        },
        "v_type": "person"
      },
      {
        "v_id": "person2",
        "attributes": {
          "interestList": ["engineering"],
          "skillSet": [ 6, 5, 3, 2 ],
          "skillList": [ 2, 3, 5, 6],
          "locationId": "chn",
          "interestSet": ["engineering"],
          "id": "person2"
        },
        "v_type": "person"
      }
    ]},
    {"SDIFF.size()": 0}
  ]
}


GSQL > RUN QUERY to_vertex_setTest(["person1","personx"], "person1", "abc")
Runtime Error: abc is not valid vertex type.

getvid()

The getvid(v) function returns the internal ID number of the given vertex v. The internal ID is not the primary_id which the user assigned when creating the vertex. However, there is a 1-to-1 mapping between the external ID (primary_id) and internal ID. The engine can access the internal ID faster than accessing the external ID, so if a query needs unique values for a large number of vertices, but doesn't care about particular values, getvid() can be a useful option.

For example, in many community detection algorithms, we start by assigning every vertex a unique community ID. Then, as the algorithm progresses, some vertices will join the community of one of their neighbors, giving up their current community ID and copying the IDs of their neighbors.

getvid_ex.gsql
CREATE QUERY getvid_ex () FOR GRAPH socialNet {
  MinAccum<int> @cc_id = 0;       //each vertex's tentative component id

  Start = {person.*};
  # Initialize: Label each vertex with its own internal ID
  S = SELECT x FROM Start:x
      POST-ACCUM 
         x.@cc_id = getvid(x);
         
  # Community detection steps omitted
  PRINT Start.@cc_id;
} 
getvid_ex.json
GSQL > RUN QUERY getvid_ex()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"Start": [
    {
      "v_id": "person7",
      "attributes": {"Start.@cc_id": 0},
      "v_type": "person"
    },
    {
      "v_id": "person5",
      "attributes": {"Start.@cc_id": 4194304},
      "v_type": "person"
    },
    {
      "v_id": "person1",
      "attributes": {"Start.@cc_id": 4194305},
      "v_type": "person"
    },
    {
      "v_id": "person4",
      "attributes": {"Start.@cc_id": 11534336},
      "v_type": "person"
    },
    {
      "v_id": "person2",
      "attributes": {"Start.@cc_id": 13631488},
      "v_type": "person"
    },
    {
      "v_id": "person3",
      "attributes": {"Start.@cc_id": 20971520},
      "v_type": "person"
    },
    {
      "v_id": "person8",
      "attributes": {"Start.@cc_id": 22020096},
      "v_type": "person"
    },
    {
      "v_id": "person6",
      "attributes": {"Start.@cc_id": 24117248},
      "v_type": "person"
    }
  ]}]
}

COALESCE()

The COALESCE function evaluates each argument value in order, and returns the first value which is not NULL. This evaluation is the same as that used for IS NULL and IS NOT NULL. The COALESCE function requires all its arguments have the same data type (BOOL, INT, FLOAT, DOUBLE, STRING, or VERTEX). The only exception is that different numeric types can be used together. In this case, all values are converted into the first argument type.

coalesce function example
CREATE QUERY coalesceFuncEx (INT p1, DOUBLE p2) FOR GRAPH minimalNet {
  PRINT COALESCE(p1, p2, 999.5);  # p2 and the last value will be converted into first argument type, which is INT.
}
coalesceFuncEx.json Results
GSQL > RUN QUERY coalesceFuncEx(_,_)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"coalesce(p1,p2,999.5)": 999}]
}
GSQL > RUN QUERY coalesceFuncEx(1,2)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"coalesce(p1,p2,999.5)": 1}]
}
GSQL > RUN QUERY coalesceFuncEx(_,2.5)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"coalesce(p1,p2,999.5)": 2}]
}

The COALESCE function is useful when multiple optional parameters are allowed, and one of them must be chosen if available. For example,

coalesce function example
CREATE QUERY coalesceFuncEx2 (STRING homePhone, STRING cellPhone, STRING companyPhone) FOR GRAPH minimalNet {
  PRINT "contact number: " + COALESCE(homePhone, cellPhone, companyPhone); # test all NULL
  PRINT "contact number: " + COALESCE(homePhone, cellPhone, companyPhone, "N/A");  
}

The COALESCE function's parameter list should have a default value as the last argument. Otherwise, i f all values are NULL, the default value of the data type is returned.

coalesceFuncEx2.json Results
GSQL > RUN QUERY coalesceFuncEx2(_,_,_)
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {"contact number: +coalesce(homePhone,cellPhone,companyPhone)": "contact number: "},
    {"contact number:+coalesce(homePhone,cellPhone,companyPhone,N/A)": "contact number:N/A"}
  ]
}

Dynamic Expressions with EVALUATE()

The function evaluate() takes a string argument and interprets it as an expression which is evaluated during run-time. This enables users to create a general purpose query instead of separate queries for each specific computation.

evaluate(expressionStr, typeStr)

The evaluate() function has two parameters: expressionStr is the expression string, and typeStr is a string literal indicating the type of expression. This function returns a value whose type is typeStr and whose value is the evaluation of expressionStr. The following rules apply:

  1. evaluate() can only be used inside a SELECT block, and only inside a WHERE clause, ACCUM clause, POST-ACCUM clause, HAVING clause, or ORDER BY clause. It cannot be used in a LIMIT clause or outside a SELECT block.

  2. The result type must be specified at query installation time: typeStr must be a string literal for a primitive data type, e.g., one of "int", "float", "double", "bool", "string" (case insensitive). The default value is "bool".

  3. In expressionStr, identifiers can refer only to a vertex or edge aliases, vertex-attached accumulators, global accumulators, parameters, or scalar function calls involving the above variables. The expression may not refer to local variables, global variables, or to FROM clause vertices or edges by type.

  4. Any accumulators in the expression must be scalar accumulators (e.g., MaxAccum) for primitive-type data. Container accumulators (e.g., SetAccum) or scalar accumulators with non-primitive type (e.g. VERTEX, EDGE, DATETIME) are not supported. Container type attributes are not supported.

  5. evaluate() cannot be nested.

The following situations generate a run-time error:

  1. The expression string expressionStr cannot be compiled (unless the error is due to a non-existent vertex or edge attribute).

  2. The result type of the expression does not match the parameter typeStr.

Silent failure conditions

If any of the following conditions occur, the query may continue running, but the entire clause or statement in which the evaluate() function resides will fail, without producing a run-time error message. For conditional clauses (WHERE, HAVING), a failing evaluate() clause is treated as if the condition is false. An assignment statement with a failing evaluate() will not execute, and an ORDER BY clause with a failing evaluate() will not sort.

  1. The expression references a non-existent attribute of a vertex or edge alias.

  2. The expression uses an operator for non-compatible operation. For example, 123 == "xyz".

The following example employs dynamic expressions in both the WHERE condition and the accumulator value in the POST-ACCUM clause.

Evaluate example
CREATE QUERY evaluateEx (STRING whereCond = "TRUE", STRING postAccumIntExpr = "1") FOR GRAPH socialNet {
  SetAccum<INT> @@timeSet;
  MaxAccum<INT> @latestLikeTime, @latestLikePostTime;

  S = {person.*};
  S2 = SELECT s 
       FROM S:s - (liked:e) -> post:t
       WHERE evaluate(whereCond)
       ACCUM s.@latestLikeTime += datetime_to_epoch( e.actionTime ),
             s.@latestLikePostTime += datetime_to_epoch( t.postTime )
       POST-ACCUM @@timeSet += evaluate(postAccumIntExpr, "int")
       ;
  PRINT @@timeSet;
}
Results for Query evaluateEx
GSQL > RUN QUERY evaluateEx(_,_)
{
  "error": false,
  "message": "",
  "results": [{"@@timeSet": [1]}]
}

GSQL > RUN QUERY evaluateEx("s.gender==\"Male\"", "s.@latestLikePostTime")
{
  "error": false,
  "message": "",
  "results": [
    {
      "@@timeSet": [1263295325,1296752752,1297054971,1296788551]
    }
  ]
}

GSQL > RUN QUERY evaluateEx("s.gender==\"Female\"", "s.@latestLikeTime + 1")
{
  "error": false,
  "message": "",
  "results": [
    {
      "@@timeSet": [1263293536,1263352566,1263330726]
    }
  ]
}


GSQL > RUN QUERY evaluateEx("xx", _)
Runtime Error: xx is undefined parameter.


GSQL > RUN QUERY evaluateEx("e.xyz", _)'   # The attribute doesn't exist, so the entire condition in WHERE clause is false.  
{
  "error": false,
  "message": "",
  "results": [{"@@timeSet": []}]
}


GSQL > RUN QUERY evaluateEx("e.actionTime", _) 
Runtime Error: actionTime is not a primitive type attribute.

GSQL > RUN QUERY evaluateEx("s.id", _)
Runtime Error: Expression 's.id' value type is not bool.


GSQL > RUN QUERY evaluateEx("s.gender==\"Female\"", "s.xx")   # The attribute doesn't exist, so the entire assignment is skipped. 
{
  "error": false,
  "message": "",
  "results": [{"@@timeSet": []}]
}

Queries as Functions

A query that has been defined (with a CREATE QUERY ... RETURNS statement) can be treated as a callable function. A query can call itself recursively.

The following limitations apply to queries calling queries:

  1. Each parameter of the called query may be one of the following types:

    1. Primitives: INT, UINT, FLOAT, DOUBLE, STRING, BOOL

    2. VERTEX

    3. A Set or Bag of primitive or VERTEX elements

  2. The return value may be one of the following types. See also the "Return Statement" section.

    1. Primitives: INT, UINT, FLOAT, DOUBLE, STRING, BOOL

    2. VERTEX

    3. a vertex set (e.g., the result of a SELECT statement)

    4. An accumulator of primitive types. GroupByAccum and accumulators containing tuples are not supported.

  3. A query which returns a SetAccum or BagAccum may be called with a Set or Bag argument, respectively.

  4. The order of definition matters. A query cannot call a query which has not yet been defined.

    However, recursive queries are supported.

Recursive Query example: Given a set of persons as starting points, find all the friends within a given distance. Note: while recursive queries may look simpler to write, GSQL parallel processing and flow control usually makes iterative queries more efficient than recursive queries.

CREATE QUERY subFindFriendsInDistance(SET<VERTEX> seeds, INT distance)
FOR GRAPH friendNet RETURNS (SetAccum<VERTEX>)
{
	  SetAccum<VERTEX> @@ans;

	IF distance <= 0 THEN
	  RETURN @@ans;
	ELSE
	  seed_vs = seeds;
	  next_vs = SELECT v FROM seed_vs -(friend:e)- :v;

	  RETURN seeds UNION subFindFriendsInDistance(next_vs, distance - 1);
  END;
}

CREATE QUERY findFriendsInDistance(Vertex<person> p, INT distance) FOR GRAPH friendNet { 
  
	seed = {p};
  //PRINT AllPersons;
	PRINT subFindFriendsInDistance( seed, distance) AS friends;
}

Test cases: Starting from person1, search to a depth of 2 and a depth of 3.

GSQL> RUN QUERY findFriendsInDistance("person1", 2)
[
  {
    "friends": [
      "person4",
      "person3",
      "person2",
      "person1"
    ]
  }
]
GSQL> RUN QUERY findFriendsInDistance("person1", 3)
[
  {
    "friends": [
      "person4",
      "person9",
      "person3",
      "person2",
      "person6",
      "person8",
      "person1"
    ]
  }
]

User-Defined Functions

Users can define their own expression functions in C++ in <tigergraph.root.dir>/dev/gdk/gsql/src/QueryUdf/ExprFunctions.hpp. Only bool, int, float, double, and string (NOT std::string) are allowed as the return value type and the function argument type. However, any C++ type is allowed inside a function body. Once defined, the new functions will be added into GSQL automatically next time GSQL is executed.

If a user-defined struct or a helper function needs to be defined, define it in <tigergraph.root.dir>/dev/gdk/gsql/src/QueryUdf/ExprUtil.hpp.

Here is an example:

new code in ExprFunction.hpp
#include <algorithm>  // for std::reverse
inline bool greater_than_three (double x) {
  return x > 3;
}
inline string reverse(string str){
  std::reverse(str.begin(), str.end());
  return str;
}
user defined expression function
CREATE QUERY udfExample() FOR GRAPH minimalNet {
  DOUBLE x;
  BOOL y;

  x = 3.5;
  PRINT greater_than_three(x);
  y = greater_than_three(2.5);
  PRINT y;

  PRINT reverse("abc");
}
udfExample.json Results
 GSQL > RUN QUERY udfExample()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {"greater_than_three(x)": true},
    {"y": false},
    {"reverse(abc)": "cba"}
  ]
}

If any code in ExprFunctions.hpp or ExprUtil.hpp causes a compilation error, GSQL cannot install any GSQL query, even if the GSQL query doesn't call any user-defined function. Therefore, please test each new user-defined expression function after adding it. One way of testing the function is creating a new cpp file test.cpp and compiling it by > g++ test.cpp > ./a.out You might need to remove the include header #include <gle/engine/cpplib/headers.hpp> in ExprFunction.hpp and ExprUtil.hpp in order to compile.

test.cpp
#include "ExprFunctions.hpp"
#include <iostream>
int main () {
  std::cout << to_string (123) << std::endl;    // to_string and str_to_int are two built-in functions in ExprFunction.hpp
  std::cout << str_to_int ("123") << std::endl;
  return 0;
}

Examples of Expressions

Below is a list of examples of expressions. Note that ( argList ) is a set/bag expression, while [ argList ] is a list expression.

Expression Examples
#Show various types of expressions
CREATE QUERY expressionEx() FOR GRAPH workNet {
  TYPEDEF tuple<STRING countryName, STRING companyName> companyInfo;

  ListAccum<STRING> @companyNames;
  SumAccum<INT> @companyCount;
  SumAccum<INT> @numberOfRelationships;
  ListAccum<companyInfo> @info;
  MapAccum< STRING,ListAccum<STRING> > @@companyEmployeeRelationships;
  SumAccum<INT> @@totalRelationshipCount;

  ListAccum<INT> @@valueList;
  SetAccum<INT> @@valueSet;

  SumAccum<INT> @@a;
  SumAccum<INT> @@b;

  #expr := constant
  @@a = 10;

  #expr := ["@@"] name
  @@b = @@a;

  #expr := expr mathOperator expr
  @@b = @@a + 5;

  #expr := "(" expr ")"
  @@b = (@@a + 5);

  #expr := "-" expr
  @@b = -(@@a + 5);

  PRINT @@a, @@b;

  #expr := "[" argList "]"   // a list
  @@valueList = [1,2,3,4,5];
  @@valueList += [24,80];

  #expr := "(" argList ")"  // setBagExpr
  @@valueSet += (1,2,3,4,5);

  #expr := ( COUNT | ISEMPTY | MAX | MIN | AVG | SUM ) "(" setBagExpr ")"
  PRINT MAX(@@valueList);
  PRINT AVG(@@valueList);

  seed = {ANY};

  company1 = SELECT t FROM seed:s -(worksFor)-> :t WHERE (s.id == "company1");
  company2 = SELECT t FROM seed:s -(worksFor)-> :t WHERE (s.id == "company2");

  #expr := setBagExpr
  worksForBoth = company1 INTERSECT company2;
  PRINT worksForBoth;

  #expr := name "." "type"
  employees = SELECT s FROM seed:s WHERE (s.type == "person");

  employees = SELECT s FROM employees:s -(worksFor)-> :t

    ACCUM
      #expr := name "." ["@"] name
      s.@companyNames += t.id,

      #expr := name "." name "(" [argList] ")" [ ".".FILTER "(" condition ")" ]
      s.@numberOfRelationships += s.outdegree(),

      #expr := name ["<" type ["," type"]* ">"] "(" [argList] ")"
      s.@info += companyInfo(t.country, t.id)

   POST-ACCUM
     #expr := name "." "@" name ("." name "(" [argList] ")")+ ["." name]
     s.@companyCount += s.@companyNames.size(),

    #expr := name "." "@" name ["\'"]
    @@totalRelationshipCount += s.@companyCount,

  FOREACH comp IN s.@companyNames DO
     #expr := "(" argList "->" argList ")"
     @@companyEmployeeRelationships += (s.id -> comp)
  END;

  PRINT employees;
  PRINT @@totalRelationshipCount;
  PRINT @@companyEmployeeRelationships;

  #expr := "@@" name ("." name "(" [argList] ")")+ ["." name]
  PRINT @@companyEmployeeRelationships.size();
}
expressionEx.json Results
GSQL > RUN QUERY expressionEx()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {
      "@@a": 10,
      "@@b": -15
    },
    {"max(@@valueList)": 80},
    {"avg(@@valueList)": 17},
    {"worksForBoth": [
      {
        "v_id": "person2",
        "attributes": {
          "interestList": ["engineering"],
          "@companyCount": 0,
          "@numberOfRelationships": 0,
          "skillSet": [ 6, 5, 3, 2 ],
          "skillList": [ 2, 3, 5, 6 ],
          "locationId": "chn",
          "interestSet": ["engineering"],
          "@info": [],
          "id": "person2",
          "@companyNames": []
        },
        "v_type": "person"
      },
      {
        "v_id": "person1",
        "attributes": {
          "interestList": [ "management", "financial" ],
          "@companyCount": 0,
          "@numberOfRelationships": 0,
          "skillSet": [ 3, 2, 1 ],
          "skillList": [ 1, 2, 3 ],
          "locationId": "us",
          "interestSet": [ "financial", "management" ],
          "@info": [],
          "id": "person1",
          "@companyNames": []
        },
        "v_type": "person"
      }
    ]},
    {"employees": [
      {
        "v_id": "person4",
        "attributes": {
          "interestList": ["football"],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [ 10, 1, 4 ],
          "skillList": [ 4, 1, 10 ],
          "locationId": "us",
          "interestSet": ["football"],
          "@info": [{ "companyName": "company2", "countryName": "chn" }],
          "id": "person4",
          "@companyNames": ["company2"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person12",
        "attributes": {
          "interestList": [
            "music",
            "engineering",
            "teaching",
            "teaching",
            "teaching"
          ],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [ 2, 5, 1 ],
          "skillList": [ 1, 5, 2, 2, 2 ],
          "locationId": "jp",
          "interestSet": [ "teaching", "engineering", "music" ],
          "@info": [{ "companyName": "company4", "countryName": "us" }],
          "id": "person12",
          "@companyNames": ["company4"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person3",
        "attributes": {
          "interestList": ["teaching"],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [ 6, 1, 4 ],
          "skillList": [ 4, 1, 6 ],
          "locationId": "jp",
          "interestSet": ["teaching"],
          "@info": [{ "companyName": "company1", "countryName": "us" }],
          "id": "person3",
          "@companyNames": ["company1"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person9",
        "attributes": {
          "interestList": [ "financial", "teaching" ],
          "@companyCount": 2,
          "@numberOfRelationships": 4,
          "skillSet": [ 2, 7, 4 ],
          "skillList": [ 4, 7, 2 ],
          "locationId": "us",
          "interestSet": [ "teaching", "financial" ],
          "@info": [
            {
              "companyName": "company3",
              "countryName": "jp"
            },
            {
              "companyName": "company2",
              "countryName": "chn"
            }
          ],
          "id": "person9",
          "@companyNames": [ "company3", "company2" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person11",
        "attributes": {
          "interestList": [ "sport", "football" ],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [10],
          "skillList": [10],
          "locationId": "can",
          "interestSet": [ "football", "sport" ],
          "@info": [{ "companyName": "company5", "countryName": "can" }],
          "id": "person11",
          "@companyNames": ["company5"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person10",
        "attributes": {
          "interestList": [ "football", "sport" ],
          "@companyCount": 2,
          "@numberOfRelationships": 4,
          "skillSet": [3],
          "skillList": [3],
          "locationId": "us",
          "interestSet": [ "sport", "football" ],
          "@info": [
            {
              "companyName": "company3",
              "countryName": "jp"
            },
            {
              "companyName": "company1",
              "countryName": "us"
            }
          ],
          "id": "person10",
          "@companyNames": [ "company3", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person7",
        "attributes": {
          "interestList": [ "art", "sport" ],
          "@companyCount": 2,
          "@numberOfRelationships": 4,
          "skillSet": [ 6, 8 ],
          "skillList": [ 8, 6 ],
          "locationId": "us",
          "interestSet": [ "sport", "art" ],
          "@info": [
            {
              "companyName": "company3",
              "countryName": "jp"
            },
            {
              "companyName": "company2",
              "countryName": "chn"
            }
          ],
          "id": "person7",
          "@companyNames": [ "company3", "company2" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person1",
        "attributes": {
          "interestList": [ "management", "financial" ],
          "@companyCount": 2,
          "@numberOfRelationships": 4,
          "skillSet": [ 3, 2, 1 ],
          "skillList": [ 1, 2, 3 ],
          "locationId": "us",
          "interestSet": [ "financial", "management" ],
          "@info": [
            {
              "companyName": "company2",
              "countryName": "chn"
            },
            {
              "companyName": "company1",
              "countryName": "us"
            }
          ],
          "id": "person1",
          "@companyNames": [ "company2", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person5",
        "attributes": {
          "interestList": [ "sport", "financial", "engineering" ],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [ 5, 2, 8 ],
          "skillList": [ 8, 2, 5 ],
          "locationId": "can",
          "interestSet": [ "engineering", "financial", "sport" ],
          "@info": [{ "companyName": "company2", "countryName": "chn" }],
          "id": "person5",
          "@companyNames": ["company2"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person6",
        "attributes": {
          "interestList": [ "music", "art" ],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [ 10, 7 ],
          "skillList": [ 7, 10 ],
          "locationId": "jp",
          "interestSet": [ "art", "music" ],
          "@info": [{ "companyName": "company1", "countryName": "us" }],
          "id": "person6",
          "@companyNames": ["company1"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person2",
        "attributes": {
          "interestList": ["engineering"],
          "@companyCount": 2,
          "@numberOfRelationships": 4,
          "skillSet": [ 6, 5, 3, 2 ],
          "skillList": [ 2, 3, 5, 6 ],
          "locationId": "chn",
          "interestSet": ["engineering"],
          "@info": [
            {
              "companyName": "company2",
              "countryName": "chn"
            },
            {
              "companyName": "company1",
              "countryName": "us"
            }
          ],
          "id": "person2",
          "@companyNames": [ "company2", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person8",
        "attributes": {
          "interestList": ["management"],
          "@companyCount": 1,
          "@numberOfRelationships": 1,
          "skillSet": [ 2, 5, 1 ],
          "skillList": [ 1, 5, 2 ],
          "locationId": "chn",
          "interestSet": ["management"],
          "@info": [{ "companyName": "company1", "countryName": "us" }],
          "id": "person8",
          "@companyNames": ["company1"]
        },
        "v_type": "person"
      }
    ]},
    {"@@totalRelationshipCount": 17},
    {"@@companyEmployeeRelationships": {
      "person4": ["company2"],
      "person3": ["company1"],
      "person2": [ "company2", "company1" ],
      "person1": [ "company2", "company1" ],
      "person9": [ "company3", "company2" ],
      "person12": ["company4"],
      "person8": ["company1"],
      "person7": [ "company3", "company2" ],
      "person6": ["company1"],
      "person10": [ "company3", "company1" ],
      "person5": ["company2"],
      "person11": ["company5"]
    }},
    {"@@companyEmployeeRelationships.size()": 12}
  ]
}

Examples of Expression Statements

Expression Statement Examples
#Show various types of expression statements
CREATE QUERY expressionStmntEx() FOR GRAPH workNet { 
  TYPEDEF tuple<STRING countryName, STRING companyName> companyInfo;

  ListAccum<companyInfo> @employerInfo;
  SumAccum<INT> @@a;
  ListAccum<STRING> @employers;
  SumAccum<INT> @employerCount;
  SetAccum<STRING> @@countrySet;

  int x;

  #exprStmnt := name "=" expr
  x = 10;

  #gAccumAssignStmt := "@@" name ("+=" | "=") expr
  @@a = 10;

  PRINT x, @@a;

  start = {person.*};

  employees = SELECT s FROM start:s -(worksFor)-> :t              
  	          ACCUM #exprStmnt := name "." "@" name ("+="| "=") expr
                    s.@employers += t.id,
       		        #exprStmnt := name ["<" type ["," type"]* ">"] "(" [argList] ")"
		            s.@employerInfo += companyInfo(t.country, t.id),
                    #gAccumAccumStmt := "@@" name "+=" expr
		            @@countrySet += t.country
	                #exprStmnt := name "." "@" name ["." name "(" [argList] ")"]
	          POST-ACCUM s.@employerCount += s.@employers.size();

  #exprStmnt := "@@" name ["." name "(" [argList] ")"]+
  PRINT @@countrySet.size();
  PRINT employees;
}
GSQL > RUN QUERY expressionStmntEx()
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [
    {
      "@@a": 10,
      "x": 10
    },
    {"@@countrySet.size()": 4},
    {"employees": [
      {
        "v_id": "person4",
        "attributes": {
          "interestList": ["football"],
          "skillSet": [ 10, 1, 4 ],
          "skillList": [ 4, 1, 10 ],
          "locationId": "us",
          "@employerInfo": [{
            "companyName": "company2",
            "countryName": "chn"
          }],
          "interestSet": ["football"],
          "@employerCount": 1,
          "id": "person4",
          "@employers": ["company2"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person11",
        "attributes": {
          "interestList": [ "sport", "football" ],
          "skillSet": [10],
          "skillList": [10],
          "locationId": "can",
          "@employerInfo": [{
            "companyName": "company5",
            "countryName": "can"
          }],
          "interestSet": [ "football", "sport" ],
          "@employerCount": 1,
          "id": "person11",
          "@employers": ["company5"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person10",
        "attributes": {
          "interestList": [ "football", "sport" ],
          "skillSet": [3],
          "skillList": [3],
          "locationId": "us",
          "@employerInfo": [
            {
              "companyName": "company3",
              "countryName": "jp"
            },
            {
              "companyName": "company1",
              "countryName": "us"
            }
          ],
          "interestSet": [ "sport", "football" ],
          "@employerCount": 2,
          "id": "person10",
          "@employers": [ "company3", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person7",
        "attributes": {
          "interestList": [ "art", "sport" ],
          "skillSet": [ 6, 8 ],
          "skillList": [ 8, 6 ],
          "locationId": "us",
          "@employerInfo": [
            {
              "companyName": "company3",
              "countryName": "jp"
            },
            {
              "companyName": "company2",
              "countryName": "chn"
            }
          ],
          "interestSet": [ "sport", "art" ],
          "@employerCount": 2,
          "id": "person7",
          "@employers": [ "company3", "company2" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person1",
        "attributes": {
          "interestList": [ "management", "financial" ],
          "skillSet": [ 3, 2, 1 ],
          "skillList": [ 1, 2, 3 ],
          "locationId": "us",
          "@employerInfo": [
            {
              "companyName": "company2",
              "countryName": "chn"
            },
            {
              "companyName": "company1",
              "countryName": "us"
            }
          ],
          "interestSet": [ "financial", "management" ],
          "@employerCount": 2,
          "id": "person1",
          "@employers": [ "company2", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person6",
        "attributes": {
          "interestList": [ "music", "art" ],
          "skillSet": [ 10, 7 ],
          "skillList": [ 7, 10 ],
          "locationId": "jp",
          "@employerInfo": [{ "companyName": "company1", "countryName": "us" }],
          "interestSet": [ "art", "music" ],
          "@employerCount": 1,
          "id": "person6",
          "@employers": ["company1"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person2",
        "attributes": {
          "interestList": ["engineering"],
          "skillSet": [ 6, 5, 3, 2 ],
          "skillList": [ 2, 3, 5, 6 ],
          "locationId": "chn",
          "@employerInfo": [
            {
              "companyName": "company2",
              "countryName": "chn"
            },
            {
              "companyName": "company1",
              "countryName": "us"
            }
          ],
          "interestSet": ["engineering"],
          "@employerCount": 2,
          "id": "person2",
          "@employers": [ "company2", "company1" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person5",
        "attributes": {
          "interestList": [ "sport", "financial", "engineering" ],
          "skillSet": [ 5, 2, 8 ],
          "skillList": [ 8, 2, 5 ],
          "locationId": "can",
          "@employerInfo": [{
            "companyName": "company2",
            "countryName": "chn"
          }],
          "interestSet": [ "engineering", "financial", "sport" ],
          "@employerCount": 1,
          "id": "person5",
          "@employers": ["company2"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person12",
        "attributes": {
          "interestList": [
            "music",
            "engineering",
            "teaching",
            "teaching",
            "teaching"
          ],
          "skillSet": [ 2, 5, 1 ],
          "skillList": [ 1, 5, 2, 2, 2 ],
          "locationId": "jp",
          "@employerInfo": [{ "companyName": "company4", "countryName": "us" }],
          "interestSet": [ "teaching", "engineering", "music" ],
          "@employerCount": 1,
          "id": "person12",
          "@employers": ["company4"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person3",
        "attributes": {
          "interestList": ["teaching"],
          "skillSet": [ 6, 1, 4 ],
          "skillList": [ 4, 1, 6 ],
          "locationId": "jp",
          "@employerInfo": [{ "companyName": "company1", "countryName": "us" }],
          "interestSet": ["teaching"],
          "@employerCount": 1,
          "id": "person3",
          "@employers": ["company1"]
        },
        "v_type": "person"
      },
      {
        "v_id": "person9",
        "attributes": {
          "interestList": [ "financial", "teaching" ],
          "skillSet": [ 2, 7, 4 ],
          "skillList": [ 4, 7, 2 ],
          "locationId": "us",
          "@employerInfo": [
            {
              "companyName": "company3",
              "countryName": "jp"
            },
            {
              "companyName": "company2",
              "countryName": "chn"
            }
          ],
          "interestSet": [ "teaching", "financial" ],
          "@employerCount": 2,
          "id": "person9",
          "@employers": [ "company3", "company2" ]
        },
        "v_type": "person"
      },
      {
        "v_id": "person8",
        "attributes": {
          "interestList": ["management"],
          "skillSet": [ 2, 5, 1 ],
          "skillList": [ 1, 5, 2 ],
          "locationId": "chn",
          "@employerInfo": [{ "companyName": "company1", "countryName": "us" }],
          "interestSet": ["management"],
          "@employerCount": 1,
          "id": "person8",
          "@employers": ["company1"]
        },
        "v_type": "person"
      }
    ]}
  ]
}

Last updated