This section introduces the core data structures provided by the driver. These objects represent the entities you work with when retrieving results from Ultipa.
Node
Node
includes the following fields:
Field |
Type |
Description |
---|---|---|
uuid |
string | Node _uuid . |
id |
string | Node _id . |
schema |
string | Name of the schema the node belongs to. |
values |
[key: string]: any | Node property key-value pairs. |
If a query returns nodes, you can use asNodes()
to convert the results into a list of Node
objects:
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("MATCH (n) RETURN n LIMIT 2", requestConfig);
const nodes = response.alias("n").asNodes();
for (const node of nodes) {
console.log(node)
};
Node {
uuid: '72059793061183494',
id: 'ULTIPA800000000000003B',
schema: 'account',
values: {
industry: 'Publishing',
name: 'Velox',
gender: 'female',
year: 1976
}
}
Node {
uuid: '504405357288751106',
id: 'ULTIPA8000000000000060',
schema: 'account',
values: {
industry: 'Real Estate',
name: 'Kox3',
gender: 'male',
year: 1980
}
}
Edge
Edge
includes the following fields:
Field |
Type |
Description |
---|---|---|
uuid |
string | Edge _uuid . |
fromUuid |
string | _uuid of the source node of the edge. |
toUuid |
string | _uuid of the destination node of the edge. |
from |
string | _id of the source node of the edge. |
to |
string | _id of the destination node of the edge. |
schema |
string | Name of the schema the edge belongs to. |
values |
[key: string]: any | Edge property key-value pairs. |
If a query returns edges, you can use asEdges()
to convert the results into a list of Edge
objects:
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("MATCH ()-[e]->() RETURN e LIMIT 3",requestConfig);
const edges = response.alias("e").asEdges();
for (const edge of edges) {
console.log(edge)
};
Edge {
uuid: '385',
fromUuid: '6196955286285058053',
toUuid: '7998395137233256458',
from: 'ULTIPA8000000000000007',
to: 'ULTIPA800000000000000D',
schema: 'follow',
values: {}
}
Edge {
uuid: '553',
fromUuid: '6196955286285058053',
toUuid: '9223377534412914779',
from: 'ULTIPA8000000000000007',
to: 'ULTIPA8000000000000408',
schema: 'wishlist',
values: { timestamp: 1537331913, datetime: '2018-09-19 12:38:33' }
}
Edge {
uuid: '559',
fromUuid: '6196955286285058053',
toUuid: '4611691515985526871',
from: 'ULTIPA8000000000000007',
to: 'ULTIPA80000000000003ED',
schema: 'wishlist',
values: { timestamp: 1544118960, datetime: '2018-12-07 01:56:00' }
}
Path
Path
includes the following fields:
Field |
Type |
Description |
---|---|---|
nodeUuids |
string[] | The list of node _uuid s in the path. |
edgeUuids |
string[] | The list of edge _uuid s in the path |
nodes |
Map<string, Node > |
A map of nodes in the path, where the key is the node’s _uuid , and the value is the corresponding node. |
edges |
Map<string, Edge > |
A map of edges in the path, where the key is the edge’s _uuid , and the value is the corresponding edge. |
Methods on Path
:
Method |
Return |
Description |
---|---|---|
length() |
number | Returns the number of edges in the path. |
If a query returns paths, you can first use asGraph()
to convert the results into a Graph
object; the Graph
object provides access to the returned paths:
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("MATCH p = ()-[]-() RETURN p LIMIT 2", requestConfig);
const graph = response.alias("p").asGraph();
const paths = graph.getPaths();
for (const path of paths) {
console.log("Node _uuids:", path.nodeUuids, "Length:", path.length())
};
Node _uuids: [ '6196955286285058053', '7998395137233256458' ] Length: 1
Node _uuids: [ '6196955286285058053', '9223377534412914779' ] Length: 1
Graph
Graph
includes the following fields:
Field |
Type |
Description |
---|---|---|
paths |
Path [] |
The list of the returned paths. |
nodes |
Map<string, Node > |
A map of unique nodes in the graph, where the key is the node’s _uuid , and the value is the corresponding node. |
edges |
Map<string, Edge > |
A map of unique edges in the graph, where the key is the edge’s _uuid , and the value is the corresponding edge. |
Methods on Graph
:
Method |
Parameter |
Return |
Description |
---|---|---|---|
getPaths() |
Path [] |
Returns the list of Path objects in the graph. |
|
addNode() |
node: Node |
void | Adds a Node to the graph. Duplicate nodes are not added; nodes remains unique. |
addEdge() |
edge: Edge |
void | Adds an Edge to the graph. Duplicate edges are not added; edges remains unique. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("MATCH p = ()-[]-() RETURN p LIMIT 2", requestConfig);
const graph = response.alias("p").asGraph();
console.log("Unique nodes in the graph:");
console.log(graph.nodes);
console.log("\n_uuid of nodes in each path:");
const paths = graph.getPaths();
for (const path of paths) {
console.log("Node _uuids:", path.nodeUuids)
};
Unique nodes in the graph:
Map(3) {
'6196955286285058053' => Node {
uuid: '6196955286285058053',
id: 'ULTIPA8000000000000007',
schema: 'account',
values: {
industry: 'Government',
name: 'Yuki',
gender: 'female',
year: 1986
}
},
'7998395137233256458' => Node {
uuid: '7998395137233256458',
id: 'ULTIPA800000000000000D',
schema: 'account',
values: {
industry: 'Health',
name: 'CR.',
gender: 'female',
year: 1987
}
},
'9223377534412914779' => Node {
uuid: '9223377534412914779',
id: 'ULTIPA8000000000000408',
schema: 'movie',
values: {
genre: 'Fiction Action Thriller',
name: 'Inception',
rating: 9,
year: 2010
}
}
}
_uuid of nodes in each path:
Node _uuids: [ '6196955286285058053', '7998395137233256458' ]
Node _uuids: [ '6196955286285058053', '9223377534412914779' ]
const response1 = await conn.gql("MATCH p = ()-[]-() RETURN p LIMIT 2", requestConfig);
const graph = response1.alias("p").asGraph();
console.log("Original nodes in the graph:")
for (const [_uuid, node] of graph.nodes) {
console.log(_uuid)
};
// Adds 3 account nodes to the returned graph
const response2 = await conn.gql("MATCH (n:account) RETURN n LIMIT 3", requestConfig);
const nodes = response2.alias("n").asNodes();
for (const node of nodes) {
graph.addNode(node);
};
console.log("New collection of nodes in the graph:")
for (const [_uuid, node] of graph.nodes) {
console.log(_uuid)
};
Original nodes in the graph:
6196955286285058053
7998395137233256458
9223377534412914779
New collection of nodes in the graph:
6196955286285058053
7998395137233256458
9223377534412914779
72059793061183494
504405357288751106
576462951326679064
GraphSet
GraphSet
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
id |
string | / | Graph ID. |
name |
string | / | Graph name. |
totalNodes |
string | / | Total number of nodes in the graph. |
totalEdges |
string | / | Total number of edges in the graph. |
shards |
string[] | [] |
The list of IDs of shard servers where the graph is stored. |
partitionBy |
string | Crc32 |
The hash function used for graph sharding, which can be Crc32 , Crc64WE , Crc64XZ , or CityHash64 . |
status |
string | / | Graph status, which can be NORMAL , LOADING_SNAPSHOT , CREATING , DROPPING , or SCALING . |
description |
string | / | Graph description. |
slotNum |
number | 0 | The number of slots used for graph sharding. |
const response = await conn.gql("SHOW GRAPH");
const graphs = response.alias("_graph").asGraphSets();
for (const graph of graphs) {
console.log(graph.name)
}
DFS_EG
cyber
netflow
Schema
Schema
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Schema name |
dbType |
DBType |
/ | Schema type, which can be DBNODE or DBEDGE . |
properties |
Property [] |
/ | The list of properties associated with the schema. |
description |
string | / | Schema description |
total |
string | 0 | Total number of nodes or edges belonging to the schema. |
id |
string | / | Schema ID. |
stats |
SchemaStat [] |
/ | A list of SchemaStat objects; each SchemaStat includes attributes schema (schema name), dbType (schema type), fromSchema (source node schema), toSchema (destination node schema), and count (count of nodes or edges). |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("SHOW NODE SCHEMA", requestConfig);
const schemas = response.alias("_nodeSchema").asSchemas();
for (const schema of schemas) {
console.log(schema.name)
}
default
account
celebrity
country
movie
Property
Property
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Property name. |
type |
UltipaPropertyType |
/ | Property value type, which can be INT32 , UINT32 , INT64 , UINT64 , FLOAT , DOUBLE , DECIMAL , STRING , TEXT , LOCAL_DATETIME , ZONED_DATETIME , DATE , LOCAL_TIME , ZONED_TIME , DATETIME , TIMESTAMP , YEAR_TO_MONTH , DAY_TO_SECOND , BLOB , BOOL , POINT , LIST , SET , MAP , NULL , UUID , ID , FROM , FROM_UUID , TO , TO_UUID , IGNORE , or UNSET . |
subType |
UltipaPropertyType [] |
/ | If the type is LIST or SET , sets its element type; only one UltipaPropertyType is allowed in the list. |
schema |
string | / | The associated schema of the property. |
description |
string | / | Property description. |
lte |
boolean | / | Whether the property is LTE-ed. |
read |
boolean | / | Whether the property is readable. |
write |
boolean | / | Whether the property can be written. |
encrypt |
string | / | Encryption method of the property, which can be AES128 , AES256 , RSA , or ECC . |
decimalExtra |
DecimalExtra |
/ | The precision (1–65) and scale (0–30) of the DECIMAL type. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("SHOW NODE account PROPERTY", requestConfig);
const properties = response.alias("_nodeProperty").asProperties();
for (const property of properties) {
console.log(property.name)
}
title
profile
age
name
logo
Attr
Attr
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Name of the returned alias. |
values |
object[] | / | The returned values. |
propertyType |
UltipaPropertyType |
/ | Type of the property. |
resultType |
ResultType |
/ | Type of the results, which can be RESULT_TYPE_UNSET , RESULT_TYPE_PATH , RESULT_TYPE_NODE , RESULT_TYPE_EDGE , RESULT_TYPE_ATTR or RESULT_TYPE_TABLE . |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("MATCH (n:account) RETURN n.name LIMIT 3");
const attr = response.alias("n.name").asAttr();
console.log("name:", attr.name);
console.log("values:", attr.values);
console.log("type:", attr.propertyType);
name: n.name
values: ['Velox', 'K03', 'Lunatique']
type: 7
Table
Table
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Table name. |
headers |
Header [] |
/ | Table headers. |
rows |
any[][] | / | Table rows. |
Methods on a Table
object:
Method |
Return |
Description |
---|---|---|
toKV() |
any[] | Convert all rows in the table to an array of key-value objects. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql(
"MATCH (n:account) RETURN table(n._id, n.name) LIMIT 3",
requestConfig
);
const table = response.get(0).asTable();
console.log("Header:");
for (const header of table.getHeaders()) {
console.log(header.propertyName, " - ", header.propertyType);
}
console.log("First Row:");
const rows = table.toKV();
if (rows.length != 0) {
console.log(rows[0]);
}
Header:
n._id - 7
n.name - 7
First Row:
{'n._id': 'ULTIPA800000000000003B', 'n.name': 'Velox'}
HDCGraph
HDCGraph
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | HDC graph name. |
graphName |
string | / | The source graph from which the HDC graph is created. |
status |
string | / | HDC graph status. |
stats |
string | / | Statistics of the HDC graph. |
isDefault |
string | / | Whether it is the default HDC graph of the source graph. |
hdcServerName |
string | / | Name of the HDC server that hosts the HDC graph. |
hdcServerStatus |
string | / | Status of the HDC server that hosts the HDC graph. |
config |
string | / | Configurations of the HDC graph. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.uql("hdc.graph.show()", requestConfig);
const hdcGraphs = response.alias("_hdcGraphList").asHDCGraphs();
for (const hdcGraph of hdcGraphs) {
console.log(hdcGraph.name, "on", hdcGraph.hdcServerName);
}
miniCircle_hdc_graph on hdc-server-1
miniCircle_hdc_graph2 on hdc-server-2
Algo
Algo
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Algorithm name. |
type |
string | / | Algorithm type. |
version |
string | / | Algorithm version. |
params |
AlgoParam [] |
/ | Algorithm parameters, each AlgoParam has attributes name and desc . |
writeSupportType |
string | / | The writeback types supported by the algorithm. |
canRollback |
string | / | Whether the algorithm version supports rollback. |
configContext |
string | / | The configurations of the algorithm. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.uql("show().hdc('hdc-server-1')");
const algos = response.alias("_algoList").asAlgos();
for (const algo of algos) {
if (algo.type == "algo") {
console.log(
`${algo.name} supports writeback types: ${algo.writeSupportType}`
);
}
}
fastRP supports writeback types: DB,FILE
struc2vec supports writeback types: DB,FILE
Projection
Projection
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Projection name. |
graphName |
string | / | The source graph from which the projection is created. |
status |
string | / | Projection status. |
stats |
string | / | Statistics of the projection. |
config |
string | / | Configurations of the projection. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.uql("show().projection()", requestConfig);
const projections = response.alias("_projectionList").asProjections();
for (const projection of projections) {
console.log(projection.name)
}
miniCircle_projection_1
Index
Index
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
id |
string | / | Index ID. |
name |
string | / | Index name. |
properties |
string | / | Properties associated with the index. |
schema |
string | / | The schema associated with the index |
status |
string | / | Index status. |
size |
string | / | Index size in bytes. |
dbType |
DBType |
/ | Index type, which can be DBNODE or DBEDGE . |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("SHOW NODE INDEX", requestConfig);
const indexList = response.alias("_nodeIndex").asIndexes();
for (const index of indexList) {
console.log(index.schema, "-", index.properties)
}
account - gender(6)
account - year
Privilege
Privilege
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Privilege name. |
level |
PrivilegeLevel |
/ | Privilege level, which can be GraphLevel or SystemLevel . |
const response = await conn.uql("show().privilege()");
const privileges = response.alias("_privilege").asPrivileges();
const graphPrivilegeNames = privileges
.filter((p) => p.level === PrivilegeLevel.GraphLevel)
.map((p) => p.name)
.join(", ");
console.log("Graph privileges: " + graphPrivilegeNames);
const systemPrivilegeNames = privileges
.filter((p) => p.level === PrivilegeLevel.SystemLevel)
.map((p) => p.name)
.join(", ");
console.log("System privileges: " + systemPrivilegeNames);
Graph privileges: READ, INSERT, UPSERT, UPDATE, DELETE, CREATE_SCHEMA, DROP_SCHEMA, ALTER_SCHEMA, SHOW_SCHEMA, RELOAD_SCHEMA, CREATE_PROPERTY, DROP_PROPERTY, ALTER_PROPERTY, SHOW_PROPERTY, CREATE_FULLTEXT, DROP_FULLTEXT, SHOW_FULLTEXT, CREATE_INDEX, DROP_INDEX, SHOW_INDEX, LTE, UFE, CLEAR_JOB, STOP_JOB, SHOW_JOB, ALGO, CREATE_PROJECT, SHOW_PROJECT, DROP_PROJECT, CREATE_HDC_GRAPH, SHOW_HDC_GRAPH, DROP_HDC_GRAPH, COMPACT_HDC_GRAPH, SHOW_VECTOR_INDEX, CREATE_VECTOR_INDEX, DROP_VECTOR_INDEX, SHOW_CONSTRAINT, CREATE_CONSTRAINT, DROP_CONSTRAINT
System privileges: TRUNCATE, COMPACT, CREATE_GRAPH, SHOW_GRAPH, DROP_GRAPH, ALTER_GRAPH, TOP, KILL, STAT, SHOW_POLICY, CREATE_POLICY, DROP_POLICY, ALTER_POLICY, SHOW_USER, CREATE_USER, DROP_USER, ALTER_USER, SHOW_PRIVILEGE, SHOW_META, SHOW_SHARD, ADD_SHARD, DELETE_SHARD, REPLACE_SHARD, SHOW_HDC_SERVER, ADD_HDC_SERVER, DELETE_HDC_SERVER, LICENSE_UPDATE, LICENSE_DUMP, GRANT, REVOKE, SHOW_BACKUP, CREATE_BACKUP, SHOW_VECTOR_SERVER, ADD_VECTOR_SERVER, DELETE_VECTOR_SERVER
Policy
Policy
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
name |
string | / | Policy name. |
systemPrivileges |
string[] | / | System privileges included in the policy. |
graphPrivileges |
Map<string, string[]> | / | Graph privileges included in the policy; in the map, the key is the name of the graph, and the value is the corresponding graph privileges. |
propertyPrivileges |
PropertyPrivilege |
/ | Property privileges included in the policy; the PropertyPrivilege has attributes node and edge , both are PropertyPrivilegeElement objects. |
policies |
string[] | / | Policies included in the policy. |
PropertyPrivilegeElement
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
read |
string[][] | / | An array of arrays; each inner array contains three strings representing the graph, schema, and property. |
write |
string[][] | / | An array of arrays; each inner array contains three strings representing the graph, schema, and property. |
deny |
string[][] | / | An array of arrays; each inner array contains three strings representing the graph, schema, and property. |
const response = await conn.uql("show().policy('Tester')");
const policy = response.alias("_policy").asPolicies();
console.log("Graph privileges: ", policy[0].graphPrivileges);
console.log("System privileges: ", policy[0].systemPrivileges);
console.log("Property privileges:");
console.log("- Node (Read): ", policy[0].propertyPrivileges.node.read);
console.log("- Node (Write): ", policy[0].propertyPrivileges.node.write);
console.log("- Node (Deny): ", policy[0].propertyPrivileges.node.deny);
console.log("- Edge (Read): ", policy[0].propertyPrivileges.edge.read);
console.log("- Edge (Write): ", policy[0].propertyPrivileges.edge.write);
console.log("- Edge (Deny): ", policy[0].propertyPrivileges.edge.deny);
console.log("Policies: ", policy[0].policies)
Graph Privileges: Map(3) {'amz' => ['ALGO', 'INSERT', 'DELETE', 'UPSERT'], 'StoryGraph' => ['UPDATE', 'READ']}
System Privileges: ['TRUNCATE', 'KILL', 'TOP']
Property Privileges:
- Node (Read): [['*', '*', '*']]
- Node (Write): []
- Node (Deny): []
- Edge (Read): []
- Edge (Write): [['amz', '*', '*'], ['alimama', '*', '*']]
- Edge (Deny): [['miniCircle', 'review', 'value, timestamp']]
Policies: ['sales', 'manager']
User
User
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
username |
string | / | Username. |
password |
string | / | Password. |
createdTime |
Date |
/ | The time when the user was created. |
systemPrivileges |
string[] | / | System privileges granted to the user. |
graphPrivileges |
Map<string, string[]> | / | Graph privileges granted to the user; in the map, the key is the name of the graph, and the value is the corresponding graph privileges. |
propertyPrivileges |
PropertyPrivilege |
/ | Property privileges granted to the user; the PropertyPrivilege has attributes node and edge , both are PropertyPrivilegeElement objects. |
policies |
string[] | / | Policies granted to the user. |
PropertyPrivilegeElement
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
read |
string[][] | / | An array of arrays; each array list contains three strings representing the graph, schema, and property. |
write |
string[][] | / | An array of arrays; each array list contains three strings representing the graph, schema, and property. |
deny |
string[][] | / | An array of arrays; each array list contains three strings representing the graph, schema, and property. |
const response = await conn.uql("show().user('johndoe')");
const user = response.alias("_user").asUsers();
console.log("Created Time: ", user[0].createdTime);
console.log("Graph privileges: ", user[0].graphPrivileges);
console.log("System privileges: ", user[0].systemPrivileges);
console.log("Property privileges:");
console.log("- Node (Read): ", user[0].propertyPrivileges.node.read);
console.log("- Node (Write): ", user[0].propertyPrivileges.node.write);
console.log("- Node (Deny): ", user[0].propertyPrivileges.node.deny);
console.log("- Edge (Read): ", user[0].propertyPrivileges.edge.read);
console.log("- Edge (Write): ", user[0].propertyPrivileges.edge.write);
console.log("- Edge (Deny): ", user[0].propertyPrivileges.edge.deny);
console.log("Policies: ", user[0].policies);
Created Time: 2025-04-02 11:08:38
Graph Privileges: Map(2){'amz'=> ['ALGO', 'INSERT', 'DELETE', 'UPSERT'], 'StoryGraph'=> ['UPDATE', 'READ']}
System Privileges: ['TRUNCATE', 'KILL', 'TOP']
Property Privileges:
- Node (Read): [['*', '*', '*']]
- Node (Write): []
- Node (Deny): []
- Edge (Read): []
- Edge (Write): [['amz', '*', '*'], ['alimama', '*', '*']]
- Edge (Deny): [['miniCircle', 'review', 'value, timestamp']]
Policies: ['sales', 'manager']
Process
Process
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
processId |
string | / | Process ID. |
processQuery |
string | / | The query that the process executes. |
status |
string | / | Process status. |
duration |
string | / | The duration (in seconds) the process has run. |
const response = await conn.uql("top()");
const processes = response.alias("_top").asProcesses();
for (const process of processes) {
console.log(process.processId);
}
1049435
Job
Job
includes the following fields:
Field |
Type |
Default |
Description |
---|---|---|---|
id |
string | / | Job ID. |
graphName |
string | / | Name of the graph where the job executes on. |
query |
string | / | The query that the job executes. |
type |
string | / | Job type. |
errNsg |
string | / | Error message of the job. |
result |
Map<any, any> | / | Result of the job. |
startTime |
string | / | The time when the job begins. |
endTime |
string | / | The times when the job ends. |
status |
string | / | Job status. |
progress |
string | / | Progress updates for the job, such as indications that the write operation has been started. |
const requestConfig: RequestConfig = {
graph: "miniCircle",
};
const response = await conn.gql("SHOW JOB", requestConfig);
const jobs = response.alias("_job").asJobs();
const falid_jobs = jobs.filter((job) => job.status === "FAILED");
if (falid_jobs.length > 0) {
for (const job of falid_jobs) {
console.log(job.id, "_", job.errMsg, "-", job.type);
}
}
51 - Fulltext name already exists. - CREATE_FULLTEXT
42 - Fulltext name already exists. - CREATE_FULLTEXT
26 - [engine] uuids should be unsigned integer - HDC_ALGO
26_1 - - HDC_ALGO
17 - [engine] all failed, because some nodes do not exist in db - HDC_ALGO
17_1 - - HDC_ALGO