Skip to content
  • RKGMS
  • SDK GUIDES
  • Go

RelationalAI SDK for Go

This guide presents the main features of the RelationalAI SDK for Go, used to interact with the Relational Knowledge Graph Management System (RKGMS).

Go logo

The rai-sdk-go SDK is open source and is available in the Github repository:


RelationalAI/rai-sdk-go

It includes self-contained examples of the main API functionality. Contributions and pull requests are welcome.

Note: This guide applies to rai-sdk-go, the latest iteration of the Go SDK. The relationalai-sdk package is deprecated.

The last section describes how the different Console notebook cell types can be implemented with this Go SDK API.

Requirements

Check the rai-sdk-go repository in pkg.go.dev for the latest version requirements to interact with the RKGMS using the RelationalAI SDK for Go.

Installation

To use the Go SDK, you should include it in your Go code as follows:

import  "github.com/relationalai/rai-sdk-go/rai"

Then simply run go mod tidy to update your go.mod file:

go mod tidy

Configuration

To connect to the RAI Server using the Go SDK, you need a configuration file. See the SDK Configuration guide for more details.

The Go rai.NewClientFromConfig() function allows you to specify a configuration to use for connecting to the RAI Server. You can load the configuration within a Go application as follows:

package main
 
import "github.com/relationalai/rai-sdk-go/rai"
 
func main() {
    rai.NewClientFromConfig("default")
}

To load a different configuration, you can simply replace "default" with a different profile name.

Creating a Client

Most API operations use a client, which contains the necessary settings for making requests against the RelationalAI REST APIs.

You can test your client by listing the databases as follows:

package main
 
import (
	"log"
	"github.com/relationalai/rai-sdk-go/rai"
)
 
func main() {
	// create client with default profile
	client, err := rai.NewClientFromConfig("default")
	if err != nil {
		log.Fatal(err)
	}
 
	// get list of databases
	rsp, err := client.ListDatabases()
	if err != nil {
		log.Fatal(err)
	}
 
	// print them
	rai.Print(rsp, 4)
}

This should print a list with the database info, assuming your keys have the corresponding permissions. See the Listing Databases section below.

The remaining code examples in this document assume that you have a valid client in the client Go variable and that you have imported the necessary packages, such as the ones in the previous examples. Here’s an example:

import (
	"log"
	"github.com/relationalai/rai-sdk-go/rai"
)
...

Additionally, most of the Go API calls return an error together with the expected result if something goes wrong. Therefore, you can typically handle the errors by logging them as above, or in any other way that may be appropriate. This is an example for logging:

rsp, err := client.ListDatabases()
if err != nil {
    log.Fatal(err)
}

Managing Users

A client with the right permissions can create, disable, and list the users under the account.

Creating a User

You can create a user using the CreateUser function of the client. To create a user, you need to specify an email and a set of roles for the user:

email := "user@relational.ai"
roles := []string{ "user" }
 
rsp, err := client.CreateUser(email, roles)
rai.Print(rsp, 4)

Here, email is a string identifying the user, and roles is a list of strings, each one representing a role. The roles currently supported are user and admin. user is the default role if no role is specified.

Disabling and Enabling a User

You can disable a user using the DisableUser function of the client as follows:

rsp, err := client.DisableUser(id)
rai.Print(rsp, 4)

In this case, id is a string reflecting a given user’s ID, as returned within the response in the user creation example above.

You can reenable the user by using the EnableUser function as follows:

rsp, err = client.EnableUser(id);
rai.Print(rsp, 4);

Listing Users

You can list users using the ListUsers function of the client as follows:

rsp, err := client.ListUsers()
rai.Print(rsp, 4)

Retrieving User Details

You can retrieve user details from the system using the GetUser function of the client as follows:

rsp, err := client.GetUser(id)
rai.Print(rsp, 4)

Here, similar to DisableUser above, id is a string ID uniquely identifying the user, such as "auth0|XXXXXXXXXXXXXXXXXX".

