控制流语句

GSQL图形查询语言中包括了一系列功能全面的控制流语句,用户可以利用它们进行复杂的图遍历和数据计算,它们包括了:IF/ELSE,CASE,WHILE和FOREACH语句。

块语法(Block Syntax)的差异

请注意,这些语句中的任何一个都可以用于查询主体层语句或DML子句。

如果控制流语句位于查询的主体层,则其语句块是查询主体层语句(queryBodyStmts)。 在查询主体层语句中,每个单独的语句以分号结束,语句块末尾也是分号。

如果控制流语句处于DML子句层,则该语句块为DML子句(DMLSubStmtList)。 在DML子句中,语句之间由逗号分隔,但最后的末尾没有标点符号。

有关查询主体层语句和DML子句之间差异,详见“创建/安装/执行/显示/删除查询”一章中的“语句类型”小节。

IF语句

IF语句根据设定的条件产生逻辑分支:仅当给定条件为真时才执行语句块(查询主体层语句或DML子句)。 IF语句允许零个或多个ELSE-IF子句,后跟可选的ELSE子句。 IF语句可以在查询主体层语句或DML子句使用。 (请参阅有关块语法差异的说明。)

IF 的EBNF范式
queryBodyIfStmt := IF  condition  THEN  queryBodyStmts   [ELSE IF  condition  THEN  queryBodyStmts  ]* [ELSE  queryBodyStmts  ] END
DMLSubIfStmt := IF  condition  THEN  DMLSubStmtList   [ELSE IF  condition  THEN  DMLSubStmtList  ]* [ELSE  DMLSubStmtList  ] END

如果某个IF的条件不为真,则跳过该条件的语句而检测下一个ELSE IF条件。 直到遇到条件为真时,执行相应的语句块,然后IF语句结束(即跳过任何剩余的ELSE-IF或ELSE子句)。 当存在ELSE子句时,则如果前述所有条件均不为真,则执行该ELSE语句块。 总的来说,功能可以概括为“执行条件测试为真的第一个语句块”。

IF 的语义说明
# if then
IF x == 5 THEN y = 10; END;      # y is assigned to 10 only if x is 5.
 
# if then else
IF x == 5 THEN y = 10;       # y is 10 only if x is 5.
ELSE y = 20; END;            # y is 20 only if x is NOT 5.
 
#if with ELSE IF
IF x == 5 THEN y = 10;       # y is 10 only if x is 5.
ELSE IF x == 7 THEN y = 5;   # y is 5  only if x is 7.
ELSE y = 20; END;            # y is 20 only if x is NOT 5 and NOT 7.
例1:countFriendsOf2.gsql:在查询主体层语句中的IF-ELSE语句的简单示例
# count the number of friends a person has, and optionally include coworkers in that count
CREATE QUERY countFriendsOf2(vertex<person> seed, BOOL includeCoworkers) FOR GRAPH friendNet
{
      SumAccum<INT> @@numFriends = 0;
      start = {seed};
 
      IF includeCoworkers THEN
         friends = SELECT v FROM start -((friend | coworker):e)-> :v
               ACCUM @@numFriends +=1;
      ELSE
         friends = SELECT v FROM start -(friend:e)-> :v
               ACCUM @@numFriends +=1;
      END;
      PRINT @@numFriends, includeCoworkers;
}
例 1 的结果
GSQL > RUN QUERY countFriendsOf2("person2", true)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{
   "@@numFriends": 5,
   "includeCoworkers": true
 }]
}
GSQL > RUN QUERY countFriendsOf2("person2", false)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{
   "@@numFriends": 2,
   "includeCoworkers": false
 }]
}
例2:在查询主体层语句中使用IF-ELSE语句
# determine if a user is active in terms of social networking (i.e., posts frequently)
CREATE QUERY calculateActivity(vertex<person> seed) FOR GRAPH socialNet
{
   SumAccum<INT> @@numberPosts = 0;
   start = {seed};
   result = SELECT postVertex FROM start -(posted:e)-> :postVertex
          ACCUM @@numberPosts += 1;
 
   IF  @@numberPosts < 2 THEN
      PRINT "Not very active";
   ELSE IF @@numberPosts < 3 THEN
      PRINT "Semi-active";
   ELSE
      PRINT "Very active";
   END;
}
例 2 Query calculateActivity的结果
GSQL > RUN QUERY calculateActivity("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"Not very active": "Not very active"}]
}
GSQL > RUN QUERY calculateActivity("person5")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"Semi-active": "Semi-active"}]
}
例3:在查询主体层语句嵌套IF语句
# use a more advanced activity calculation, taking into account number of posts
# and number of likes that a user made
CREATE QUERY calculateInDepthActivity(vertex<person> seed) FOR GRAPH socialNet
{
   SumAccum<INT> @@numberPosts = 0;
   SumAccum<INT> @@numberLikes = 0;
   start = {seed};
   result = SELECT postVertex FROM start -(posted:e)-> :postVertex
          ACCUM @@numberPosts += 1;
   result = SELECT likedPost FROM start -(liked:e)-> :likedPost
          ACCUM @@numberLikes += 1;
 
   IF @@numberPosts < 2 THEN
       IF @@numberLikes < 1 THEN
           PRINT "Not very active";
       ELSE
           PRINT "Semi-active";
       END;
   ELSE IF @@numberPosts < 3 THEN
       IF @@numberLikes < 2 THEN
           PRINT "Semi-active";
       ELSE
           PRINT "Active";
       END;
   ELSE
       PRINT "Very active";
   END;
}
例3 Query calculateInDepthActivity的结果
GSQL > RUN QUERY calculateInDepthActivity("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"Semi-active": "Semi-active"}]
}
例4:在DML子句嵌套IF语句
# give each user post an accumulated rating based on the subject and how many likes it has
# This query is equivalent to the query ratePosts shown above
CREATE QUERY ratePosts2() FOR GRAPH socialNet {      
   SumAccum<INT> @rating = 0;
   allPeople = {person.*};
 
   results = SELECT v FROM allPeople -(:e)-> post:v
       ACCUM IF e.type == "posted" THEN
               IF v.subject == "cats" THEN
                 v.@rating += -1     # -1 if post is about cats
               ELSE IF v.subject == "Graphs" THEN
                 v.@rating += 2      # +2 if post is about graphs
               ELSE IF v.subject == "tigergraph" THEN
                 v.@rating += 10     # +10 if post is about tigergraph
               END
             ELSE IF e.type == "liked" THEN
               v.@rating += 3                            # +3 each time post was liked
           END
       ORDER BY v.@rating DESC
       LIMIT 5;
   PRINT results;
}
例4 Query ratePosts2的结果
GSQL > RUN QUERY ratePosts2()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"results": [
   {
     "v_id": "6",
     "attributes": {
       "postTime": "2011-02-05 02:02:05",
       "subject": "tigergraph",
       "@rating": 13
     },
     "v_type": "post"
   },
   {
     "v_id": "0",
     "attributes": {
       "postTime": "2010-01-12 11:22:05",
       "subject": "Graphs",
       "@rating": 11
     },
     "v_type": "post"
   },
   {
     "v_id": "1",
     "attributes": {
       "postTime": "2011-03-03 23:02:00",
       "subject": "tigergraph",
       "@rating": 10
     },
     "v_type": "post"
   },
   {
     "v_id": "5",
     "attributes": {
       "postTime": "2011-02-06 01:02:02",
       "subject": "tigergraph",
       "@rating": 10
     },
     "v_type": "post"
   },
   {
     "v_id": "4",
     "attributes": {
       "postTime": "2011-02-07 05:02:51",
       "subject": "coffee",
       "@rating": 6
     },
     "v_type": "post"
   }
 ]}]
}

