运算符,函数和表达式

表达式是将常数,变量,运算符,函数调用和分组进行整合后的一种形式。表达式定 义了计算方法,并从而使得运算可以得到结果。 “规范”一节详述了 GSQL 图形查询 语言中可用的文字表述(常数),运算符和函数。 它涵盖了下面的定义。 但是,与 “规范”一章中的其他部分相比,该语法󿰁述本身并不够充分。 特定运算符和函数的 语义(功能)是该语法的必要补充。

运算符、函数和表达式的 EBNF 范式
constant := numeric | stringLiteral | TRUE | FALSE | GSQL_UINT_MAX | GSQL_INT_MAX | GSQL_INT_MIN | TO_DATETIME "(" stringLiteral ")"
mathOperator := "*" | "/" | "%" | "+" | "-" | "<<" | ">>" | "&" | "|"
comparisonOperator := "<" | "<=" | ">" | ">=" | "==" | "!=" condition := expr
expr :=
|
| | | |
condition | |
name]
| 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)
["@@"]name
name "." "type"
name "." ["@"]name
name "." "@"name ["\'"]
name "." name "." name "(" [argList] ")"
name "." name "(" [argList] ")" [ ".".FILTER "(" ")" ]
name ["<" type ["," type"]* ">"] "(" [argList] ")" name "." "@"name ("." name "(" [argList] ")")+ ["."
"@@"name ("." name "(" [argList] ")")+ ["." name] COALESCE "(" [argList] ")"
( COUNT | ISEMPTY | MAX | MIN | AVG | SUM ) "("
| | |
setBagExpr ")"
| expr mathOperator expr
| "-" expr
| "(" expr ")"
| "(" argList "->" argList ")"
| "[" argList "]"
| constant
| setBagExpr
| name "(" argList ")"
setBagExpr := ["@@"]name
| name "." ["@"]name
// key value pair for // a list
MapAccum
| name "." "@"name ("." name "(" [argList] ")")+
| name "." name "(" [argList] ")" [ ".".FILTER "(" condition ")" ]
| "@@"name ("." name "(" [argList] ")")+
| setBagExpr (UNION | INTERSECT | MINUS) setBagExpr | "(" argList ")"
| "(" setBagExpr ")"
argList := expr ["," expr]*

Microsoft Word - [Tran]Part-2---Querying-(FINAL-c)_RV20181120.docx

常数

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

常数的基本数据类型:

数据类型

常量

示例

数字格式(INT, UINT, FLOAT,DOUBLE)

numeric

123 -5 45.67 2.0e-0.5

UINT

GSQL_UINT_MAX

INT

GSQL_INT_MAX GSQL_INT_MIN

布尔值

TRUE FALSE

字符串值

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

运算符

运算符是一种关键词的标记。它将相邻的表达式(其操作数)作为输入值,运行特定的计算并返回结果。 运算符与函数类似,因为它们都根据输入的数据计算结果,但在语法上它们是不同的。 我们最熟悉的运算符是加号和减号。

提示:本节中列出的运算符与MySQL的运算符相似。

数学运算符和表达式

本语言支持以下标准的数学运算符和及其定义。 后四个(“<<”|“>>”|“&”|“|”)用于逐位运算。 详见之后的“比特运算符”一节。

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

下表列出了运算符的优先级,从高到低。 同一行中的运算符具有相同的优先级:

运算符优先级,从高到低
*, /, %
-, +
<<, >>
&
|
==, >=, >, <=, <, !=
例 1. 数学运算符 + - * /
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 结果
GSQL > RUN QUERY mathOperators()
{
 "error": false,
 "message": "",
 "version": {
   "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
   }
 ]
}

布尔运算符

我们支持标准布尔运算符和标准优先顺序:AND,OR,NOT

比特运算符

比特运算符(<<,>>,&和|)对整数进行操作并返回整数。

比特运算符
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
}

字符串运算符

运算符加号(+)可以被用于串联字符串

元组字段

元组的字段可以通过使用点运算符获取

比较运算符和条件

条件是一种表达式,其值为布尔值true或false。各种类型的条件都使用其常用的比较运算符。比较运算符用于比较两个条件值。

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 AND范围表达式