Finding Users Using Email

You can look up a user’s details by specifying their email through the FindUser function of the client:

rsp, err := client.FindUser("user@relational.ai")
rai.Print(rsp, 4)

Managing OAuth Clients

OAuth clients can be managed with the following functions, provided you have the corresponding permissions:

rsp, err := client.CreateOAuthClient(name, permissions)

Here, name is a string identifying the client. permissions is a list of permissions from the following supported permissions:

  • create:accesskey
  • create:compute
  • create:oauth_client
  • create:user
  • delete:compute
  • delete:database
  • delete:oauth_client
  • list:accesskey
  • list:compute
  • list:database
  • list:oauth_client
  • list:permission
  • list:role
  • list:user
  • read:compute
  • read:credits_usage
  • read:oauth_client
  • read:role
  • read:user
  • rotate:oauth_client_secret
  • run:transaction
  • update:database
  • update:oauth_client
  • update:user

You can get a list of OAuth clients as follows:

rsp, err := client.ListOAuthClients()
rai.Print(rsp, 4)

You can get details for a specific OAuth client, identified by the string id, as follows:

rsp, err := client.GetOAuthClient(id)

This is how to delete the OAuth client identified by the string id:

rsp, err := client.DeleteOAuthClient(id)

Each OAuth client has its own set of permissions, which determine the operations it can execute. Depending on the permissions, some operations, such as listing other users or creating or deleting compute engines, may fail. Refer to the RAI Console Managing Users guide for further details.

Managing Engines

To query and update RAICloud databases, you will need a running engine. The following API calls create and manage them.

Creating an Engine

You can create a new engine as follows. Note that the default size is XS:

package main
 
import (
	"log"
	"github.com/relationalai/rai-sdk-go/rai"
)
 
func main() {
	// create client with default profile
	client, err := rai.NewClientFromConfig("default")
	if err != nil {
		log.Fatal(err)
	}
 
    // create engine
	engine := "my-engine"
	size := "XS"
	rsp, err := client.CreateEngine(engine, size)
	if err != nil {
		log.Fatal(err)
	}
 
	rai.Print(rsp, 4)
}

Valid sizes are given as a string and can be one of:

  • XS (extra small).
  • S (small).
  • M (medium).
  • L (large).
  • XL (extra large).

Note: It may take some time before your engine is in the “PROVISIONED” state, where it is ready for queries. It will be in the “PROVISIONING” state before that.

To list all the engines that are currently provisioned, you can use ListEngines():

rsp, err := client.ListEngines()

If you want to list engines that are in a given state, you can use the "state" parameter as follows:

rsp, err := client.ListEngines("state", "DELETED")

Most of the API examples below assume there is a running engine (in the “PROVISIONED” state) in the engine string variable, and a test database in the database string variable:

// replace by your values for testing:
database := "mydatabase";
engine := "myengine";

Deleting an Engine

An engine can be deleted with:

rsp, err := client.DeleteEngine(engine)
rai.Print(rsp, 4)

If successful, it will return this:

{'status':
    {'name': XXXX
    'state': 'DELETING',
    'message': 'engine XXXX deleted successfully'}
}

Note that since RAICloud decouples computation from storage, deleting an engine does not delete any cloud databases. See Managing Engines for more details.

Getting Info for an Engine

The details for a specific compute engine can be retrieved with GetEngine():

rsp, err := client.GetEngine(opts.Engine)
rai.Print(rsp, 4)

Managing Databases

Creating a Database

You can create a database by calling the client’s CreateDatabase() method as follows:

database := "mydatabase";
rsp, err := client.CreateDatabase(database)
rai.Print(rsp, 4)

The result from a successful CreateDatabase() call will look like this:

{
    "id": "xxxx-xxxx-xxx-xxxx-xxx",
    "name": "mydatabase",
    "region": "us-east",
    "account_name": "acount-name",
    "created_by": "xxxx@xyzw",
    "deleted_on": "",
    "state": "CREATED"
}