CASE语句

CASE语句根据条件设定产生逻辑分支:仅当给定条件为真时才执行语句块。 CASE语句可以用作查询主体层语句或DML子句。(请参阅有关块语法差异的说明。)

CASE 语句的语法
queryBodyCaseStmt := CASE      (WHEN condition THEN queryBodyStmts)+ [ELSE queryBodyStmts] END
                  | CASE expr (WHEN constant  THEN queryBodyStmts)+ [ELSE queryBodyStmts] END
DMLSubCaseStmt := CASE      (WHEN condition THEN DMLSubStmtList)+ [ELSE DMLSubStmtList] END
               | CASE expr (WHEN constant  THEN DMLSubStmtList)+ [ELSE DMLSubStmtList] END

一个CASE语句包含一个或多个WHEN-THEN子句,每个子句都是一个表达式。 CASE语句也可以包含一个ELSE子句,当之前的条件都不为真时,则执行该ELSE子语句。

CASE语句有两种语法:其中的一种语法等效于if-else语句,而另一种则类似于SWITCH语句。对于等效于IF-ELSE语句来说,系统会计算每个WHEN子句中的布尔条件,并执行条件为真的第一个语句块。仅当所有WHEN子句条件均为假时,才会执行末尾的ELSE子句(如果有)。

另一种类似于SWITCH语句的语法的运算逻辑则如下:计算关键字WHEN之后的表达式,并将其结果与紧跟在关键字CASE之后的表达式的值进行比较。这些值并不需要一定是布尔类型; CASE语句会比较它们,并判断它们是否相等。系统会执行第一个判断结果相等的WHEN-THEN子句;剩下的子句则被跳过。仅当没有WHEN子句结果与CASE的输出值相等的时候,系统才会执行可选的ELSE子句。

CASE 语句的语法结构
STRING drink = "Juice";
 
# CASE statement: if-else version
CASE
 WHEN drink == "Juice" THEN @@calories += 50    
 WHEN drink == "Soda"  THEN @@calories += 120
 ...
 ELSE @@calories = 0       # Optional else-clause
END
# Since drink = "Juice", 50 will be added to calories
 
# CASE statement: switch version
CASE drink                        
 WHEN "Juice" THEN @@calories += 50  
 WHEN "Soda"  THEN @@calories += 120
 ...
 ELSE  @@calories = 0    # Optional else-clause
END
# Since drink = "Juice", 50 will be added to calories
例1. 与 IF-ELSE等效的CASE语句示例
# Display the total number times connected users posted about a certain subject
CREATE QUERY userNetworkPosts (vertex<person> seedUser, STRING subjectName) FOR GRAPH socialNet {
   SumAccum<INT> @@topicSum = 0;
   OrAccum @visited;
   reachableVertices = {};            # empty vertex set
   visitedVertices (ANY) = {seedUser};  # set that can contain ANY type of vertex
 
   WHILE visitedVertices.size() !=0 DO        # loop terminates when all neighbors are visited
       visitedVertices = SELECT s              # s is all neighbors of visitedVertices which have not been visited
           FROM visitedVertices-(:e)->:s
           WHERE s.@visited == false
           ACCUM s.@visited = true,
               CASE
                   WHEN s.type == "post" and s.subject == subjectName THEN @@topicSum += 1
               END;
   END;
   PRINT @@topicSum;
}
例 1 Query userNetworkPosts的结果
GSQL > RUN QUERY userNetworkPosts("person1", "Graphs")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@topicSum": 3}]
}