如果值expr1位于expr2~expr3的范围之内,则表达式expr1 BETWEEN expr2 AND expr3的运算结果为true(包含端点值)。 其中每个expr都必须是数字类型。

“expr1 BETWEEN expr2 AND expr3”等效于“expr1 <= expr3 AND expr1> = expr2”。

BETWEEN AND的例子
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和IS NOT NULL可用于检查可选参数是否被赋值。

IS NULL的例子
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 的结果
GSQL > RUN QUERY parameterIsNULL(_)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"p is null": "p is null"}]
}
GSQL > RUN QUERY parameterIsNULL(3)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"p is not null": "p is not null"}]
}

在GSQL中存储的每个属性值都是有效的,因此IS NULL和IS NOT NULL仅用于每个查询操作的参数。

LIKE运算符

LIKE运算符用于检查字符串是否与某个模式匹配。 表达方式为:

string1 LIKE string_pattern

如果string1与string_pattern中的模式相匹配,则返回结果值为布尔值true; 反之为false。 前后两个值必须都是字符串。 LIKE只能在WHERE子句中使用。 此外,string_pattern支持以下通配符和其他符号:

字符或语法

含义

%

与0个或超过1个字符相同

例 : "%abc% " 匹配任何包含字符串 "abc"的字符串.

_ (underscore)

表示任一字符. 例 : "_abc_e" 匹配任何6字节的字符串,其中第2到第4个字符为 "abc" 且最后一个字符为"e".

[charlist]

匹配charlist中的任何字符。 charlist是一个合并字符集且没有分隔符。 例 : "[Tiger]" 匹配五个字符:T, i, g, e, r.

[^charlist]

匹配任一不在charlist中的字符. 例 : "[^qxz]" 匹配除了q, x, z之外的所有字符.

[!charlist]

匹配任一不在charlist中的字符

charlist中的特殊语法

α-β

匹配任何字符,该字符位于α与β之间。一个charlist中可以包含多个不同范围 例: "[a-mA-M0-3]" 匹配从 a到m之间的任一字符, 小写或大写, 或 0到3之间的某个数.

\\

匹配字符\

\\]

匹配字符] 对于charlist中的左方括号[不需要特殊对待. 例: "%[\\]!]" 匹配任意以] 或 ! 结尾的字符串

数学函数

系统包含许多内置函数。他们可以用于累加器,基本类或顶点变量。 累加器函数的调用在“累加器”一章中详述。

下面是一个内置函数列表,它们对INT,FLOAT或DOUBLE类型的值起作用。

函数名及其参数

(NUM 表示 INT, FLOAT或DOUBLE)

描述

返回值类型

abs (NUM num )

返回num的绝对值

与参数类型相同

sqrt(NUM num)

返回num的平方根

FLOAT

pow ( NUM base, NUMexp )

返回指数 base exp

若base和exp都是INT,则输出INT。否则输出FLOAT

acos(NUM num)

反余弦

FLOAT

asin(NUM num)

反正弦

FLOAT

atan(NUM num)

反正切

FLOAT

atan2 (NUM y, NUM x )

y / x的反正切

FLOAT

ceil(NUM num)

向上四舍五入

INT

cos(NUM num)

余弦

FLOAT

cosh(NUM num)

双曲余弦

FLOAT

exp(NUM num)

Base的e 的指数

FLOAT

floor(NUM num)

向下四舍五入

INT

fmod (NUM numer, NUM denom )

numer / denom和浮点余数

FLOAT

ldexp (NUM x, NUMexp )

FLOAT

log(NUM num)

自然对数

FLOAT

log10(NUM num)

常用对数(以10为底)

FLOAT

sin(NUM num)

正弦

FLOAT

sinh(NUM num)

双曲正弦

FLOAT

tan(NUM num)

正切

FLOAT

tanh(NUM num)

双曲正切

FLOAT

to_string (NUM num)

将数字num转换成字符串

STRING

float_to_int (FLOAT num)

将浮点数num转换成整数(剔除浮点部分)

INT

str_to_int (STRING str)

将str转换成整数值。若str为浮点数,则浮点部分被剔除;若str不是一个数字,则返回0.

INT

字符串函数

以下内置函数用于处理文本。 请注意,这些函数不会修改输入的文本,而是输出一个全新的字符串。

函数名及其参数

描述