Cloning a Database

You can use CloneDatabase() to clone a database by specifying source and destination arguments:

sourceDB := "myDB";
targetDB := "cloneDB";
 
rsp, err := client.CloneDatabase(targetDB, sourceDB)
rai.Print(rsp, 4);

With this API call, you can clone a database from myDB to "cloneDB", creating an identical copy. Any subsequent changes to either database will not affect the other. Cloning a database only succeeds if the source database already exists.

Retrieving Database Details

You can view the details of a database using the GetDatabase() method of a client:

rsp, err := client.GetDatabase(sourceDB)
rai.Print(rsp, 4);

The response is a JSON object. If the database does not exist, there will be an error in err.

Listing Databases

Using ListDatabases() will list the databases available to the account:

rsp, err := client.ListDatabases()
rai.Print(rsp, 4);

A variation of the ListDatabases() method takes a parameter state as input that can be used to filter databases by state. Example states are: “CREATED”, “CREATING”, or “CREATION_FAILED”.

rsp, err := client.ListDatabases("state", "CREATED")
rai.Print(rsp, 4);

Deleting Databases

A database can be deleted with DeleteDatabase() if the client has the right permissions.

The database is identified by its name, as used in CreateDatabase():

err := client.DeleteDatabase("my-old-db")

Deleting a database cannot be undone.

Rel Model Sources

Rel model sources are collections of Rel code that can be added, updated, or deleted from a particular database. Since they update a database, a running engine — and a database — is required to perform operations on sources.

Installing Rel Model Sources

The LoadModel() method of the client installs a Rel model source in a given database. LoadModel() takes as input the database, the engine, a name for the model, and an io.Reader where the model will be read from:

func (c *Client) LoadModel(
	database, engine, name string, r io.Reader,
) (*TransactionResult, error)

For example, consider the following simple Rel model, stored in a file named hello.rel:

def R = "hello", "world"

You can then use the following complete example, which loads a model named hello from the hello.rel file into the my-database database using the my-engine engine:

package main
 
import (
	"log"
	"os"
	"github.com/relationalai/rai-sdk-go/rai"
)
 
func main() {
	// create client with default profile
	client, err := rai.NewClientFromConfig("default")
	if err != nil {
		log.Fatal(err)
	}
 
	file := "hello.rel"			// file with model
	name := "hello"				// model name
	database := "my-database"
	engine := "my-engine"
 
	// create reader
	r, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}
 
	// load the model
	rsp, err := client.LoadModel(database, engine, name, r)
	if err != nil {
		log.Fatal(err)
	}
 
	rai.Print(rsp, 4);
}

You can also easily provide the model code through a string, instead of a file, by first creating a Reader:

package main
 
import (
	"log"
	"strings"
	"github.com/relationalai/rai-sdk-go/rai"
)
 
func main() {
 
	// create client with default profile
	client, err := rai.NewClientFromConfig("default")
	if err != nil {
		log.Fatal(err)
	}
 
	model := "def R = \"hello\", \"world\""
	name := "hello"				// model name
	database := "my-database"
	engine := "my-engine"
 
	// create reader from string
	r := strings.NewReader(model)
 
	// load the model
	rsp, err := client.LoadModel(database, engine, name,  r)
	if err != nil {
		log.Fatal(err)
	}
 
	rai.Print(rsp, 4);
}

In addition to the simple version of providing one model either through a file or a string, you can provide a number of models using a map through the LoadModels() method:

func (c *Client) LoadModels(
	database, engine string, models map[string]io.Reader,
) (*TransactionResult, error)

The map contains the model names as string together with the respective Reader. Here is an example that loads three different models coming from three different files:

package main
 
import (
	"io"
	"log"
	"os"
	"github.com/relationalai/rai-sdk-go/rai"
)
 