2.与switch等效的CASE语句的示例
# tally male and female friends of the starting vertex
CREATE QUERY countGenderOfFriends(vertex<person> seed) FOR GRAPH socialNet {
      SumAccum<INT> @@males = 0;
      SumAccum<INT> @@females = 0;
      SumAccum<INT> @@unknown = 0;
      startingVertex = {seed};      
 
      people = SELECT v FROM startingVertex -(friend:e)->:v
      ​        ACCUM CASE v.gender
​      ​      WHEN "Male" THEN @@males += 1
​​   ​​  WHEN "Female" THEN @@females +=1
​​    ​  ELSE @@unknown += 1
​​   ​    END;
PRINT @@males, @@females, @@unknown;
}
例 2.与switch等效的CASE语句的示例
# tally male and female friends of the starting vertex
CREATE QUERY countGenderOfFriends(vertex<person> seed) FOR GRAPH socialNet {
      SumAccum<INT> @@males = 0;
      SumAccum<INT> @@females = 0;
      SumAccum<INT> @@unknown = 0;
      startingVertex = {seed};      
 
      people = SELECT v FROM startingVertex -(friend:e)->:v
      ​        ACCUM CASE v.gender
​      ​      WHEN "Male" THEN @@males += 1
​​   ​​  WHEN "Female" THEN @@females +=1
​​    ​  ELSE @@unknown += 1
​​   ​    END;
PRINT @@males, @@females, @@unknown;
}
例 2 Query countGenderOfFriends的结果
GSQL > RUN QUERY countGenderOfFriends("person4")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{
   "@@males": 2,
   "@@unknown": 0,
   "@@females": 1
 }]
}
例 3.多个CASE语句共同工作
# give each social network user a social impact score which accumulates
# based on how many friends and posts they have
CREATE QUERY scoreSocialImpact() FOR GRAPH socialNet api("v2") {
      SumAccum<INT> @socialImpact = 0;
      allPeople = {person.*};
      people = SELECT v FROM allPeople:v
             ACCUM CASE WHEN v.outdegree("friend") > 1 THEN v.@socialImpact +=1 END, # +1 point for having > 1 friend
                   CASE WHEN v.outdegree("friend") > 2 THEN v.@socialImpact +=1 END, # +1 point for having > 2 friends
                   CASE WHEN v.outdegree("posted") > 1 THEN v.@socialImpact +=1 END, # +1 point for having > 1 posts
                   CASE WHEN v.outdegree("posted") > 3 THEN v.@socialImpact +=2 END; # +2 points for having > 2 posts
      #PRINT people.@socialImpact;       // api v1
      PRINT people[people.@socialImpact]; // api v2
}
例 3 Query scoreSocialImpact的结果
GSQL > RUN QUERY scoreSocialImpact()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"people": [
   {
     "v_id": "person4",
     "attributes": {"people.@socialImpact": 2},
     "v_type": "person"
   },
   {
     "v_id": "person3",
     "attributes": {"people.@socialImpact": 1},
     "v_type": "person"
   },
   {
     "v_id": "person7",
     "attributes": {"people.@socialImpact": 2},
     "v_type": "person"
   },
   {
     "v_id": "person1",
     "attributes": {"people.@socialImpact": 1},
     "v_type": "person"
   },
   {
     "v_id": "person5",
     "attributes": {"people.@socialImpact": 2},
     "v_type": "person"
   },
   {
     "v_id": "person6",
     "attributes": {"people.@socialImpact": 2},
     "v_type": "person"
   },
   {
     "v_id": "person2",
     "attributes": {"people.@socialImpact": 1},
     "v_type": "person"
   },
   {
     "v_id": "person8",
     "attributes": {"people.@socialImpact": 3},
     "v_type": "person"
   }
 ]}]
}
例4: CASE语句的嵌套
# give each user post a rating based on the subject and how many likes it has
CREATE QUERY ratePosts() FOR GRAPH socialNet api("v2") {
 SumAccum<INT> @rating = 0;
 allPeople = {person.*};
 
 results = SELECT v FROM allPeople -(:e)-> post:v
   ACCUM CASE e.type
     WHEN "posted" THEN
       CASE
         WHEN v.subject == "cats" THEN v.@rating += -1    # -1 if post about cats
         WHEN v.subject == "Graphs" THEN v.@rating += 2   # +2 if post about graphs
         WHEN v.subject == "tigergraph" THEN v.@rating += 10 # +10 if post about tigergraph
         END
     WHEN "liked" THEN v.@rating += 3                 # +3 each time post was liked
     END;
 #PRINT results.@rating; // api v1
 PRINT results[results.@rating]; // api v2
}
例 4 Query ratePosts的结果
GSQL > RUN QUERY ratePosts()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"results": [
   {
     "v_id": "0",
     "attributes": {"results.@rating": 11},
     "v_type": "post"
   },
   {
     "v_id": "10",
     "attributes": {"results.@rating": 2},
     "v_type": "post"
   },
   {
     "v_id": "2",
     "attributes": {"results.@rating": 0},
     "v_type": "post"
   },
   {
     "v_id": "4",
     "attributes": {"results.@rating": 6},
     "v_type": "post"
   },
   {
     "v_id": "9",
     "attributes": {"results.@rating": -1},
     "v_type": "post"
   },
   {
     "v_id": "3",
     "attributes": {"results.@rating": 2},
     "v_type": "post"
   },
   {
     "v_id": "5",
     "attributes": {"results.@rating": 10},
     "v_type": "post"
   },
   {
     "v_id": "7",
     "attributes": {"results.@rating": 2},
     "v_type": "post"
   },
   {
     "v_id": "1",
     "attributes": {"results.@rating": 10},
     "v_type": "post"
   },
   {
     "v_id": "11",
     "attributes": {"results.@rating": -1},
     "v_type": "post"
   },
   {
     "v_id": "8",
     "attributes": {"results.@rating": 2},
     "v_type": "post"
   },
   {
     "v_id": "6",
     "attributes": {"results.@rating": 13},
     "v_type": "post"
   }
 ]}]
}

WHILE语句

WHILE语句提供无限次的迭代功能。 WHILE语句可以用作查询主体层语句或DML子句。 (请参阅有关块语法差异的说明。)

WHILE 的语法
queryBodyWhileStmt := WHILE  condition  [LIMIT (name | integer)] DO queryBodyStmts END
DMLSubWhileStmt := WHILE  condition  [LIMIT (name | integer)] DO DMLSubStmtList END

WHILE语句反复运行其代码(查询主体层语句或DML子句),直到条件判断结果为假或直到满足迭代次数的上限为止。 其条件设定可以是任何输出值为布尔值的运算,每次迭代之前需要计算条件是否为真。 CONTINUE子句可用于改变WHILE语句中的控制流。 BREAK子句可用于退出WHILE循环。

用户也可以选择WHILE语句中添加LIMIT子句。 由于LIMIT子句输出一个正整数或整数变量,所以可以用其约束循环迭代的最大次数。 下面的示例演示了添加LIMIT子句的使用方法。

如果不指定限制值,则WHILE循环会无限次迭代。 编写查询的人必须设计合理的条件逻辑,以确保循环拥有正确的退出机制(或设置循环次数限制)。

