数据类型

本章介绍GSQL图形查询语言原生支持的数据类型。 在查询中,大多数数据对象都是以下三者之一:

  1. 查询的输入参数

  2. 遍历图形时遇到的顶点,边和它们的属性

  3. 在查询中定义的变量, 它们用于协助查询的计算工作。

本节介绍了语法定义的EBNF范式:

EBNF范式的数据类型
lowercase          := [a-z]
uppercase          := [A-Z]
letter             := lowercase | uppercase
digit              := [0-9]
integer            := ["-"]digit+
real               := ["-"]("." digit+) | ["-"](digit+ "." digit*)
numeric            := integer | real
stringLiteral      := '"' [~["] | '\\' ('"' | '\\')]* '"'
 
name := (letter | "_") [letter | digit | "_"]*   // Can be a single "_" or start with "_"
 
type := baseType | name | accumType | STRING COMPRESS
 
baseType := INT
         | UINT
         | FLOAT
         | DOUBLE
         | STRING
         | BOOL
         | VERTEX ["<" name ">"]
         | EDGE
         | JSONOBJECT
         | JSONARRAY
         | DATETIME
 
filePath := name | stringLiteral
 
typedef := TYPEDEF TUPLE "<" tupleType ">" name
 
tupleType := (baseType name) | (name baseType) ["," (baseType name) | (name baseType)]*
 
parameterType := baseType
              | [ SET | BAG ] "<" baseType ">"
              | FILE

标识符

标识符语言元素实例的名字。 在GSQL图形查询语言中,标识符用于命名元素,例如查询,变量或用户定义函数。 在EBNF范式中,标识符称为名称(name)。 它可以是一系列字母,数字或下划线(“_”)但不支持其他标点符号。 初始字符只能是字母或下划线。

name (标识符)
name := (letter | "_") [letter | digit | "_"]*

类型的总览

不同类型的数据可以在不同的上下文中使用。 EBNF范式定义了几种数据类型。 最基本的称为基本类(baseType)。 其他独立的类型包括文件(FILE)和压缩字符串。 剩下的类型是由独立数据类型构建的复合数据类型,或其他类型的超集。 下面的表格详述了他们的定义和用途。

基本类

本查询语言支持以下基本类,它们可以在其范围内的任何位置声明或复制。 在定义全局变量,局部变量,查询返回值,参数,元组的一部分或容器累加器的元素时,可以使用任何这些基本类。 累加器则将在后续的章节中详细介绍。

BNF范式
baseType := INT
         | UINT
         | FLOAT
         | DOUBLE
         | STRING
         | BOOL
         | VERTEX ["<" name ">"]
         | EDGE
         | JSONOBJECT
         | JSONARRAY
         | DATETIME

每种基本类的默认值如下表所示。 默认值是基本类变量的初始值(详见“变量类型”一章),或某些函数的默认返回值(详见“运算符,函数和表达式”一章)。

前七种类型(INT,UINT,FLOAT,DOUBLE,BOOL,STRING和DATETIME)与GSQL语言参考-第1部分的“属性数据类型”一章相同。

FLOAT和DOUBLE输入值必须采用固定小数点的d.dddd格式,其中d是数字。 输出值要么也以这种格式打印,要么以指数表示法更紧凑地表示。

GSQL加载器可以用指数表示法读取FLOAT和DOUBLE值(例如,1.25 E-7)。

顶点和边

顶点(VERTEX)和边(EDGE)是构成图形的两种最基本的对象类型。 查询的参数或变量可以被声明为这两种类型的其中之一。 此外图形数据库Schema也用于定义特定的顶点和边类型(例如CREATE VERTEX person )。 若用户想要限制参数或变量的类型,可以在关键字VERTEX/EDGE之后的尖括号<>中给出顶点或边的类型。 若该顶点或边的类没有被声明,则该VERTEX或EDGE变量称为泛类(generic type)。 下面是泛类和指定类型的顶点和边变量的声明示例:

例:声明顶点和边(泛类和指定类)
VERTEX anyVertex;
VERTEX<person> owner;
EDGE anyEdge;
EDGE<friendship> friendEdge;

顶点和边的属性类

下表将数据定义语言(DDL)中的顶点或边的属性类映射到GSQL图形查询语言类中。 有关累加器的信息可以在“累加器”章节找到。

JSONOBJECT 和JSONARRAY

这两种基本类允许用户传递复杂的数据对象或以自定义格式输出。 这些类遵循www.json.org上有关JSON的业界标准。JSONOBJECT实例的外部表现形式(作为输入和输出)是一个字符串,以花括号“{”和“}”开头和结尾,并包含一系列没有顺序的键值对。 JSONARRAY也是一个字符串,以方括号“[”和“]”开头和结尾。它包含一系列有先后顺序的值。 由于这些值可能是对象或数组,因此JSON支持分层的嵌套数据结构。

更多细节将在标题为“JSONOBJECT和JSONARRAY函数”的部分中介绍。

一个JSONOBJECT或JSONARRAY值是不可变的。任何用户都无法修改其值。

元组

元组是一种用户自定义数据结构,由固定顺序的基本类变量组成。 用户可以使用TYPEDEF语句创建和命名元组类型。元组 必须在查询中的任何其他语句之前被首先定义。

元组的EBNF范式
typedef := TYPEDEF TUPLE "<" tupleType ">" name
 
tupleType := (baseType name) | (name baseType) ["," (baseType name) | (name baseType)]*

元组也可以在图形数据库Schema中定义,随后便可以用作顶点或边的属性类。 在图形数据库Schema中定义的元组类不需要在查询中重新定义。

图形数据库SchemainvestmentNet包含两个复杂的属性:

  • 用户自定义元组SECRET_INFO,用于顶点person中的secret_info属性。

  • portfolio MAP <STRING,DOUBLE>属性,也在person顶点中。

investmentNet 图形数据库Schema
TYPEDEF TUPLE <age UINT (4), mothersName STRING(20) > SECRET_INFO
CREATE VERTEX person(PRIMARY_ID personId STRING, portfolio MAP<STRING, DOUBLE>, secretInfo SECRET_INFO)
CREATE VERTEX stockOrder(PRIMARY_ID orderId STRING, ticker STRING, orderSize UINT, price FLOAT)
CREATE UNDIRECTED EDGE makeOrder(FROM person, TO stockOrder, orderTime DATETIME)
CREATE GRAPH investmentNet (*)

下例中的查询会读取SECRET_INFO元组和portfolio MAP。 该元组类型不需要重新定义SECRET_INFO。 要读取和保存映射,我们可以使用相同的键值对定义MapAccum作为最初的portfolio映射。 (“累加器”一章详述了有关累加器的更多信息。)此外,该查询还会创建一个新的元组类型ORDER_RECORD。

tupleEx 查询
CREATE QUERY tupleEx(VERTEX<person> p) FOR GRAPH investmentNet{
 #TYPEDEF TUPLE <UINT age, STRING mothersName> SECRET_INFO;       # already defined in schema
 TYPEDEF TUPLE <STRING ticker, FLOAT price, DATETIME orderTime> ORDER_RECORD; # new for query
 
 SetAccum<SECRET_INFO> @@info;
 ListAccum<ORDER_RECORD> @@orderRecords;
 MapAccum<STRING, DOUBLE> @@portf;       # corresponds to MAP<STRING, DOUBLE> attribute
 
 INIT = {p};
 
 # Get person p's secret_info and portfolio
 X = SELECT v FROM INIT:v
     ACCUM @@portf += v.portfolio, @@info += v.secretInfo;
 
 # Search person p's orders to record ticker, price, and order time.
 # Note that the tuple gathers info from both edges and vertices.
 orders = SELECT t
     FROM INIT:s -(makeOrder:e)->stockOrder:t
     ACCUM @@orderRecords += ORDER_RECORD(t.ticker, t.price, e.orderTime);
 
 PRINT @@portf, @@info;
 PRINT @@orderRecords;
}
tupleEx.json
GSQL > RUN QUERY tupleEx("person1")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {
     "@@info": [{
       "mothersName": "JAMES",
       "age": 25
     }],
     "@@portf": {
       "AAPL": 3142.24,
       "MS": 5000,
       "G": 6112.23
     }
   },
   {"@@orderRecords": [
     {
       "ticker": "AAPL",
       "orderTime": "2017-03-03 18:42:28",
       "price": 34.42
     },
     {
       "ticker": "B",
       "orderTime": "2017-03-03 18:42:30",
       "price": 202.32001
     },
     {
       "ticker": "A",
       "orderTime": "2017-03-03 18:42:29",
       "price": 50.55
     }
   ]}
 ]
}

