使用参数化GSQL查询进行查询

我们刚刚看到运行简单的内置查询是多么简单和快捷。然而,您无疑希望创建自定义或更复杂的查询。GSQL通过参数化的顶点集查询将最大的权力交给您。参数化查询允许您遍历从一个顶点到相邻的顶点集的图,一次又一次地执行计算过程,内置并行执行和方便的聚合操作。您甚至可以让一个查询调用另一个查询。我们从简单的学起。

写一个GSQL参数化查询有三个步骤。

  1. 在GSQL中定义查询。这个查询将被添加到GSQL目录中。

  2. 在目录中安装一个或多个查询,为每个查询生成一个REST端点。

  3. 运行已安装的查询,提供适当的参数,可以调用GSQL命令,也可以通过向REST端点发送HTTP请求。

一个简单的1-Hop查询

现在,让我们编写第一个GSQL查询。我们将显示作为输入参数的人的所有直接(1-hop)邻居。

GSQL command
USE GRAPH social
CREATE QUERY hello(VERTEX<person> p) FOR GRAPH social{
 Start = {p};
 Result = SELECT tgt
          FROM Start:s-(friendship:e) ->person:tgt;
 PRINT Result;
}

此查询具有一个SELECT语句。这里的SELECT语句比内置查询中的语句强大得多。在这里,您可以执行以下操作:查询开始时,通过从查询调用传入的参数p所标识的person顶点,将一个顶点集“Start”设置为“Start”。大括号告诉GSQL构造一个包含所包含项目的集合。

接下来,SELECT语句根据FROM子句中描述的模式描述了1跳遍历:

Start:s -(friendship:e)- >person:tgt

这基本上与我们用于内置select edges查询的语法相同。也就是说,我们从给定的源集(Start)中选择所有的边,这些边具有给定的边类型(friendship),并以给定的顶点类型(person)结束。我们以前没有见过的一个特性是使用“:alias”: “s”是顶点集别名,“e" 是边集别名,“tgt”是目标顶点集别名。

返回到初始子句和赋值(“Result = SELECT tgt”)。这里我们看到目标集的别名tgt。这意味着SELECT语句应该返回目标顶点集(通过SELECT查询块中的完整子句集进行过滤和处理),并将该输出集分配给名为Result的变量。

最后,我们打印出JSON格式的结果顶点集。

创建一个查询

与其在交互模式中定义查询,不如将查询存储在一个文件中,并使用@filename语法从GSQL shell中调用该文件。将上面的查询复制并粘贴到文件/home/tigergraph/hello.gsql中。然后,进入GSQL shell并使用@hello调用该文件(注意,如果您在开始gsql时没有在/home/tigergraph文件夹中,那么您可以使用绝对路径来调用gsql文件。例如@/home/tigergraph/hello.gsql)。然后运行“ls”命令,查看新加的查询现在在目录中了。

GSQL shell
GSQL > @hello.gsql
Using graph 'social'
The query hello has been added!
GSQL > ls
---- Graph social
Vertex Types:
 - VERTEX person(PRIMARY_ID name STRING, name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE"
Edge Types:
 - UNDIRECTED EDGE friendship(from person, to person, connect_day DATETIME)
 
Graphs:
 - Graph social(person:v, friendship:e)
Jobs:
 - CREATE LOADING JOB load_social FOR GRAPH social {
     DEFINE FILENAME file2 = "/home/tigergraph/friendship.csv";
     DEFINE FILENAME file1 = "/home/tigergraph/person.csv";
 
     LOAD file1 TO VERTEX person VALUES($"name", $"name", $"age", $"gender", $"state") USING SEPARATOR=",", HEADER="true", EOL="\n";
     LOAD file2 TO EDGE friendship VALUES($0, $1, $2) USING SEPARATOR=",", HEADER="true", EOL="\n";
   }
   Queries:
 - hello(vertex<person> p)

安装一个查询

但是,查询尚未安装;它还不能运行。在GSQL shell中,输入以下命令来安装刚刚添加的查询“hello”:

GSQL command
INSTALL QUERY hello
GSQL shell
GSQL > INSTALL QUERY hello
Start installing queries, about 1 minute ...
hello query: curl -X GET 'http://127.0.0.1:9000/query/social/hello?p=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled.
 
[========================================================================================================] 100% (1/1)

数据库安装这个新查询大约需要1分钟。要有耐心!

对于大型数据集上的查询,这种小的投资可以在更快的查询执行中获得多次回报,尤其是在使用不同参数多次运行查询时。安装将生成机器指令和一个REST端点。当进度条达到100%后,我们就可以运行这个查询了。

在GSQL中运行查询

要在GSQL中运行查询,请使用“run query”,后跟查询名和一组参数值:

GSQL command - run query examples
RUN QUERY hello("Tom")

结果以JSON格式显示。Tom有两个一步邻居,即Dan和Jenny。

GSQL shell
GSQL > RUN QUERY hello("Tom")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [{"Result": [
   {
     "v_id": "Dan",
     "attributes": {
       "gender": "male",
       "name": "Dan",
       "state": "ny",
       "age": 34
     },
     "v_type": "person"
   },
   {
     "v_id": "Jenny",
     "attributes": {
       "gender": "female",
       "name": "Jenny",
       "state": "tx",
       "age": 25
     },
     "v_type": "person"
   }
 ]}]
}