WHILE语句配合LIMIT子句共同工作
# These three WHILE statements behave the same。Each terminates when
# (v.size == 0) or after 5 iterations of the loop.
WHILE v.size() !=0 LIMIT 5 DO
   # Some statements​​
END;
 
INT iter = 0;
WHILE (v.size() !=0) AND (iter < 5) DO
​# Some statements
   iter = iter + 1;​​
END;
 
INT iter = 0;
WHILE v.size() !=0 DO
   IF iter == 5 THEN  BREAK;  END;
   # Some statements​
​iter = iter + 1;​
END;

下面是一些WHILE语句的使用示例。

例 1. 一个简单的WHILE 循环
# find all vertices which are reachable from a starting seed vertex (i.e., breadth-first search)
CREATE QUERY reachable(vertex<person> seed) FOR GRAPH workNet
{
   OrAccum @visited;
   reachableVertices = {};        # empty vertex set
   visitedVertices (ANY) = {seed};  # set that can contain ANY type of vertex
 
   WHILE visitedVertices.size() !=0 DO        # loop terminates when all neighbors are visited
       visitedVertices = SELECT s              # s is all neighbors of visitedVertices which have not been visited
               FROM visitedVertices-(:e)->:s
               WHERE s.@visited == false
               POST-ACCUM s.@visited = true;
       reachableVertices = reachableVertices UNION visitedVertices;
   END;
   PRINT reachableVertices;
}
reachable 的结果
GSQL > RUN QUERY reachable("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"reachableVertices": [
   {
     "v_id": "person3",
     "attributes": {
       "interestList": ["teaching"],
       "skillSet": [ 6, 1, 4 ],
       "skillList": [ 4, 1, 6 ],
       "locationId": "jp",
       "interestSet": ["teaching"],
       "@visited": true,
       "id": "person3"
     },
     "v_type": "person"
   },
   {
     "v_id": "person9",
     "attributes": {
       "interestList": [ "financial", "teaching" ],
       "skillSet": [ 2, 7, 4 ],
       "skillList": [ 4, 7, 2 ],
       "locationId": "us",
       "interestSet": [ "teaching", "financial" ],
       "@visited": true,
       "id": "person9"
     },
     "v_type": "person"
   },
   {
     "v_id": "person4",
     "attributes": {
       "interestList": ["football"],
       "skillSet": [ 10, 1, 4 ],
       "skillList": [ 4, 1, 10 ],
       "locationId": "us",
       "interestSet": ["football"],
       "@visited": true,
       "id": "person4"
     },
     "v_type": "person"
   },
   {
     "v_id": "person7",
     "attributes": {
       "interestList": [ "art", "sport" ],
       "skillSet": [ 6, 8 ],
       "skillList": [ 8, 6 ],
       "locationId": "us",
       "interestSet": [ "sport", "art" ],
       "@visited": true,
       "id": "person7"
     },
     "v_type": "person"
   },
   {
     "v_id": "person1",
     "attributes": {
       "interestList": [ "management", "financial" ],
       "skillSet": [ 3, 2, 1 ],
       "skillList": [ 1, 2, 3 ],
       "locationId": "us",
       "interestSet": [ "financial", "management" ],
       "@visited": true,
       "id": "person1"
     },
     "v_type": "person"
   },
   {
     "v_id": "person5",
     "attributes": {
       "interestList": [ "sport", "financial", "engineering" ],
       "skillSet": [ 5, 2, 8 ],
       "skillList": [ 8, 2, 5 ],
       "locationId": "can",
       "interestSet": [ "engineering", "financial", "sport" ],
       "@visited": true,
       "id": "person5"
     },
     "v_type": "person"
   },
   {
     "v_id": "person6",
     "attributes": {
       "interestList": [ "music", "art" ],
       "skillSet": [ 10, 7 ],
       "skillList": [ 7, 10 ],
       "locationId": "jp",
       "interestSet": [ "art", "music" ],
       "@visited": true,
       "id": "person6"
     },
     "v_type": "person"
   },
   {
     "v_id": "person2",
     "attributes": {
       "interestList": ["engineering"],
       "skillSet": [ 6, 5, 3, 2 ],
       "skillList": [ 2, 3, 5, 6 ],
       "locationId": "chn",
       "interestSet": ["engineering"],
       "@visited": true,
       "id": "person2"
     },
     "v_type": "person"
   },
   {
     "v_id": "person8",
     "attributes": {
       "interestList": ["management"],
       "skillSet": [ 2, 5, 1 ],
       "skillList": [ 1, 5, 2 ],
       "locationId": "chn",
       "interestSet": ["management"],
       "@visited": true,
       "id": "person8"
     },
     "v_type": "person"
   },
   {
     "v_id": "company3",
     "attributes": {
       "country": "jp",
       "@visited": true,
       "id": "company3"
     },
     "v_type": "company"
   },
   {
     "v_id": "company2",
     "attributes": {
       "country": "chn",
       "@visited": true,
       "id": "company2"
     },
     "v_type": "company"
   },
   {
     "v_id": "company1",
     "attributes": {
       "country": "us",
       "@visited": true,
       "id": "company1"
     },
     "v_type": "company"
   },
   {
     "v_id": "person10",
     "attributes": {
       "interestList": [ "football", "sport" ],
       "skillSet": [3],
       "skillList": [3],
       "locationId": "us",
       "interestSet": [ "sport", "football" ],
       "@visited": true,
       "id": "person10"
     },
     "v_type": "person"
   }
 ]}]
}
例2. WHILE 循环中使用 LIMIT子句
# find all vertices which are reachable within two hops from a starting seed vertex (i.e., breadth-first search)
CREATE QUERY reachableWithinTwo(vertex<person> seed) FOR GRAPH workNet
{
   OrAccum @visited;
   reachableVertices = {};        # empty vertex set
   visitedVertices (ANY) = {seed};  # set that can contain ANY type of vertex
 
   WHILE visitedVertices.size() !=0 LIMIT 2 DO # loop terminates when all neighbors within 2-hops of the seed vertex are visited
       visitedVertices = SELECT s               # s is all neighbors of visitedVertices which have not been visited
               FROM visitedVertices-(:e)->:s
               WHERE s.@visited == false
               POST-ACCUM s.@visited = true;
       reachableVertices = reachableVertices UNION visitedVertices;
   END;
   PRINT reachableVertices;
}
reachableWithinTwo 的结果
GSQL > RUN QUERY reachableWithinTwo("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"reachableVertices": [
   {
     "v_id": "person4",
     "attributes": {
       "interestList": ["football"],
       "skillSet": [ 10, 1, 4 ],
       "skillList": [ 4, 1, 10 ],
       "locationId": "us",
       "interestSet": ["football"],
       "@visited": true,
       "id": "person4"
     },
     "v_type": "person"
   },
   {
     "v_id": "person3",
     "attributes": {
       "interestList": ["teaching"],
       "skillSet": [ 6, 1, 4 ],
       "skillList": [ 4, 1, 6 ],
       "locationId": "jp",
       "interestSet": ["teaching"],
       "@visited": true,
       "id": "person3"
     },
     "v_type": "person"
   },
   {
     "v_id": "person9",
     "attributes": {
       "interestList": [ "financial", "teaching" ],
       "skillSet": [ 2, 7, 4 ],
       "skillList": [ 4, 7, 2 ],
       "locationId": "us",
       "interestSet": [ "teaching", "financial" ],
       "@visited": true,
       "id": "person9"
     },
     "v_type": "person"
   },
   {
     "v_id": "person5",
     "attributes": {
       "interestList": [ "sport", "financial", "engineering" ],
       "skillSet": [ 5, 2, 8 ],
       "skillList": [ 8, 2, 5 ],
       "locationId": "can",
       "interestSet": [ "engineering", "financial", "sport" ],
       "@visited": true,
       "id": "person5"
     },
     "v_type": "person"
   },
   {
     "v_id": "person6",
     "attributes": {
       "interestList": [ "music", "art" ],
       "skillSet": [ 10, 7 ],
       "skillList": [ 7, 10 ],
       "locationId": "jp",
       "interestSet": [ "art", "music" ],
       "@visited": true,
       "id": "person6"
     },
     "v_type": "person"
   },
   {
     "v_id": "person10",
     "attributes": {
       "interestList": [ "football", "sport" ],
       "skillSet": [3],
       "skillList": [3],
       "locationId": "us",
       "interestSet": [ "sport", "football" ],
       "@visited": true,
       "id": "person10"
     },
     "v_type": "person"
   },
   {
     "v_id": "person8",
     "attributes": {
       "interestList": ["management"],
       "skillSet": [ 2, 5, 1 ],
       "skillList": [ 1, 5, 2 ],
       "locationId": "chn",
       "interestSet": ["management"],
       "@visited": true,
       "id": "person8"
     },
     "v_type": "person"
   },
   {
     "v_id": "company1",
     "attributes": {
       "country": "us",
       "@visited": true,
       "id": "company1"
     },
     "v_type": "company"
   },
   {
     "v_id": "person2",
     "attributes": {
       "interestList": ["engineering"],
       "skillSet": [ 6, 5, 3, 2 ],
       "skillList": [ 2, 3, 5, 6 ],
       "locationId": "chn",
       "interestSet": ["engineering"],
       "@visited": true,
       "id": "person2"
     },
     "v_type": "person"
   },
   {
     "v_id": "company2",
     "attributes": {
       "country": "chn",
       "@visited": true,
       "id": "company2"
     },
     "v_type": "company"
   },
   {
     "v_id": "person7",
     "attributes": {
       "interestList": [ "art", "sport" ],
       "skillSet": [ 6, 8 ],
       "skillList": [ 8, 6 ],
       "locationId": "us",
       "interestSet": [ "sport", "art" ],
       "@visited": true,
       "id": "person7"
     },
     "v_type": "person"
   },
   {
     "v_id": "person1",
     "attributes": {
       "interestList": [ "management", "financial" ],
       "skillSet": [ 3, 2, 1 ],
       "skillList": [ 1, 2, 3 ],
       "locationId": "us",
       "interestSet": [ "financial", "management" ],
       "@visited": true,
       "id": "person1"
     },
     "v_type": "person"
   }
 ]}]
}