func main() {
	// create client with default profile
	client, err := rai.NewClientFromConfig("default")
	if err != nil {
		log.Fatal(err)
	}
 
	// we assume the files are model1.rel, model2.rel, etc.
	modelNames := []string{"model1", "model2", "model3"}
	modelMap := make(map[string]io.Reader)
 
	for _, m := range modelNames {
		r, err := os.Open(m + ".rel")
		if err != nil {
			log.Fatal(err)
		}
		modelMap[m] = r
	}
 
	database := "my-database"
	engine := "my-engine"
 
	// load all the models
	rsp, err := client.LoadModels(database, engine, modelMap)
	if err != nil {
		log.Fatal(err)
	}
 
	rai.Print(rsp, 4);
}

Note that if the database already contains an installed source with the same given name, it is replaced by the new source.

Deleting a Rel Model Source

You can delete a source from a database using the DeleteModel() method and providing the model name like this:

rsp, err := client.DeleteModel(database, engine, name)

Listing Installed Rel Model Sources

You can list the sources in a database using the ListModelNames() method like this:

rsp, err := client.ListModelNames(database, engine)
rai.Print(rsp, 4);

This returns a JSON array of names.

To see the contents of a named source, use:

rsp, err := client.GetModel(database, engine, name)
rai.Print(rsp, 4);

name is the name of the model.

You can also list all models together with their sources using ListModels():

rsp, err := client.ListModels(database, engine)
rai.Print(rsp, 4);

Querying a Database

The high-level API method for running a single query/transaction against the database is Execute(). The function call blocks until the transaction is completed or there are several timeouts indicating that the system may be inaccessible. It specifies a Rel source, which can be empty, and a set of input relations.

The Execute() function is defined as follows:

func (c *Client) Execute(
	database, engine, source string,
	inputs map[string]string,
	readonly bool,
) (*TransactionAsyncResult, error)

Here is an example of Execute() for a simple Rel query:

source := "def output[x in {1;2;3}] = x * 2"
 
rsp, err := client.Execute(database, engine, source, nil, true)
 
if err != nil {
    log.Fatal(err)
}

In the code above, rsp contains a TransactionAsyncResult struct that has different fields containing the results and the status details of the transaction that was just executed.

For example, here is how to check the status of the transaction:

fmt.Println(rsp.Transaction.State)  // ABORTED or COMPLETED

Similarly, you can view the results of the transaction as follows:

rai.Print(rsp, 4)

This gives the following output:

{
    "GotCompleteResult": true,
    "Transaction": {
        "id": "12345678-1234-1234-1234-123456789012",
        "state": "COMPLETED"
    },
    "Results": [
        {
            "RelationID": "v1",
            "Table": [
                1,
                2,
                3
            ]
        },
        {
            "RelationID": "v2",
            "Table": [
                2,
                4,
                6
            ]
        }
    ],
    "Metadata": [
        {
            "relationId": "/:output/Int64/Int64",
            "types": [
                ":output",
                "Int64",
                "Int64"
            ]
        }
    ],
    "Problems": []
}

If the transaction was aborted, you would see a description of the problem in the Problems field of the JSON output.

Note that in Execute(), by convention, the readonly parameter is false by default. Queries meant to update base relations with insert and delete must use readonly=false. Also, by convention, only the output relation is returned, as in the Console notebooks.

As another query example, here is an API call to load some CSV data and store them in the base relation mybaserelation:

data := `name,lastname,id
John,Smith,1
Peter,Jones,2`
 
source := `def config:schema:name="string"
def config:schema:lastname="string"
def config:schema:id="int"
def config:syntax:header_row=1
def config:data = mydata
def delete[:mybaserelation] = mybaserelation
def insert[:mybaserelation] = load_csv[config]`
 
inputs := map[string]string{"mydata" : data}
readonly := false
 
rsp, err := client.Execute(database, engine, source, inputs, readonly)
fmt.Println(rsp.Transaction.State)
rai.Print(rsp, 4)