返回值类型

lower(STRING str )

把str的每个字符转换成小写字母

STRING

upper(STRING str )

把str的每个字符转换成大写字母

STRING

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

剔除str的开头和/或结尾的*个字符

STRING

关于使用trim()函数的注意事项:

  • 在trim()中的粗体字(LEADING,TRAILING,BOTH和FROM)是关键字,不能省略。

  • STRING只表示此处参数的数据类型; 它不是一个关键字。

  • trim()函数具有以下选项:

    • 通过使用LEADING,TRAILING或BOTH之一关键字,用户可以指定分别从字符串的左端,右端或两端删除字符。 如果未使用这些关键字,则默认从两端删除。

    • removal_char是一个单个字符。 该函数将按顺序按照removed_char的内容来删除字符串,直到遇到与之不同的字符。 如果未指定removed_char,则trim()将删除空白字符(包括空格,制表符和换行符)。

日期时间函数

下面的函数将其它类型的值转换成时间日期,或将时间日期转换成其它格式。

函数名及其参数

描述

返回值类型

to_datetime (STRING str )

将str转换为DATETIME值

DATETIME

epoch_to_datetime (INT int_value )

将int_value按照纪元时间方式转换为DATETIME值

DATETIME

datetime_to_epoch (DATETIME date )

将date转换为纪元时间。

INT

以下函数将DATETIME值转换为用户自定义格式:

函数名及其参数

描述

返回值类型

datetime_format( DATETIME date[, STRING str ] )

按照str提供的格式打印出日期。 以下记号可用作编写str的格式。 记号前需要“%”。 如果没有给出str,则默认使用“%Y-%m-%d%H:%M:%S”的格式‘’

  • %Y:年, 四位数字

  • %S:秒,范围为(0~59)

  • %m:月, 数字,范围为(1~12)

  • %M:分钟, 数字,范围为(0~59)

  • %H:小时, 数字,范围为(0~23)

  • %d:当月日期,数字,范围为 (1~31)

STRING

datetime_format的例子
# 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
}

函数名及其参数

描述

返回值类型

now()

DATETIME 类型返回当前时间.

DATETIME

year(DATETIME date)

从date获取年份 .

INT

month(DATETIME date)

从date获取月份 .

INT

day(DATETIME date)

从date获取当月的日期 .

INT

hour(DATETIME date)

从date获取小时 .

INT

minute(DATETIME date)

从date获取分钟 .

INT

second(DATETIME date)

从date获取秒数 .

INT

datetime_add( DATETIME date, INTERVALint_value time_unit )

INTERVAL是一个关键词; time_unit是YEAR,MONTH,DAY,HOUR,MINUTE或SECOND的其中之一。该函数返回一个DATETIME值,该值是date之后int_value个时间单位的时间值。 例如,datetime_add(now(),INTERVAL 1 MONTH)从现在开始1个月后的DATETIME值。

DATETIME

datetime_sub( DATETIME date, INTERVAL int_value time_unit )

与datetime_add类似, 区别是返回早于date的int_value个时间单位的DATETIME值。

DATETIME

datetime_diff( DATETIME date1, DATETIMEdate2)

返回date1与date2的时间间隔,单位为秒

INT

JSONOBJECT 和JSONARRAY 函数

JSONOBJECT和JSONARRAY是基本类,这意味着它们可以是参数类型,或大多数累加器的元素类型,或一个返回值类型。 这使得用户能够输入和输出更加复杂的自定义数据结构。 对于输入和输出来说,我们使用JSON格式的字符串表达式。 因此,GSQL图形查询语言提供几个函数,从而可以将标准格式的字符串转换为JSON,然后搜索和访问该JSON的内容。

数据转换函数

以下的解析函数将字符串转换为JSONOBJECT或JSONARRAY:

函数名及其参数

描述

返回值类型

parse_json_object(STRING str)

将str转换成JSON对象

JSONOBJECT

parse_json_array( STRING str )

将str转换成JSON数组

JSONARRAY

如果输入的字符串无法转换为JSON对象或JSON数组,则这两个函数都会实时报错。 所以若要格式正确,除了正确嵌套和正确匹配花括号{}和方括号[]之外,每个值都必须是以下几种类型之一:字符串(由双引号引用),数字,布尔值(true或false),JSONOBJECT或JSONARRAY。键值对的每个键必须是由双引号引用的字符串。