FOREACH语句

FOREACH语句用于执行带有条件约束的迭代循环。 FOREACH语句可以用于查询主体层语句或DML子句。 (请参阅有关块语法差异的说明。)

FOREACH 的语法
queryBodyForEachStmt := FOREACH  forEachControl  DO  queryBodyStmts  END
DMLSubForEachStmt := FOREACH  forEachControl  DO  DMLSubStmtList  END
forEachControl := (name | "(" name [, name]+ ")") IN setBagExpr
               | name IN RANGE "[" expr, expr"]" [".STEP(" expr ")"]

forEachControl的正式语法看起来很复杂,它可以分为以下几种情况:

  • name IN setBagExpr

  • tuple IN setBagExpr

  • name IN RANGE [expr,expr]

  • name IN RANGE [expr,expr] .STEP(expr)

请注意,setBagExpr包括了累加器和集合。

FOREACH语句具有以下限制:

  • 在DML子句中的FOREACH语句中,永远不允许更新循环变量值(即在IN之前声明的变量,例如,“FOREACH var INsetBagExpr”中的var)。

  • 在查询主体层FOREACH语句中,在大多数情况下,不允许更新循环变量。 但以下情况除外:

    • 如果在ListAccum累加器上迭代,则可以更新其值。

    • 如果在MapAccum累加器上迭代,则可以更新其值,但不能更新其键的键值。

  • 如果迭代位于一组顶点之上,则不允许访问(读取或写入)附属于顶点之上的累加器。

  • 在查询主体层语句中的FOREACH语句不允许循环迭代一个常量集合(包括Set或Bag)。 例如,不支持FOREACH i in (1,2,3)这样的操作。 但是,在DML子句上,这样的操作是允许的。

FOREACH ... IN RANGE的语句

用户可以选择在FOREACH语句中添加一个RANGE子句,格式为RANGE [expr,expr];用户可以用它定义迭代集合的范围。用户还能选定迭代的步进长度以控制范围。

RANGE [expr,expr] .STEP(expr)

