RelationalAI CLI
This guide presents the main features of the RelationalAI Command-Line Interface, which is used to interact with RelationalAI’s Relational Knowledge Graph System (RKGS).
The rai-cli
is open source and is available in the GitHub repository:

Contributions and pull requests are welcome.
Note: This guide applies to rai-cli
, the latest iteration of the RelationalAI CLI.
The relationalai-sdk
package is deprecated.
Installation
To use the CLI, you can directly download a binary for your installation from the rai-cli releases (opens in a new tab).
You can find precompiled binaries for MacOS, Linux, and Windows for different architectures.
After selecting the one that fits your system, you can unzip the archive and include the rai
binary in your system’s path.
Configuration
To connect to the RAI Server using the RelationalAI CLI, you need a configuration file. See SDK Configuration for more details.
The CLI allows you to specify a configuration to use for connecting to the RAI Server.
You can specify which configuration to use through the --config
and --profile
flags.
The first flag specifies the file to use, while the second one specifies the name of the configuration.
Here is an example using the ./myconfig
file with the default
profile to list all databases:
rai --config ./myconfig --profile default list-databases
To load a different configuration from within a configuration file, you can replace "default"
with a different profile name.
Managing Users
The CLI allows a client with the right permissions to create, disable, and list the users under an account.
Creating a User
You can create a user using the create-user
command in the CLI.
To create a user, you need to specify an email and, optionally, a set of roles for the user:
rai create-user user@relational.ai --role user
Here, user@relational.ai
is a string identifying the user.
The --role
flag allows you to provide a role for the specific user that is about to be created.
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 disable-user
command:
rai disable-user auth0|123456789
In this case, auth0|123456789
is a string representing a given user’s ID, as returned within the response in the user creation example above.
You can reenable the user by using the enable-user
command:
rai enable-user auth0|123456789
Deleting a User
You can delete a user in a way very similar to disabling them by using the delete-user
command:
rai delete-user auth0|123456789
Listing Users
You can list users using the list-users
command of the CLI as follows:
rai list-users
Retrieving User Details
You can retrieve user details from the system using the get-user
command of the CLI as follows:
rai get-user auth0|123456789
Here, similarly to disable-user
above, you need to provide 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 find-user
command of the CLI:
rai find-user user@relational.ai
Managing OAuth Clients
OAuth clients can be managed with the following commands, provided you have the corresponding permissions:
rai create-oauth-client name --perms 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:
rai list-oauth-clients
You can get details for a specific OAuth client, identified by the string client-id
, as follows:
rai get-oauth-client client-id
This is how to delete the OAuth client identified by the string client-id
:
rai delete-oauth-client client-id
Managing Engines
To query and update RelationalAI 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
:
rai create-engine my-engine
You can create an engine of a different size by specifying the size with the --size
flag:
rai create-engine my-engine --size M
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 list-engines
command:
rai list-engines
If you want to list engines that are in a given state, you can use the --state
flag as follows:
rai list-engines --state DELETED
Most of the API examples below assume there is a running engine in the “PROVISIONED” state as well as a test database.
Deleting an Engine
You can delete an engine with:
rai delete-engine my-engine
Note that since RelationalAI 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 the get-engine
command:
rai get-engine my-engine
Managing Databases
Creating a Database
You can create a database by using the create-database
command as follows:
rai create-database my-database
The result from a successful create-database
command will look like this:
{
"id": "xxxx-xxxx-xxx-xxxx-xxx",
"name": "my-database",
"region": "us-east",
"account_name": "acount-name",
"created_by": "xxxx@xyzw",
"created_on": "",
"state": "CREATED"
}
Cloning a Database
You can use the clone-database
command to clone a database by specifying the target and source databases in that order.
Here is an example that clones my-database
to my-database-clone
:
rai clone-database my-database-clone my-database
With this command, you create an identical copy of my-database
.
Any subsequent changes to either database will not affect the other.
Cloning a database fails if the source database does not exist.
You cannot clone from a database until an engine has executed at least one transaction on that database.
Retrieving Database Details
You can view the details of a database using the get-database
command:
rai get-database my-database
The response is a JSON object.
If the database does not exist, there will be an error reported as Not found
.
Listing Databases
Using the list-databases
command will list the databases available to the account:
rai list-databases
A variation of the list-databases
command takes a parameter --state
as input that can be used to filter databases by state.
Example states are: “CREATED”, “CREATING”, or “CREATION_FAILED”.
rai list-databases --status CREATED
Deleting Databases
A database can be deleted with the delete-database
command if the client has the right permissions.
The database is identified by its name, as used in the create-database
command:
rai delete-database my-database
Deleting a database cannot be undone.
Rel Models
Rel models are collections of Rel code that can be added, updated, or deleted from a dedicated database. A running engine — and a database — is required to perform operations on models.
Loading a Rel Model
The load-model
command loads a Rel model in a given database.
It takes as input the database, the filename where to read the model from, and a name for the model.
If no name is specified, the filename is used as the name of the model.
For example, consider the following simple Rel model, stored in a file named hello.rel:
def R = "hello", "world"
Here is an example that loads a model to my-database
using my-engine
from the hello.rel file:
rai load-model my-database hello.rel
In addition to the simple version of providing one model through a file, you can also provide a number of models using a list of files through the load-models
command.
Here is an example that loads model1.rel, model2.rel, and model3.rel in my-database
using my-engine
:
rai load-models my-database model1.rel model2.rel model3.rel
In case you want to group the models in the database, you can use the --prefix
string flag.
Here is an example that uses my-model
as a prefix:
rai load-models my-database model1.rel model2.rel model3.rel --prefix my-model
Note that if the database already contains an installed model with the same given name, it is replaced by the new one.
Deleting a Rel Model
You can delete a model from a database by using the delete-models
command and providing the model name
like this:
rai delete-models my-database model1
You can delete more than one model at a time by specifying multiple names in the delete-models
command like this:
rai delete-models my-database model1 model2 model3
Listing Installed Rel Models
You can list the models in a database using the list-model-names
command like this:
rai list-model-names my-database
This returns a JSON array of names.
To see the contents of a specific model — for example, my-model
— you can use:
rai get-model my-database my-model
You can also list all models together with their code using the list-models
command:
rai list-models my-database
Querying a Database
The high-level command for executing queries against the database is exec
.
The exec
command blocks until the transaction is completed or there are several timeouts indicating that the system may be inaccessible.
You can provide exec
with a query either through a command-line string or through a file.
Here is an example of the exec
command for a read query:
rai exec my-database --engine my-engine --code "def output[x in {1;2;3}] = x * 2"
Consider a file simple.rel
containing the following Rel code:
def output[x in {1;2;3}] = x * 2
You can execute the same query by specifying the file name as follows:
rai exec my-database --engine my-engine --file simple.rel
The output in both cases is:
/:output/Int64/Int64
1, 2
2, 4
3, 6
Note that the exec
command has an additional --readonly
flag.
By default, the --readonly
flag is false
.
Write queries, which update base relations through the control relations insert
and delete
,
must use --readonly false
.
Here is a Rel code that loads some CSV data and stores them in the base relation mybaserelation
:
def mydata = """
name,lastname,id
John,Smith,1
Peter,Jones,2
"""
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]
You can execute this Rel code as follows:
rai exec my-database --engine my-engine --file csv-example.rel
The query size is limited to 64MB. An error will be returned if the request exceeds this API limit.
Getting Multiple Relations Back
In order to return multiple relations, you can define subrelations of output
.
Here is an example:
rai exec my-database --engine my-engine --code \
"def a = 1;2 def b = 3;4 def output:one = a def output:two = b"
It gives the following output:
/:output/:one/Int64
1
2
/:output/:two/Int64
3
4
The default functionality of the CLI is to provide the input in this textual format. You can also request for a JSON output that is more detailed and that contains metadata about the transaction just performed. The output can also report any potential problems. For more details, see the Printing Responses section.
To get the output in JSON format, you can specify the --format json
flag as follows:
rai exec my-database --engine my-engine --format json --code \
"def a = 1;2 def b = 3;4 def output:one = a def output:two = b"
This produces a much more detailed output:
{
"GotCompleteResult": true,
"Transaction": {
"id": "12345678-1234-1234-1234-123456789012",
"state": "COMPLETED"
},
"Results": [
{
"RelationID": "/:output/:one/Int64",
"Table": [
[
1,
2
]
]
},
{
"RelationID": "/:output/:two/Int64",
"Table": [
[
3,
4
]
]
}
],
"Metadata": {
"relations": [
{
"relation_id": {
"arguments": [
{
"tag": 3,
"constant_type": {
"rel_type": {
"tag": 1,
"primitive_type": 16
},
"value": {
"arguments": [
{
"tag": 16,
"Value": {
"StringVal": "b3V0cHV0"
}
}
]
}
}
},
{
"tag": 3,
"constant_type": {
"rel_type": {
"tag": 1,
"primitive_type": 16
},
"value": {
"arguments": [
{
"tag": 16,
"Value": {
"StringVal": "b25l"
}
}
]
}
}
},
{
"tag": 1,
"primitive_type": 2
}
]
},
"file_name": "0.arrow"
},
{
"relation_id": {
"arguments": [
{
"tag": 3,
"constant_type": {
"rel_type": {
"tag": 1,
"primitive_type": 16
},
"value": {
"arguments": [
{
"tag": 16,
"Value": {
"StringVal": "b3V0cHV0"
}
}
]
}
}
},
{
"tag": 3,
"constant_type": {
"rel_type": {
"tag": 1,
"primitive_type": 16
},
"value": {
"arguments": [
{
"tag": 16,
"Value": {
"StringVal": "dHdv"
}
}
]
}
}
},
{
"tag": 1,
"primitive_type": 2
}
]
},
"file_name": "1.arrow"
}
]
},
"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
When the response is returned as a JSON string, it contains the following fields:
Field | Meaning |
---|---|
RelationID | This is a key for the relation, for example, "v1" . It refers to the column name in the Arrow table that contains the data, where "v" stands for variable, since a relation’s tuples contain several variables. |
Table | This 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
is also a JSON string containing internal system information on the relations — for example, for the types of the data.
Finally, problems
also contains a JSON string with the following fields:
Field | Meaning |
---|---|
error_code | The type of error that happened, for example, "PARSE_ERROR" . |
is_error | Whether an error occurred or there was some other problem. |
is_exception | Whether an exception occurred or there was some other problem. |
message | A short description of the problem. |
path | A file path for the cases when such a path was used. |
report | A long description of the problem. |
type | Type of the problem, for example, "ClientProblem" . |
Printing Responses
The CLI allows you to get responses back in two different formats by specifying the --format
flag.
The default format, called pretty
, returns results in text form.
Here is an example:
rai exec my-database --engine my-engine --code "def output='a';'b';'c'"
This is equivalent to:
rai exec my-database --engine my-engine --format pretty --code "def output='a';'b';'c'"
This command produces the following output:
/:output/Char
97
98
99
You can change the output format to JSON by changing the --format
flag.
Here is the same example producing JSON output:
rai exec my-database --engine my-engine --format json --code "def output='a';'b';'c'"
This command produces the following output:
{
"GotCompleteResult": true,
"Transaction": {
"id": "e3057f0d-27cb-6224-1086-f3d86bafe6ae",
"state": "COMPLETED"
},
"Results": [
{
"RelationID": "/:output/Char",
"Table": [
[
97,
98,
99
]
]
}
],
"Metadata": {
"relations": [
{
"relation_id": {
"arguments": [
{
"tag": 3,
"constant_type": {
"rel_type": {
"tag": 1,
"primitive_type": 16
},
"value": {
"arguments": [
{
"tag": 16,
"Value": {
"StringVal": "b3V0cHV0"
}
}
]
}
}
},
{
"tag": 1,
"primitive_type": 14
}
]
},
"file_name": "0.arrow"
}
]
},
"Problems": []
}
The JSON format has more detailed information on the results, including internal metadata and any potential problems that may have occured.
Here is an example code producing a problem since aaa
is undefined:
rai exec my-database --engine my-engine --format json --code "def output=aaa"
This populates the following text in the "Problems"
section of the JSON output:
{
...
"Problems": [
{
"type": "ClientProblem",
"error_code": "UNDEFINED",
"is_error": true,
"is_exception": false,
"message": "`aaa` is undefined.",
"report": "1| def output=aaa\n ^^^\n\nThe problem occurred while compiling declaration `output`:\n1| def output=aaa\n ^^^^^^^^^^^^^^\n\n"
}
]
}
Loading Data: load-csv
and load-json
Commands
As a convenience, the CLI includes load-csv
and load-json
commands.
These are not strictly necessary, since the load utilities in Rel itself can also be used for this task.
For more details, see Data I/O: CSV Import and Data I/O: JSON Import and Export.
The load-csv
command loads CSV data from a file and inserts the result into a base relation specified by the --relation
flag.
If no relation is specified, the data are loaded in a relation named after the file provided as a parameter to the load-csv
command.
Additionally, load-csv
attempts to guess the schema of the data.
For more control over the schema and parsing of the data, load-csv
provides additional flags.
Specifically:
--delim char
allows you to change the delimiter character from the default,
.--escapechar char
allows you to change the escape character from the default\
.--quotechar char
allows you to change the quote character from the default"
.--header-row int
allows you to specify the row number of the header. You should specify0
for no header. The default value is1
.--schema string
allows you to specify the schema of the imported data. If no schema string is specified,load-csv
imports all data as strings.
Here are the contents of an example CSV file with four columns four-cols.csv:
cocktail,quantity,price,date
"martini",2,12.50,"2020-01-01"
"sazerac",4,14.25,"2020-02-02"
"cosmopolitan",4,11.00,"2020-03-03"
"bellini",3,12.25,"2020-04-04"
You can load this file in the relation fourcols
using the CLI as follows:
rai load-csv my-database four-cols.csv --relation fourcols
You can view the loaded data using the exec
command:
rai exec my-database --code "def output=fourcols"
This gives the following output:
/:output/:cocktail/FilePos/String
2, martini
3, sazerac
4, cosmopolitan
5, bellini
/:output/:date/FilePos/String
2, 2020-01-01
3, 2020-02-02
4, 2020-03-03
5, 2020-04-04
/:output/:price/FilePos/String
2, 12.50
3, 14.25
4, 11.00
5, 12.25
/:output/:quantity/FilePos/String
2, 2
3, 4
4, 4
5, 3
Note that all columns from the CSV file were imported as strings.
You can specify the schema through the --schema
flag.
Here is an example that specifies a different schema for each column in the CSV file and loads the data in the fourcols_schema
relation:
rai load-csv my-database four-cols.csv --relation fourcols_schema \
--schema "cocktail:string;quantity:int;price:float;date:date"
You can now view the new loaded data using the exec
command:
rai exec my-database --engine my-engine --code "def output=fourcols_schema"
This gives the following output:
/:output/:cocktail/FilePos/String
2, martini
3, sazerac
4, cosmopolitan
5, bellini
/:output/:date/FilePos/Dates.Date
2, 737425
3, 737457
4, 737487
5, 737519
/:output/:price/FilePos/Float64
2, 12.5
3, 14.25
4, 11
5, 12.25
/:output/:quantity/FilePos/Int64
2, 2
3, 4
4, 4
5, 3
Similarly to load-csv
, load-json
loads the data from the specified file as JSON and inserts them into the base relation specified through the --relation
flag.
Here is an example JSON file john.json:
{
"first_name": "John",
"last_name": "Smith",
"address": { "city": "Seattle",
"state": "WA" },
"phone": [
{ "type": "home",
"number": 206456 },
{ "type": "work",
"number": 206123 }
]
}
You can load this data into a john
relation using the CLI as follows:
rai load-json my-database john.json --relation john
You can view the loaded data as follows:
rai exec my-database --engine my-engine --code "def output=john"
This gives the following output:
/:output/:address/:city/String
Seattle
/:output/:address/:state/String
WA
/:output/:first_name/String
John
/:output/:last_name/String
Smith
/:output/:phone/:[]/Int64/:number/Int64
1, 206456
2, 206123
/:output/:phone/:[]/Int64/:type/String
1, home
2, work
Note: In both load-csv
and load-json
, the base relation that is specified in the --relation
flag is not cleared, allowing for multipart, incremental loads.
To clear it, you can do:
def delete[:relation] = relation
Using the CLI:
rai exec my-database --engine my-engine --code "def delete[:john]=john"
This will clear the john
base relation.
Listing Base Relations
You can list base relations as follows:
rai list-edbs my-database
This will list the base relations in the given database (my-database
in the previous example).
The result is a JSON list of objects.