将查询作为REST端点运行

在后台,安装一个查询还会生成一个REST端点,这样就可以通过http调用来调用参数化查询。在Linux中,curl命令是提交http请求的最流行的方式。在下面的示例中,所有查询的标准部分用粗体显示;正常权重中的部分属于这个特定的查询和参数值。JSON结果将返回到Linux shell的标准输出。因此,我们的参数化查询变成了http服务!

Linux shell
curl -X GET 'http://localhost:9000/query/social/hello?p=Tom'

最后,要查看目录中查询的GSQL文本,可以使用:

GSQL command - show query example
#SHOW QUERY query_name. E.g.
SHOW QUERY hello

恭喜你! 至此,您已经完成了定义、安装和运行查询的整个过程。

一个更高级的查询

现在,让我们执行一个更高级的查询。这一次,我们将学习如何使用强大的内置累加器,它充当在图中遍历过程中访问的每个顶点的运行时属性。运行时意味着它们只在查询运行时存在;它们被称为累加器,因为它们是专门用来在查询的隐式并行处理过程中收集(积累)数据的。

GSQL command file - hello2.gsql
USE GRAPH social
CREATE QUERY hello2 (VERTEX<person> p) FOR GRAPH social{
 OrAccum  @visited = false;
 AvgAccum @@avgAge;
 Start = {p};
 
 FirstNeighbors = SELECT tgt
                  FROM Start:s -(friendship:e)-> person:tgt
                  ACCUM tgt.@visited += true, s.@visited += true;
 
 SecondNeighbors = SELECT tgt
                   FROM FirstNeighbors -(:e)-> :tgt
                   WHERE tgt.@visited == false
                   POST_ACCUM @@avgAge += tgt.age;
 
 PRINT SecondNeighbors;
 PRINT @@avgAge;
}
INSTALL QUERY hello2
RUN QUERY hello2("Tom")

在这个查询中,我们将找到所有与参数化输入人员两步距离的人。为了好玩,我们来计算一下这些2跳邻居的平均年龄。

在这种图遍历算法的标准方法中,您使用一个布尔变量来标记算法“访问”一个顶点的第一次,这样它就知道不再计算它了。为了满足这一需求,我们将定义一个OrAccum类型的本地累加器。要声明本地累加器,我们在标识符名称前面加上一个“@”符号。每个累加器类型都有一个默认的初始值;布尔累加器的默认值为false。可以选择指定初始值。

我们还需要计算一个平均值,因此我们将定义一个全局AvgAccum。全局累加器的标识符以两个“@”开头。

定义了开始集之后,我们就有了第一个单跳遍历。SELECT和FROM子句与第一个示例相同,但是还有一个附加的ACCUM子句。ACCUM子句中的+=运算符意味着,对于匹配FROM子句模式的每条边,我们将右手边的表达式(true)累加到左手边的累加器(tgt.@visited以及s.@visited)。注意,可能会多次访问源顶点或目标顶点。参照图1,如果我们从顶点Tom开始,会有两个边事件发生,所以第一个SELECT语句中的ACCUM子句会访问Tom两次。由于累加器类型是OrAccum,两遍历的累积效应如下:

Tom.@visited <==(initial value:false)OR (true)OR(true)

请注意,先处理两条边中的哪一条并不重要,因此此操作适合多线程并行处理。最终的结果是,只要访问一个顶点至少一次,它就会以@visited = true结束。第一个SELECT语句的结果被分配给变量FirstNeighbour。

第二个SELECT块将进一步执行一跳,从FirstNeighbor顶点集变量开始,并到达2跳的邻居。注意,这一次,我们在FROM子句中省略了edge类型friendship和目标顶点类型person,但是保留了别名。如果没有提到别名的类型,则将其解释为所有类型。由于我们的图只有一个顶点类型和一个边缘类型,所以逻辑上它与我们指定的类型相同。WHERE子句过滤掉之前被标记为已访问的顶点(1跳邻居和起始顶点p)。这个SELECT语句使用POST_ACCUM而不是ACCUM,原因是POST_ACCUM会遍历顶点集而不是边集,保证我们不会重复计算任何顶点。在这里,我们计算2跳邻居的年龄,得到他们的平均年龄。

最后,p的第二个邻居被打印出来。

这次,我们将以下所有GSQL命令放入一个文件hello2.gsql:

  • USE GRAPH social

  • 查询定义

  • 安装查询

  • 运行查询

我们可以在不输入GSQL shell的情况下执行这一完整的命令集。请将上面的GSQL命令复制并粘贴到一个名为/home/tigergraph/hello2.gsql的Linux文件中。

在Linux shell中,在/home/tigergraph下,输入以下内容:

Linux shell
gsql hello2.gsql

结果如下所示。

查询方法总结

  • 查询被安装在目录中,可以有一个或多个输入参数,从而可以重用查询

  • GSQL查询由一系列SELECT查询块组成,每个查询块生成一个指定的顶点集

  • 每个SELECT查询块都可以开始从前面定义的任何顶点集遍历图形(也就是说,序列不必形成一个线性链)

  • 累加器是具有内置累加操作的运行时变量,用于高效的多线程计算

  • 输出是JSON格式。

Last updated