每个expr的运算结果必须为整数。 可以是负整数,但Step expr 可能不为0。

RANGE [a,b] .STEP(c)子句会生成一个从a到b的整数序列,包括步长c。 即如下所示:

(a,a + c,a + 2 * c,a + 3 * c,... a + k * c),其中k =最大整数,使得| k * c | ≤| b-a |。

如果未给出.STEP参数,则步长c 默认为1。

带有MapAccum 累加器的嵌套FOREACH IN RANGE 语句
CREATE QUERY foreachRangeEx() FOR GRAPH socialNet {
 ListAccum<INT> @@t;
 Start = {person.*};
 FOREACH i IN RANGE[0, 2] DO
   @@t += i;
   L = SELECT Start
       FROM Start
       WHERE Start.id == "person1"
       ACCUM
         FOREACH j IN RANGE[0, i] DO
           @@t += j
         END
       ;
 END;
 PRINT @@t;
}
查询foreachRangeEx的结果
GSQL > RUN QUERY foreachRangeEx()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@t": [ 0, 0, 1, 0, 1, 2, 0, 1, 2 ]}]
}
带有步长的FOREACH IN RANGE 语句
CREATE QUERY foreachRangeStep(INT a, INT b, INT c) FOR GRAPH minimalNet {
 ListAccum<INT> @@t;
 FOREACH i IN RANGE[a,b].step(c) DO
   @@t += i;
 END;
 PRINT @@t;
}

步长值为正表示升序,为负表示降序。 如果循环的计数方向设置错误,则该循环不会进行,因为一开始循环的退出条件就已经满足了。

oreachRangeStep.json 的结果
GSQL > RUN QUERY foreachRangeStep(100,0,-9)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@t": [
   100,
   91,
   82,
   73,
   64,
   55,
   46,
   37,
   28,
   19,
   10,
   1
 ]}]
}
GSQL > RUN QUERY foreachRangeStep(-100,100,-9)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@t": []}]
}

查询主体级(Query-body-level)语句中的FOREACH示例

例 1 – 带有ListAccum累加器的FOREACH语句
# Count the number of companies whose country matches the provided string
CREATE QUERY companyCount(STRING countryName) FOR GRAPH workNet {
 ListAccum<STRING> @@companyList;
 INT countryCount;
 start = {ANY};                        # start will have a set of all vertex types
 
 s = SELECT v FROM start:v             # get all vertices
     WHERE v.type == "company"         # that have a type of "company"
     ACCUM @@companyList += v.country; # append the country attribute from all company vertices to the ListAccum
 
 # Iterate the ListAccum and compare each element to the countryName parameter
 FOREACH item in @@companyList DO
   IF item == countryName THEN
      countryCount = countryCount + 1;
   END;
 END;
 PRINT countryCount;
}
companyCount 的结果
GSQL > RUN QUERY companyCount("us")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"countryCount": 2}]
}
GSQL > RUN QUERY companyCount("can")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"countryCount": 1}]
}
例2 – 带有种子集的FOREACH 语句
#Find all company person who live in a given country
CREATE QUERY employeesByCompany(STRING country) FOR GRAPH workNet {
 ListAccum<VERTEX<company>> @@companyList;
 start = {ANY};
 
 # Build a list of all company vertices
 #  (these are vertex IDs only)
 s = SELECT v FROM start:v
     WHERE v.type == "company"
     ACCUM @@companyList += v;
 
 # Use the vertex IDs as Seeds for vertex sets
 FOREACH item IN @@companyList DO
   companyItem = {item};
   employees = SELECT t FROM companyItem -(worksFor)-> :t
               WHERE (t.locationId == country);
   PRINT employees;
 END;
}
employeesByCompany 结果
GSQL > RUN QUERY employeesByCompany("us")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [ {"employees": []},
   {"employees": []},
   {"employees": [
     {
       "v_id": "person9",
       "attributes": {
         "interestList": [
           "financial",
           "teaching"
         ],
         "skillSet": [ 2, 7, 4 ],
         "skillList": [ 4, 7, 2 ],
         "locationId": "us",
         "interestSet": [ "teaching", "financial" ],
         "id": "person9"
       },
       "v_type": "person"
     },
     {
       "v_id": "person10",
       "attributes": {
         "interestList": [ "football", "sport" ],
         "skillSet": [3],
         "skillList": [3],
         "locationId": "us",
         "interestSet": [ "sport", "football" ],
         "id": "person10"
       },
       "v_type": "person"
     },
     {
       "v_id": "person7",
       "attributes": {
         "interestList": [ "art", "sport" ],
         "skillSet": [ 6, 8 ],
         "skillList": [ 8, 6 ],
         "locationId": "us",
         "interestSet": [ "sport", "art" ],
         "id": "person7"
       },
       "v_type": "person"
     }
   ]},
   {"employees": [
     {
       "v_id": "person4",
       "attributes": {
         "interestList": ["football"],
         "skillSet": [ 10, 1, 4 ],
         "skillList": [ 4, 1, 10 ],
         "locationId": "us",
         "interestSet": ["football"],
         "id": "person4"
       },
       "v_type": "person"
     },
     {
       "v_id": "person9",
       "attributes": {
         "interestList": [ "financial", "teaching" ],
         "skillSet": [ 2, 7, 4 ],
         "skillList": [ 4, 7, 2 ],
         "locationId": "us",
         "interestSet": [ "teaching", "financial" ],
         "id": "person9"
       },
       "v_type": "person"
     },
     {
       "v_id": "person7",
       "attributes": {
         "interestList": [ "art", "sport" ],
         "skillSet": [ 6, 8 ],
         "skillList": [ 8, 6 ],
         "locationId": "us",
         "interestSet": [ "sport", "art" ],
         "id": "person7"
       },
       "v_type": "person"
     },
     {
       "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"
     }
   ]},
   {"employees": [
     {
       "v_id": "person10",
       "attributes": {
         "interestList": [
           "football",
           "sport"
         ],
         "skillSet": [3],
         "skillList": [3],
         "locationId": "us",
         "interestSet": [ "sport", "football" ],
         "id": "person10"
       },
       "v_type": "person"
     },
     {
       "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"
     }
   ]}
 ]
}
例 3 – 带有MapAccum累加器的嵌套FOREACH语句
# Count the number of employees from a given country and list their ids
CREATE QUERY employeeByCountry(STRING countryName) FOR GRAPH workNet {
 MapAccum <STRING, ListAccum<STRING>> @@employees;
 
 # start will have a set of all person type vertices
 start = {person.*};
 
 # Build a map using person locationId as a key and a list of strings to hold multiple person ids
 s = SELECT v FROM start:v
     ACCUM @@employees += (v.locationId -> v.id);
 
 # Iterate the map using (key,value) pairs
 FOREACH (key,val) in @@employees DO
   IF key == countryName THEN
     PRINT val.size();
 
     # Nested foreach to iterate over the list of person ids
     FOREACH employee in val DO
       PRINT employee;
     END;
 
     # MapAccum keys are unique so we can BREAK out of the loop
     BREAK;
   END;
 END;
}
employeeByCountry 的结果
GSQL > RUN QUERY employeeByCountry("us")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {"val.size()": 5},
   {"employee": "person4"},
   {"employee": "person10"},
   {"employee": "person7"},
   {"employee": "person1"},
   {"employee": "person9"}
 ]
}