见下面的例子。

parse_json_object 和 parse_json_array的例子
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 的结果
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": {
   "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.

数据访问方法

JSONOBJECT和JSONARRAY是对象类,每个类都使用点表示法来支持内容访问,例如:

jsonVariable.functionName(parameter_list)

以下方法(类函数)可以作用于JSONOBJECT变量:

方法名

描述

返回值类型

containsKey(STRING keyStr )

返回一个布尔值,指示JSON对象是否包含键keyStr。

BOOL

getInt(STRING keyStr )

返回与keyStr的取整。

INT

getDouble (STRING keyS tr )

返回与keyStr的DOUBLE格式。

DOUBLE

getString (STRING keyS tr )

返回与keyStr的字符串值。

STRING

getBool (STRING keyS tr )

返回与keyStr的布尔值。

BOOL

getJsonObject (STRING keyS tr )

返回与keyStr的JSONOBJECT。

JSON

OBJECT

getJsonArray (STRING keySt r )

返回与keyStr的JSONARRAY。

JSON

ARRAY

如果出现下列情况,上述getType(STRING keyStr)函数会生成实时错误:

  1. 键keyStr不存在

  2. 函数的返回类型与存储值的类型不同。 详见有关数字格式数据的下条注释。

  3. 纯正的JSON只存储“数字”而不区分INT和DOUBLE,但对于TigerGraph,如果输入值的每一位都是一个数字,则它将存储为整数类型。 其他情况下则存储为DOUBLE。 getDouble函数可以读取INT并返回其等效的DOUBLE值,但是对DOUBLE值调用getINT则会报错。

以下方法可以作用于JSONARRAY变量:

方法名

描述

返回值类型

size()

返回数组的大小

INT

getInt( INT idx )

以INT格式返回位于idx位置的数字值

INT

getDouble( INT idx )

以DOUBLE格式返回位于idx位置的数字值

DOUBLE

getString( INT idx )

返回位于idx位置的字符串值

STRING

getBool( INT idx )

返回位于idx位置的布尔值

BOOL

getJsonObject( INT idx)

返回位于idx位置的JSONOBJECT值

JSON

OBJECT

getJsonArray( INT idx )

返回位于idx位置的JSONARRAY值

JSON

ARRAY

与JSONOBJECT的方法类似,上面的getType(INT idx)函数会生成实时错误,如果:

  1. 1. idx超出界限

  2. 函数的返回类型与存储值的类型不同。详见有关数字格式数据的下条注释。

  3. 纯正的JSON只存储“数字”而不区分INT和DOUBLE,但对于TigerGraph,如果输入值的每一位都是一个数字,则它将存储为整数类型。 其他情况下则存储为DOUBLE。 getDouble函数可以读取INT并返回其等效的DOUBLE值,但是对DOUBLE值调用getINT则会报错。

以下是使用这些函数和方法的示例:

JSONOBJECT 和JSONARRAY 函数的例子
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 的结果
GSQL > RUN QUERY jsonEx2()
{
 "error": false,
 "message": "",
 "version": {
   "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"
   }
 ]
}

顶点、边、累加器函数和属性

访问属性

顶点或边的属性在图形数据库纲目中定义。 此外,每个顶点和边都有一个名叫type的内置属性字符串,它表示该边或顶点的用户自定义类型。 用户可以使用点运算符来访问某个边或顶点的属性(包括type)。

例如,以下代码段展示了两个不同的SELECT语句,但它们输出相同的结果。 第一个使用顶点变量v上的点运算符来访问“subject”属性,该属性在图形数据库纲目中定义。 第一个SELECT语句中的FROM子句要求任何目标顶点都是“post”类型(也在图形数据库纲目中定义)。 第二个SELECT语句通过让点运算符访问内置type属性的方式来检查顶点变量v的类型是否为一个“post”顶点。