压缩字符串

压缩字符串是由系统编码的整数类,用于表示字符串的值。 压缩字符串比未压缩字符串使用更少的内存。 压缩字符串类的设计类似于未压缩字符串:数据作为字符串加载和打印,大多数采用未压缩字符串输入的函数和运算符也可以使用压缩字符串输入。唯一的不同之处在于数据如何在内部存储。用户可以从STRING_SET COMPRESS或STRING_LIST COMPRESS属性中获取压缩字符串值,也可以将未压缩字符串值转换为压缩字符串值。

当一系列字符串值中有多个相同值的时候,压缩字符串便能发挥优势。实际使用中,压缩字符串最适用于容器累加器,如ListAccum < STRING COMPRESS >或SetAccum < STRING COMPRESS >。

当一个包含压缩字符串的累加器(在“累加器”一节中详述)被赋予一个属性值,或它来自另一个包含压缩字符串的累加器时,它会以字典方式存储数据。一个包含压缩字符串的累加器可以存储多个字典。但仅当未压缩字符串值已被存储在字典中时,才能将未压缩字符串值转换为压缩字符串值。如果未压缩字符串值不在字典中,则原始字符串值被保留。 反之,一个压缩字符串值可以自动转换为未压缩字符串值。

当输出压缩字符串值时(例如通过PRINT语句),它将被显示为未压缩字符串。