DML子句中的FOREACH 示例

ACCUM FOREACH的示例
# Show post topics liked by users and show total likes per topic
CREATE QUERY topicLikes() FOR GRAPH socialNet {
SetAccum<STRING> @@personPosts; 
SumAccum<INT> @postLikes; 
MapAccum<STRING,INT> @@likesByTopic; 
 
 
 start = {person.*};
 
 # Find all user posts and generate a set of post topics
 # (set has no duplicates)
 posts = SELECT g FROM start - (posted) -> :g
         ACCUM @@personPosts += g.subject;
 
 # Use set of topics to increment how many times a specfic
 #  post is liked by other users
 likedPosts = SELECT f FROM start - (liked) -> :f
              ACCUM FOREACH x in @@personPosts DO
                        CASE WHEN (f.subject == x) THEN
                          f.@postLikes += 1
                        END
                    END
              # Aggregate all liked totals by topic
              POST-ACCUM @@likesByTopic += (f.subject -> f.@postLikes);
 
 # Display the number of likes per topic
 PRINT @@likesByTopic;
}
查询topicLikes的结果
GSQL > RUN QUERY topicLikes()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@likesByTopic": {
   "cats": 3,
   "coffee": 2,
   "Graphs": 3,
   "tigergraph": 1
 }}]
}
例1 - POST-ACCUM FOREACH的示例
#Show a summary of the number of friends all persons have by gender
CREATE QUERY friendGender() FOR GRAPH socialNet {
 ListAccum<STRING> @friendGender;
 SumAccum<INT> @@maleGenderCount;
 SumAccum<INT> @@femaleGenderCount;
 
 start = {person.*};
 
 # Record a list showing each friend's gender
 socialMembers = SELECT s from start:s -(friend)-> :g
             ACCUM s.@friendGender += (g.gender)
 
             # Loop over each list of genders and total them
             POST-ACCUM FOREACH x in s.@friendGender DO
​                       CASE WHEN (x == "Male") THEN
​                         @@maleGenderCount += 1
​                       ELSE
​                         @@femaleGenderCount += 1
​                       END
                        END;
 
 PRINT @@maleGenderCount;
 PRINT @@femaleGenderCount;
}
查询friendGender的结果
GSQL > RUN QUERY friendGender()
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {"@@maleGenderCount": 11},
   {"@@femaleGenderCount": 7}
 ]
}

CONTINUE子句和BREAK子句

CONTINUE子句和BREAK子句只能在WHILE或FOREACH语句块中使用。 CONTINUE子句跳过当前循环中的剩余的所有语句,直接前往循环的末尾,然后继续下一次循环。 也就是说,运算过程会跳过CONTINUE子句后的所有内容,但循环仍然继续正常进行。

BREAK子句使得运算过程退出循环,也就是说,执行过程会退出当前的循环并停止迭代。

以下是一些演示BREAK和CONTINUE使用方法的示例。

Continue 和Break 的示例
# While with a continue
INT i = 0;
INT nCount = 0;
WHILE i < 10 DO
 i = i + 1;
 IF (i % 2 == 0) { CONTINUE; }
 nCount = nCount + 1;
END;
# i is 10, nCount is 5 (skips the increment for every even i).
 
# While with a break
i = 0;
WHILE i < 10 DO
 IF (i == 5) { BREAK; }  # When i is 5 the loop is exited
 i = i + 1;