访问顶点变量的属性
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;
}
查询 coffeeRelatedPosts的结果
GSQL > RUN QUERY coffeeRelatedPosts()
{
 "error": false,
 "message": "",
 "version": {
   "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_alias.function_name(parameter)[.FILTER(condition)]

目前,这些函数仅适用于顶点别名(在FROM子句中定义)的访问; 顶点变量并不能用于这些函数。

请注意,为了按边类计算外部出度(outdegree),用户必须定义图形数据库纲目,并令顶点使用WITH STATS=“OUTDEGREE_BY_EDGETYPE”的方式跟踪其边类(但目前“OUTDEGREE_BY_EDGETYPE”已经是STATS的默认选项了)。

函数名

描述

返回值类型

outdegree ([STRING edgeType ])

返回某个顶点上发出的或无向的边的数目。如果可选项STRING被赋值edgeType,则仅统计给定类型的边的数目。

INT

neighbors ([ STRING edgeType ])

返回一系列ID。这些ID属于某个顶点上发出的边指向的邻居顶点或无向边指向的邻居顶点。如果可选项STRING被赋值edgeType,则仅统计给定类型的边可以到达的邻居顶点

BagAccum<VERTEX>

neighborAttribute

( STRING edgeType,

STRING targetVertexType,

STRINGattribute )

从给定顶点出发,遍历指向给定类型的顶点的给定类型的边。获得一组给定的属性的值并将它们返回。edgeType只能是文字字符串

BagAccum<attributeType>

edgeAttribute

( STRING edgeType,

STRING attribute )

从给定顶点出发,遍历给定类型的边,获得一组给定的属性的值并将其返回。edgeType只能是文字字符串

BagAccum<attributeType>

Vertex function的例子
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 的结果
GSQL > RUN QUERY vertexFunctionExample("person5")
{
 "error": false,
 "message": "",
 "version": {
   "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参数

可选的.FILTER(条件)子句是一个额外的过滤器,用于选择将哪些元素添加到neighbor,neighborAttribute和edgeAttribute函数的输出集中。该条件会判断每一个元素。 如果判断结果为真,则将元素添加到输出集; 如果是假,则不添加。 下面是一个例子:

例:带有过滤器的顶点函数
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
}
filterEx.json的结果
GSQL > RUN QUERY filterEx(["person1","person2"],2016)
{
 "error": false,
 "message": "",
 "version": {
   "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
 }]
}

边函数

下面列出的函数是使用点运算符,并可以通过边的别名访问的内置函数。 边函数遵循与顶点函数相同的一般规则(参见上文)。

函数名

描述

返回值类型

isDirected ()

返回一个布尔值,提示该边是否为一个有向边或无向边。

BOOL

累加器函数

每种类型的累加器函数都已经在“累加器类型”一章中详述。

Set和Bag的表达式和运算符

SELECT语句对输入的顶点集执行各种选择和过滤操作以生成输出集。 因此,Set和Bag的表达式及其运算符是令GSQL图形查询语言与众不同且功能强大的缘由。 Set和Bag的表达式可以使用SetAccum或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的表达式运算符 – 并集,交集,集合相减

这几个运算符都很容易理解,当两个操作数都是集合时,则结果也是一个集合。 当至少一个操作数是一个Bag时,结果就是一个Bag。 如果一个操作数是Bag,而另一个是Set,则运算符将该Set视为一个Bag。Bag中包含了Set中的每个值。

例:Set和Bag运算符
# 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 的结果
GSQL > RUN QUERY setOperatorsEx()
{
 "error": false,
 "message": "",
 "version": {
   "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 ]}
 ]
}

由于这些运算符的运算结果是一个新的set或Bag,因此可以通过嵌套和链接的方式编写更复杂的表达式,例如:

(setBagExpr_A INTERSECT (setBagExpr_B UNION setBagExpr_C) ) MINUS setBagExpr_D

Set和BagExpression 运算符成员

Set和Bag表达式的运算符成员

例如,假设我们定义一个bag: 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

IN和NOT IN运算符的左侧支持所有的基本类,右侧则支持任何set 或bag表达式。 基本类必须与累加器元素的类型相同。IN和NOT IN返回一个布尔值。

以下示例使用NOT IN排除黑名单中的邻居。

Set Membership的例子
CREATE QUERY friendsNotInblacklist (VERTEX<person> seed, SET<VERTEX<person>> blackList) FOR GRAPH socialNet `{
 Start = {seed};
 Result = SELECT v
     FROM Start:s-(friend:e)-person:v
     WHERE v NOT IN blackList;
 PRINT Result;
}
查询friendsNotInblacklist的结果
GSQL > RUN QUERY friendsNotInblacklist("person1", ["person2"])
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"Result": [{
   "v_id": "person8",
   "attributes": {
     "gender": "Male",
     "id": "person8"
   },
   "v_type": "person"
 }]}]
}

聚合函数 – 计数(COUNT),求和(SUM),最小值(MIN),最大值(MAX),平均值(AVG)

聚合函数的输入参数为一个 set或bag,输出为一个值或元素。

  • count():返回set的大小(整数)。

  • sum():返回所有元素的总和。 这仅适用于具有数字类型的Set和Bag。

  • min():返回最小值。 这仅适用于具有数字类型的Set和Bag

  • max():返回最大值。 这仅适用于具有数字类型的Set和Bag。

  • avg():返回所有元素的平均值。 这仅适用于具有数字类型的Set和Bag。 如果Set和Bag内的元素类型是整数,则返回的平均值为整数。

聚合函数的例子
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 的结果
GSQL > RUN QUERY aggregateFuncEx([1,2,5])
{
 "error": false,
 "message": "",
 "version": {
   "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
   }
 ]
}

其他类型函数

SelectVertex()函数

SelectVertex()读取一个包含特定顶点列表的数据文件,并返回相应的顶点集。 此函数只能在顶点集变量的声明语句中用作种子集。 该数据文件必须是一张包含一列或多列的表,且其中一列必须是顶点id。 此外,用户也可以选择另一列用于标明顶点类型。 SelectVertex()有五个参数,如下表所示:filePath,vertexIdColumn,vertexTypeColumn,separator和header。 列分隔符和列标题的规则与GSQL 加载器的规则相同。

参数名

类型

描述

filePath

字符串

仅支持输入文件的绝对地址。不支持相对地址。

vertexIdColumn

如果文件标头(header)的值为true,则为$num, 或$"column_name"

顶点ID列的位置

vertexTypeColumn

如果文件标头(header)的值为true,则为$num, $"column_name", 亦或是直接标明一个顶点类。

顶点类的位置,或特定的顶点类

separator

单个字符

列分隔符的字符

header

布尔值

输入文件是否包含标头(header)

一个顶点集变量的声明语句中可以包含多个SelectVertex()函数调用。 但如果该声明中包含多个引用同一文件的SelectVertex(),则它们必须使用相同的分隔符和标头。 如果文件中的任何行包含无效的顶点类型,则系统会报告运行错误;如果文件的任何行包含不存在的顶点id,则系统会提示一条告警,告警显示无效id的数量。

下面的示例使用了SelectVertex(),从数据文件selectVertexInput.csv中获取信息。

selectVertexInput.csv
c1,c2,c3
person1,person,3
person5,person,4
person6,person,5
selectVertex的例子
CREATE QUERY selectVertexEx(STRING filename) FOR GRAPH socialNet {
 S = {SelectVertex(filename, $"c1", $1, ",", true),
      SelectVertex(filename, $2, post, ",", true)
 };
 PRINT S;
}
结果
GSQL > RUN QUERY selectVertexEx("/file_directory/selectVertexInput.csv")
{
 "error": false,
 "message": "",
 "version": {
   "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() 和to_vertex_set()

函数to_vertex()和函数to_vertex_set()将字符串或字符串集分别转换指定顶点类所规定的顶点或顶点集。 当顶点id只能在运行时被获取时,这两个函数十分有用。

运行这些函数需要将外部id实时转换为GSQL内部id,这是一个相对较慢的过程。 因此,

  1. 如果用户查询开始之前就知道id的话,请使用VERTEX或SET <VERTEX>参数编写查询,而不要用STRING或SET<STRING>编写查询,并避免使用to_vertex()或to_vertex_set()这两个函数。

  2. 调用一次to_vertex_set()比调用多次to_vertex()的速度要快得多,所以尽可能多使用to_vertex_set()而不是to_vertex()。

to_vertex_set()的第一个参数表示顶点id的集合(字符串集)。 两个函数的第二个参数都是顶点类(字符串)。

to_vertex() 与to_vertex_set()的函数签名
VERTEX to_vertex(STRING id, STRING vertex_type)
SET<VERTEX> to_vertex_set(SET<VERTEX>, STRING vertex_type)
SET<VERTEX> to_vertex_set(BAG<VERTEX>, STRING vertex_type)

系统允许向to_vertex_set一次输入一个Bag的顶点集合,但该功能会启动自动消重功能,从而把bag转换为set。

如果某个顶点id或顶点类不存在,则to_vertex()将会出现运行错误,错误如下所示。 但是to_vertex_set()只有当顶点类型不存在时,才会出现运行错误。 如果一个或多个顶点id不存在,则to_vertex_set()将显示警告消息但仍将运行,并转换所有有效的id并跳过不存在id。 在将字符串集转换为顶点集时,若用户面对无效ID只想要错误而不是警告,则用户应该在FOREACH循环内使用to_vertex(),而不是to_vertex_set()。 详见下面的示例。

to_vertex() 和to_vertex_set()的例子
CREATE QUERY to_vertex_set (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 的结果
GSQL > RUN QUERY to_vertex_set(["person1","personx","person2"], "person3", "person")
{
 "error": false,
 "message": "Runtime Warning: 1 ids are invalid person vertex ids.",
 "version": {
   "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_set(["person1","personx"], "person1", "abc")
Runtime Error: abc is not valid vertex type.

COALESCE()函数

COALESCE函数按顺序计算每个参数值,并返回第一个非空的(NOT NULL)值。该运算与IS NULL和IS NOT NULL操作相同。 COALESCE函数要求其所有的参数都具有相同的数据类型(布尔,整数,浮点,双浮点,字符串或顶点),唯一的一个例外是不同的数字类型可以放在一起使用。 在这种情况下,所有数字类型都将转换为第一个参数的类型。

coalesce function的例子
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 的结果
GSQL > RUN QUERY coalesceFuncEx(_,_)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"coalesce(p1,p2,999.5)": 999}]
}
GSQL > RUN QUERY coalesceFuncEx(1,2)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"coalesce(p1,p2,999.5)": 1}]
}
GSQL > RUN QUERY coalesceFuncEx(_,2.5)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"coalesce(p1,p2,999.5)": 2}]
}

COALESCE函数在允许出现多个可选参数,且至少有一个必须选中的情况下十分有用,例如:

coalesce function的例子
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");  
}

COALESCE函数的参数列表的最后一个参数,应该拥有一个默认值。 这样,如果当所有的值都为NULL的时候,函数就返回最后一个参数的默认值。

GSQL > RUN QUERY coalesceFuncEx2(_,_,_)
{
 "error": false,
 "message": "",
 "version": {
   "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"}
 ]
}

带有EVALUATE()函数的动态表达式

函数evaluate()将一个字符串转换为一个表达式,并实时运算执行该表达式。 这使用户有能力创建一个通用的查询,而不必为每种特定的计算都创建单独的查询。

evaluate(expressionStr, typeStr)

evaluate()函数有两个参数:expressionStr是字符串类型的表达式,typeStr也是字符串,用于描述表达式的类型。 此函数返回一个类型为typeStr的值,该值的内容为expressionStr的运算结果。 以下规则适用:

  1. evaluate()只能在SELECT语句的内部使用,并且只能在WHERE子句,ACCUM子句,POST-ACCUM子句,HAVING子句或排序子句中使用。它不能用于LIMIT子句或用于SELECT语句之外。

  2. 用户必须在安装查询的时候就指定结果的类型:typeStr必须是基础数据类型的字符串形式,例如“int”,“float”,“double”,“bool”,“string”(不区分大小写)。默认值为“bool”。

  3. 在expressionStr中,标识符只能是以下类型:顶点或边的别名,附属于顶点的累加器,全局累加器,参数,或涉及上述变量的标量函数。表达式可能不是局部变量,全局变量或FROM子句内的顶点或边。

  4. 表达式中的任何累加器必须是标量累加器(例如,MaxAccum),用于基本类的数据。不支持容器累加器(如SetAccum)或具有非基本类的标量累加器(如VERTEX,EDGE,DATETIME)。不支持容器类型的属性。

  5. evaluate()不能嵌套。

  6. 以下情况会生成运行时错误:

    1. 字符串表达式无法编译(由不存在的顶点或边导致的错误除外)。

    2. 表达式的结果类型与参数typeStr不匹配。

静默错误

如果出现以下任何一种情况,查询可能会继续运行,但evaluate()函数所在的整个子句或语句将失败,却不会产生运行错误消息。 对于条件子句(WHERE,HAVING)来说,错误的evaluate()子句会被视为条件值false。 所以包含错误evaluate()语句的赋值动作将不会被执行,包含错误evaluate()语句的排序子句也不会被排序。

  1. 表达式引用了某个在顶点或边的别名中不存在的属性。

  2. 表达式使用运算符进行非兼容操作。 例如,123 ==“xyz”。

例:在WHERE条件子句和POST-ACCUM子句中的累加器值中使用动态表达式。

Evaluate的例子
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;
}
查询 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": []}]
}

函数形式的查询 (子查询)

一个使用CREATE QUERY ... RETURNS语句定义的查询可被另一个查询调用,即查询可以嵌套。

已知问题:

当前查询无法调用自身。 修复正在进行中。

查询调用另一个查询有以下限制:

  1. 被调用查询的每个参数必须是以下类型之一:

    1. 基本类:INT,UINT,FLOAT,DOUBLE,STRING,BOOL

    2. 顶点

    3. 基本类型的Set或Bag或顶点

  2. 返回值可以是以下类型之一。 详见“返回声明”部分。

    1. 基本类:INT,UINT,FLOAT,DOUBLE,STRING,BOOL

    2. 顶点

    3. 顶点集(例如,SELECT语句的输出结果)

    4. 基本类的累加器,但不支持GroupByAccum累加器和包含元组的累加器。

  3. 如果一个查询返回的是SetAccum或BagAccum累加器值, 则可在调用时分别使用Set或Bag的参数

  4. 定义查询的顺序是很重要的。 一个查询无法调用一个尚未定义的查询。

子查询 – 例1
CREATE QUERY subquery1 (VERTEX<person> m1) FOR GRAPH socialNet RETURNS(BagAccum<VERTEX<post>>)
{
 Start = {m1};
 L = SELECT t
     FROM Start:s - (liked:e) - post:t;
 RETURN L;
}
CREATE QUERY mainquery1 () FOR GRAPH socialNet
{
 BagAccum<VERTEX<post>> @@testBag;
 Start = {person.*};
 Start = SELECT s FROM Start:s
         ACCUM @@testBag += subquery1(s);
 PRINT @@testBag;
}

用户自定义函数

用户可以在以下文件<tigergraph.root.dir>/dev/gdk/gsql/src/QueryUdf/ExprFunctions.hpp中用C++定义自己的函数表达式,但只允许使用bool,int,float,double和string(NOT std :: string)作为返回值的类型和函数参数的类型。 但是,在函数表达式正文内允许任何C++的类型。 一旦某个函数被定义,则下次执行GSQL时,新函数将会自动添加到GSQL中。

如果需要定义一个用户自定义的结构或一个帮助函数,请在<tigergraph.root.dir>/dev/gdk/gsql/src/QueryUdf/ExprUtil.hpp中定义它们。

以下是一个例子:

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;
}
用户自定义函数表达式
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 的结果
GSQL > RUN QUERY udfExample() 
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {"greater_than_three(x)": true},
   {"y": false},
   {"reverse(abc)": "cba"}
 ]
}

如果ExprFunctions.hpp或ExprUtil.hpp中的任何代码导致了编译错误,则即使某个GSQL查询并未调用任何用户自定义函数,系统也将无法安装任何GSQL查询。 因此,请每次在添加代码后,都要对新的用户自定义表达式函数进行测试。 测试该函数的一种可选的方法是:创建一个新的cpp文件test.cpp并对其进行编译

> g ++ test.cpp

> ./a.out

您可能需要删除ExprFunction.hpp和ExprUtil.hpp中的include标头 #include <gle/engine/cpplib/headers.hpp>才能进行编译。

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;
}

表达式的示例

下面是一系列表达式示例。请注意,(argList)是Set或Bag表达式,而[ argList ]是列表表达式。

表达式示例
#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 的结果
GSQL > RUN QUERY expressionEx()
{
 "error": false,
 "message": "",
 "version": {
   "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}
 ]
}

表达式声明的示例

表达式声明的示例
#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": {
   "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