The RAI Go SDK also supports asynchronous transactions, through ExecuteAsync() that takes similar parameters to Execute(). In summary, when you issue a query to the database, the return output contains a transaction ID that can be subsequently used to retrieve the actual query results.

ExecuteAsync() is defined similar to Execute(), but in this case the running process is not blocked. You can obtain the query results whenever they are ready by polling the transaction state:

package main
import (
    "log"
    "time"
    "github.com/relationalai/rai-sdk-go/rai"
)
 
func main() {
    // create client with default profile
    client, err := rai.NewClientFromConfig("default")
    if err != nil {
        log.Fatal(err)
    }
 
    database := "my-database"
    engine := "my-engine"
    source := "def output[x in {1;2;3}] = x * 2"
 
    rsp, err := client.ExecuteAsync(database, engine, source, nil, true)
 
    if err != nil {
        log.Fatal(err)
    }
 
    // get the transaction ID
    id := rsp.Transaction.ID
    for {       // poll until transaction is either completed or aborted
        txn, err := client.GetTransaction(id)
 
        if err != nil {
            log.Fatal(err)
        }
 
        if txn.Transaction.State == "COMPLETED" || txn.Transaction.State == "ABORTED" {
            results, _ := client.GetTransactionResults(id)
            rai.Print(results, 4)
            break
        }
 
        time.Sleep(2 * time.Second)
    }
}
 

Similar to client.GetTransactionResults(id), you can also get metadata and problems for a given transaction ID:

metadata, _ := client.GetTransactionMetadata(id)
problems, _ := client.GetTransactionProblems(id)

The query size is limited to 64MB. An error will be returned if the request exceeds this API limit.

Getting Multiple Relations Back

As in the RAI Console, if you want to return multiple relations, you can define subrelations of output. Here’s an example:

source := `def a = 1;2 def b = 3;4 def output:one = a def output:two = b`
rsp, _ := client.Execute(database, engine, source, nil, true)
 
rai.Print(rsp, 4)

It gives the following output:

{
    "GotCompleteResult": true,
    "Transaction": {
        "id": "12345678-1234-1234-1234-123456789012",
        "state": "COMPLETED"
    },
    "Results": [
        {
            "RelationID": "v1",
            "Table": [
                1,
                2
            ]
        },
        {
            "RelationID": "v1",
            "Table": [
                3,
                4
            ]
        }
    ],
    "Metadata": [
        {
            "relationId": "/:output/:one/Int64",
            "types": [
                ":output",
                ":one",
                "Int64"
            ]
        },
        {
            "relationId": "/:output/:two/Int64",
            "types": [
                ":output",
                ":two",
                "Int64"
            ]
        }
    ],
    "Problems": []
}

Note that each section of metadata corresponds to the respective section of results, i.e., the first metadata section corresponds to the first section of results.

Result Structure

The response is a JSON string with the following fields:

FieldMeaning
RelationIDThis is a key for the relation, for example, "/:output/Int64".
TableThis contains the results of the query in a JSON-array format.

Each query is a complete transaction, executed in the context of the provided database.

The metadata are also a JSON string with the following fields:

FieldMeaning
relationIDThis is a key for the relation, for example, "/:output/:two/Int64". This key describes the keys of the relation together with the type of the data.
typesThis is a JSON-array that contains the key names of the relation and their data type.

Finally, problems also contain a JSON string with the following fields:

FieldMeaning
error_codeThe type of error that happened, for example, "PARSE_ERROR".
is_errorWhether an error occurred or there was some other problem.
is_exceptionWhether an exception occurred or there was some other problem.
messageA short description of the problem.
pathA file path for the cases when such a path was used.
reportA long description of the problem.
typeType of the problem, for example, "ClientProblem".

Specifying Inputs