END;
# i is now 5
例1. Break
# find posts of a given person, and post of friends of that person, friends of friends, etc
# until a post about cats is found. The number of friend-hops to reach is the 'degree' of cats
CREATE QUERY findDegreeOfCats(vertex<person> seed) FOR GRAPH socialNet
{
   SumAccum<INT> @@degree = 0;
   OrAccum @@foundCatPost = false;
   OrAccum @visited = false;
 
   friends (ANY) = {seed};
   WHILE @@foundCatPost != true AND friends.size() > 0 DO
         posts = SELECT v FROM friends-(posted:e)->:v
                 ACCUM CASE WHEN v.subject == "cats" THEN @@foundCatPost += true END;
 
         IF @@foundCatPost THEN
           BREAK;
         END;
 
         friends = SELECT v FROM friends-(friend:e)->:v
                 WHERE v.@visited == false
                 ACCUM v.@visited = true;
         @@degree += 1;
   END;
   PRINT @@degree;
}
findDegreeOfCats的结果
GSQL > RUN QUERY findDegreeOfCats("person2")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@degree": 2}]
}
GSQL > RUN QUERY findDegreeOfCats("person4")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@degree": 0}]
}
例2. findEnoughFriends.gsql:带有CONTINUE语句的WHILE循环
# find all 3-hop friends of a starting vertex. count coworkers as friends
# if there are not enough friends
CREATE QUERY findEnoughFriends(vertex<person> seed) FOR GRAPH friendNet
{
   SumAccum<INT> @@distance = 0;   # keep track of the distance from the seed
   OrAccum @visited = false;
   visitedVertices = {seed};
   WHILE true LIMIT 3 DO
       @@distance += 1;
       # traverse from visitedVertices to its friends
       friends = SELECT v
           FROM visitedVertices -(friend:e)-> :v
           WHERE v.@visited == false
           POST-ACCUM v.@visited = true;
       PRINT @@distance, friends;
 
       # if number of friends at this level is sufficient, finish this iteration
       IF visitedVertices.size() >= 2 THEN
           visitedVertices = friends;
           CONTINUE;
       END;
       # if fewer than 4 friends, add in coworkers
       coworkers = SELECT v
           FROM visitedVertices -(coworker:e)-> :v
           WHERE v.@visited == false
           POST-ACCUM v.@visited = true;
       visitedVertices = friends UNION coworkers;
       PRINT @@distance, coworkers;
   END;
}
例2 findEnoughFriends的结果
GSQL > RUN QUERY findEnoughFriends("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {
     "@@distance": 1,
     "friends": [
       {
         "v_id": "person4",
         "attributes": {
           "@visited": true,
           "id": "person4"
         },
         "v_type": "person"
       },
       {
         "v_id": "person2",
         "attributes": {
           "@visited": true,
           "id": "person2"
         },
         "v_type": "person"
       },
       {
         "v_id": "person3",
         "attributes": {
           "@visited": true,
           "id": "person3"
         },
         "v_type": "person"
       }
     ]
   },
   {
     "coworkers": [
       {
         "v_id": "person5",
         "attributes": {
           "@visited": true,
           "id": "person5"
         },
         "v_type": "person"
       },
       {
         "v_id": "person6",
         "attributes": {
           "@visited": true,
           "id": "person6"
         },
         "v_type": "person"
       }
     ],
     "@@distance": 1
   },
   {
     "@@distance": 2,
     "friends": [
       {
         "v_id": "person9",
         "attributes": {
           "@visited": true,
           "id": "person9"
         },
         "v_type": "person"
       },
       {
         "v_id": "person1",
         "attributes": {
           "@visited": true,
           "id": "person1"
         },
         "v_type": "person"
       },
       {
         "v_id": "person8",
         "attributes": {
           "@visited": true,
           "id": "person8"
         },
         "v_type": "person"
       }
     ]
   },
   {
     "@@distance": 3,
     "friends": [
       {
         "v_id": "person12",
         "attributes": {
           "@visited": true,
           "id": "person12"
         },
         "v_type": "person"
       },
       {
         "v_id": "person10",
         "attributes": {
           "@visited": true,
           "id": "person10"
         },
         "v_type": "person"
       },
       {
         "v_id": "person7",
         "attributes": {
           "@visited": true,
           "id": "person7"
         },
         "v_type": "person"
       }
     ]
   }
 ]
}
例3. 带有BREAK语句的WHILE循环
# find at least the top-k companies closest to a given seed vertex, if they exist
CREATE QUERY topkCompanies(vertex<person> seed, INT k) FOR GRAPH workNet
{
   SetAccum<vertex<company>> @@companyList;
   OrAccum @visited = false;
   visitedVertices (ANY) = {seed};
   WHILE true DO
       visitedVertices = SELECT v                  # traverse from x to its unvisited neighbors
               FROM visitedVertices -(:e)-> :v
               WHERE v.@visited == false
               ACCUM CASE
                   WHEN (v.type == "company") THEN # count the number of company vertices encountered
                       @@companyList += v
                   END
               POST-ACCUM v.@visited += true;      # mark vertices as visited
 
       # exit loop when at least k companies have been counted
       IF @@companyList.size() >= k OR visitedVertices.size() == 0 THEN
          BREAK;
       END;
   END;
   PRINT @@companyList;
}
例 3. topkCompanies 的结果
GSQL > RUN QUERY topkCompanies("person1", 2)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@companyList": [
   "company2",
   "company1"
 ]}]
}
GSQL > RUN QUERY topkCompanies("person2", 3)
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"@@companyList": [
   "company3",
   "company2",
   "company1"
 ]}]
}
例4 - 在 FOREACH中使用CONTINUE子句
#List out all companies from a given country
CREATE QUERY companyByCountry(STRING countryName) FOR GRAPH workNet {
 MapAccum <STRING, ListAccum<STRING>> @@companies;
 start = {company.*};                   # start will have a set of all company type vertices
 
 #Build a map using company country as a key and a list of strings to hold multiple company ids
 s = SELECT v FROM start:v
     ACCUM @@companies += (v.country -> v.id);
 
 #Iterate the map using (key,value) pairs
 FOREACH (key,val) IN @@companies DO
   IF key != countryName THEN
     CONTINUE;
   END;
 
   PRINT val.size();
 
   #Nested foreach to iterate over the list of company ids
   FOREACH comp IN val DO
     PRINT comp;
   END;
 END;
}
companyByCountry 的结果
GSQL > RUN QUERY companyByCountry("us")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {"val.size()": 2},
   {"comp": "company1"},
   {"comp": "company4"}
 ]
}
例5 -在 FOREACH中使用BREAK子句
#List all the persons located in the specified country
CREATE QUERY employmentByCountry(STRING countryName) FOR GRAPH workNet {
 MapAccum < STRING, ListAccum<STRING> > @@employees;
 start = {person.*};                   # start will have a set of all person type vertices
 
 #Build a map using person locationId as a key and a list of strings to hold multiple person ids
 s = SELECT v FROM start:v
     ACCUM @@employees += (v.locationId -> v.id);
 
 #Iterate the map using (key,value) pairs
 FOREACH (key,val) IN @@employees DO
   IF key == countryName THEN
     PRINT val.size();
 
     #Nested foreach to iterate over the list of person ids
     FOREACH employee IN val DO
       PRINT employee;
     END;
 
     BREAK;
   END;
 END;
}
employmentByCountry 的结果
GSQL > RUN QUERY employmentByCountry("us")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {"val.size()": 5},
   {"employee": "person1"},
   {"employee": "person4"},
   {"employee": "person7"},
   {"employee": "person9"},
   {"employee": "person10"}
 ]
}

Last updated