压缩字符串不是一个基本类。

压缩字符串的示例
CREATE QUERY stringCompressEx(VERTEX<person> m1) FOR GRAPH workNet {
 ListAccum<STRING COMPRESS> @@strCompressList, @@strCompressList2;
 SetAccum<STRING COMPRESS> @@strCompressSet, @@strCompressSet2;
 ListAccum<STRING> @@strList, @@strList2;
 SetAccum<STRING> @@strSet, @@strSet2;
 
 S = {m1};
 
 S = SELECT s
     FROM S:s
     ACCUM @@strSet += s.interestSet,    
           @@strList += s.interestList,  
           @@strCompressSet += s.interestSet,   # use the dictionary from person.interestSet
           @@strCompressList += s.interestList; # use the dictionary from person.interestList
 
 @@strCompressList2 += @@strCompressList;  # @@strCompressList2 gets the dictionary from @@strCompressList, which is from person.interestList
 @@strCompressList2 += "xyz";   # "xyz" is not in the dictionary, so store the actual string value
 
 @@strCompressSet2 += @@strCompressSet;
 @@strCompressSet2 += @@strSet;
 
 @@strList2 += @@strCompressList;  # string compress integer values are decoded to strings
 @@strSet2 += @@strCompressSet;  
 
 PRINT @@strSet, @@strList, @@strCompressSet, @@strCompressList;
 PRINT @@strSet2, @@strList2, @@strCompressSet2, @@strCompressList2;
}
stringCompressEx.json 结果
GSQL > RUN QUERY stringCompressEx("person12")
{
 "error": false,
 "message": "",
 "version": {
   "schema": 0,
   "api": "v2"
 },
 "results": [
   {
     "@@strCompressList": [
       "music",
       "engineering",
       "teaching",
       "teaching",
       "teaching"
     ],
     "@@strSet": [ "teaching", "engineering", "music" ],
     "@@strCompressSet": [ "music", "engineering", "teaching" ],
     "@@strList": [
       "music",
       "engineering",
       "teaching",
       "teaching",
       "teaching"
     ]
   },
   {
     "@@strSet2": [ "music", "engineering", "teaching" ],
     "@@strCompressList2": [
       "music",
       "engineering",
       "teaching",
       "teaching",
       "teaching",
       "xyz"
     ],
     "@@strList2": [
       "music",
       "engineering",
       "teaching",
       "teaching",
       "teaching"
     ],
     "@@strCompressSet2": [ "teaching", "engineering", "music" ]
   }
 ]
}

文件对象

一个文件对象(FILE object)是一个包含有序数据的对象,与本地计算机上的某个文本文件关联。

当引用文件对象时,我们总是将单词FILE大写,以将其与其他普通的文件区别开来。

声明文件对象时,用户将其与指定的文本文件关联。该动作将删除文本文件中的任何现有内容。 在执行查询的过程中,写入FILE的内容将被添加到该文件对象中。 当包含该FILE声明的查询完成后,FILE中的所有内容会被保存到一个文本文件中。

文件对象可以作为参数传递给另一个查询。 当对方查询接收到某个文件对象形式的参数时,它可以将数据附加到该文件对象上,每个接收此文件对象的查询都可以将其作为参数。

查询参数类型

输入查询的参数可以是基本类(除了EDGE、JSONARARY和JSONOBJECT之外),也同样可以是一个包含基本类元素的SET或BAG。

查询参数是不可变的。 无法在查询中为其赋予新值。

但文件对象是一种特殊情况。 它可以通过引用来传递,这意味着对象的接收方收到的是一个指向文件的链接地址。 接收方的查询也可以写入该文件。

BNF范式
parameterType := baseType
              | [ SET | BAG ] "<" baseType ">"
              | FILE
例:类型参数集合
(SET<VERTEX<person> p1, BAG<INT> ids, MAP<UINT, STRING> names)

Last updated