The Execute() and ExecuteAsync() functions take an optional inputs map, which can be used to map relation names to string constants for the duration of the query, analogous to the file upload feature of Rel Console notebooks. Here is an example:

inputs := map[string]string{ "foo" : "bar" }
source := "def output = foo"
 
rsp, _ := client.Execute(database, engine, source, inputs, true)
 
rai.Print(rsp, 4)

This will return the string "bar".

Functions that transform a file and write the results to a base relation can easily be written like this. The Rel calls load_csv and load_json can be used in this way, via the data parameter, to write results to a base relation. See, for example, the sample code using load_csv in Querying a Database.

Printing Responses

The rai.Print() function prints API responses that come back in JSON format. Here’s an example:

source := "def output = 'a';'b';'c'"
rsp, _ := client.Execute(database, engine, source, nil, true)
 
rai.Print(rsp, 4)

This gives the following output:

{
    "GotCompleteResult": true,
    "Transaction": {
        "id": "12345678-1234-1234-1234-123456789012",
        "state": "COMPLETED"
    },
    "Results": [
        {
            "RelationID": "v1",
            "Table": [
                97,
                98,
                99
            ]
        }
    ],
    "Metadata": [
        {
            "relationId": "/:output/Char",
            "types": [
                ":output",
                "Char"
            ]
        }
    ],
    "Problems": []
}

You can also view the results only, instead of all the information of the transaction, as follows:

source := "def output = 'a';'b';'c'"
rsp, _ := client.Execute(database, engine, source, nil, true)
 
id := rsp.Transaction.ID
results, _ := client.GetTransactionResults(id)
 
rai.Print(results, 4)

This gives the following output:

[
    {
        "RelationID": "v1",
        "Table": [
            97,
            98,
            99
        ]
    }
]

In case there are problems, you can view the problems as follows:

source := "def output = aaa"     // aaa is undefined
rsp, _ := client.Execute(database, engine, source, nil, true)
 
id := rsp.Transaction.ID
problems, _ := client.GetTransactionProblems(id)
 
rai.Print(problems, 4)

This gives the following output:

[
    {
        "error_code": "UNDEFINED",
        "is_error": true,
        "is_exception": false,
        "message": "`aaa` is undefined.",
        "path": "",
        "report": "1| def output = aaa\n                ^^^\n\nThe problem occurred while compiling declaration `output`:\n1| def output = aaa\n   ^^^^^^^^^^^^^^^^\n\n",
        "type": "ClientProblem"
    }
]

Loading Data: LoadCSV and LoadJSON

As a convenience, the Go API includes a LoadCSV and LoadJSON function. These are not strictly necessary, since the load utilities in Rel itself can be used in a non-read-only Execute() or ExecuteAsync() query that uses the inputs parameter. See, for example, the sample code using the Rel load_csv in Querying a Database.

The Go function LoadCSV() loads data and inserts the result into the base relation named by the relation argument. Additionally, LoadCSV() attempts to guess the schema of the data. For more control over the schema, use a non-read-only Execute() or ExecuteAsync() query using the inputs option.

func (c *Client) LoadCSV(
	database, engine, relation string, r io.Reader, opts *CSVOptions,
) (*TransactionResult, error)

The CSVOptions class allows you to specify how to parse a given CSV file. Through a CSVOptions object, you can specify, for example, the delimiter and the escape character of a given file.

Similarly to LoadCSV(), LoadJson() loads the data string as JSON and inserts it into the base relation named by the relation argument:

func (c *Client) LoadJSON(
	database, engine, relation string, r io.Reader,
) (*TransactionResult, error)

Here is an example:

data := "{\"name\": \"John\", \"age\": 20}"
r := strings.NewReader(data)
 
rsp, _ := client.LoadJSON(database, engine, "myjson", r)
 
rai.Print(rsp, 4)

Note: In both cases, the relation base relation is not cleared, allowing for multipart, incremental loads. To clear it, issue a non-read-only Rel query of the form:

def delete[:relation] = relation

Listing Base Relations

You can list base relations as follows:

rsp, _ := client.ListEDBs(database, engine)
rai.Print(rsp, 4)

This will list the base relations in the given database. The result is a JSON list of objects.

Notebook Cell Types

As explained in the Working With RAI Notebooks guide, there are four notebook cell types: Query, Install, Update, and Markdown. This section describes how the three cell types Query, Install, and Update map to Go SDK API calls.

Query Cells

The Query cell in a Rel Console notebook corresponds to a simple Execute() (or ExecuteAsync()) function call with the parameter readonly=true, as shown below:

rsp, _ := client.Execute(
    database,
    engine,
    "def output = {(1,); (2,); (3,)}",
    nil,
    true,
)
rai.Print(rsp, 4)

It gives the following output:

{
    "GotCompleteResult": true,
    "Transaction": {
        "id": "12345678-1234-1234-1234-123456789012",
        "state": "COMPLETED"
    },
    "Results": [
        {
            "RelationID": "v1",
            "Table": [
                1,
                2,
                3
            ]
        }
    ],
    "Metadata": [
        {
            "relationId": "/:output/Int64",
            "types": [
                ":output",
                "Int64"
            ]
        }
    ],
    "Problems": []
}

As you can see from the empty problems array, the transaction did not produce any problems.

Note that no rules/definitions are persisted in this case.

Install Cells

To persist derived relations definitions, use the LoadModel() API function. That is, the Install cell in a Rel Console notebook corresponds to the LoadModel() Go function call, as shown below:

model := "def k = {2;3;50}"
r := strings.NewReader(model)
rsp, _ := client.LoadModel(database, engine, "arity1_k_def",  r)

Check that the relation above was persisted using the Execute() API call:

rsp, _ := client.Execute(database, engine, "def output = k", nil, true)
rai.Print(rsp, 4)

This is the output:

{
    "GotCompleteResult": true,
    "Transaction": {
        "id": "12345678-1234-1234-1234-123456789012",
        "state": "COMPLETED"
    },
    "Results": [
        {
            "RelationID": "v1",
            "Table": [
                2,
                3,
                50
            ]
        }
    ],
    "Metadata": [
        {
            "relationId": "/:output/Int64",
            "types": [
                ":output",
                "Int64"
            ]
        }
    ],
    "Problems": []
}

Update Cells

The Update cell in a Rel Console notebook corresponds to an Execute() (or ExecuteAsync()) function call with the parameter readonly=false. Here’s an example:

rsp, _ := client.Execute(
	database,
	engine,
	"def insert:employee = {(1, \"Han Solo\"); (2, \"Bart Simpson\")}",
	nil,
	false,
)
rai.Print(rsp, 4)

You can modify the employee base relation like this:

rsp, _ := client.Execute(
	database,
	engine,
	"def insert:employee = {(3, \"King\"); (4, \"Queen\")}",
	nil,
	false,
)
rai.Print(rsp, 4)

You can now query the modified employee base relation like this:

rsp, _ := client.Execute(
    database,
    engine,
    "def output=employee",
    nil,
    true,
);
rai.Print(rsp, 4)
{
    "GotCompleteResult": true,
    "Transaction": {
        "id": "12345678-1234-1234-1234-123456789012",
        "state": "COMPLETED"
    },
    "Results": [
        {
            "RelationID": "v1",
            "Table": [
                1,
                2,
                3,
                4
            ]
        },
        {
            "RelationID": "v2",
            "Table": [
                "Han Solo",
                "Bart Simpson",
                "King",
                "Queen"
            ]
        }
    ],
    "Metadata": [
        {
            "relationId": "/:output/Int64/String",
            "types": [
                ":output",
                "Int64",
                "String"
            ]
        }
    ],
    "Problems": []
}

To update base relations, always use the readonly=false parameter in the Execute() or ExecuteAsync() calls.

Was this doc helpful?