- Pure Nim wire protocol client (async + sync, no C FFI) - Query Builder: fluent API, SQL generator, execution, transactions - Schema Builder: create/alter/drop tables, column operations - Connection pool with aging and timeout handling - ORM integration via compile-time DB_BARADB switch - Tests: test_open, test_query - Documentation: README.md, PLAN_BARADB.md
12 KiB
Example: Query Builder for SurrealDB
Warning
SurrealDB support is planned for v3. The current implementation is for reference only and may not work as expected in v2.
index
About SurrealDB
SurrealDB official docs
SurrealDB Github
SurrealDB is a next-generation database built on Rust that can handle all type of data structures-relational, document, and graph-and can run in-memory, on a single node, or in a distributed environment.
It's response is JSON and allographer return as JsonNode.
Create Connection
import allographer/connection
let maxConnections = 95
let timeout = 30
let surreal = dbOpen(SurrealDb, "test", "test", "user", "pass", "http://surreal", 8000, maxConnections, timeout, true, true).await()
SurrealId type
The SurrealId type is used to handle SurrealDB Record IDs. It has the table name and id data.
insertId returns SurrealId response and can be used as an argument to the where and find clauses.
Nim API document
SurrealDB Official document
let aliceId = surreal.table("user").insertId(%*{"name": "alice"}).await
let aliceOpt = surreal.table("user").where("name", "=", "alice").first().await
let alice = aliceOpt.get()
check aliceId.rawId() == alice["id"].getStr()
check aliceId.table == alice["id"].getStr().split(":")[0]
check $aliceId == alice["id"].getStr().split(":")[1]
let alice2 = SurrealId.new(aliceId.rawId())
check alice2.rawId() == alice["id"].getStr()
check alice2.table == alice["id"].getStr().split(":")[0]
check $alice2 == alice["id"].getStr().split(":")[1]
let alice3 = SurrealId.new(aliceId.table, $aliceId)
check alice3.rawId() == alice["id"].getStr()
check alice3.table == alice["id"].getStr().split(":")[0]
check $alice3 == alice["id"].getStr().split(":")[1]
SELECT
https://surrealdb.com/docs/surrealql/statements/select
When it returns following table
INSERT INTO `type` [
{"bool":true,"string":"alice","int":1,"float":3.14,"datetime":"2023-06-05T11:58:42+00:00"},
{"bool":false,"string":"bob","int":2,"float":1.11,"datetime":"2023-06-06T11:58:42+00:00"}
]
| bool | string | int | float | datetime |
|---|---|---|---|---|
| true | alice | 1 | 3.14 | 2023-06-05T11:58:42+00:00 |
| false | bob | 2 | 1.11 | 2023-06-06T11:58:42+00:00 |
get
Retrieving all row from a table
import allographer/query_builder
echo surreal.table("user").get().await
DEBUG SELECT * FROM `type`
@[
{
"bool":true,
"datetime":"2023-06-05T12:05:07Z",
"float":3.14,
"id":"type:9nxye3dons0juyelv5if",
"int":1,
"string":"alice"
},
{
"bool":false,
"datetime":"2023-06-06T12:05:07Z"
,"float":1.11,
"id":"type:tw9iqdycdz9h1egtjkwb",
"int":2,
"string":"bob"
}
]
first
Retrieving a single row from a table. This returns Option[JsonNode]
let res = surreal.table("type").first().await
if res.isSome():
echo firstRes.get()
DEBUG SELECT * FROM `type` LIMIT 1
{
"bool":true,
"datetime":"2023-06-05T12:08:47Z",
"float":3.14,
"id":"type:64t0w6gpnye7tdf083ch",
"int":1,
"string":"alice"
}
find
Retrieve a single row by id. This returns Option[JsonNode]
Id should be SurrealId type.
https://itsumura-h.github.io/nim-allographer/query_builder/surreal/surreal_types.html#SurrealId
let id = SurrealId.new("type:64t0w6gpnye7tdf083ch")
let res = surreal.table("type").find(id).await
if res.isSome():
echo res.get()
DEBUG SELECT * FROM `type` WHERE `id` = ? LIMIT 1 ["type:64t0w6gpnye7tdf083ch"]
{
"bool":true,
"datetime":"2023-06-05T12:08:47Z",
"float":3.14,
"id":"type:64t0w6gpnye7tdf083ch",
"int":1,
"string":"alice"
}
columns
Retrieve columns from table.
let columns = surreal.table("users").columns().await
columns == @["email", "name", "id"]
where
user
| id | name | |
|---|---|---|
| user:2jrkvgwpdr02p71sfu1f | alice | alice@example.com |
let res = surreal.table("user").where("name", "=", "alice").first().await
if res.isSome():
echo res.get()
{
"email": "alice@example.com",
"id": "user:iss5w0fp4x3o08t2br3s",
"name": "alice"
}
fetch
Fetch and replace records with the remote record data.
It is used for retrieve relational tables.
auth
| id | name |
|---|---|
| auth:pl93k823yinrm8hzip22 | admin |
| auth:e3is4h0txnn6cpmcuoca | editor |
user
| id | name | auth | |
|---|---|---|---|
| user:2jrkvgwpdr02p71sfu1f | alice | alice@example.com | auth:pl93k823yinrm8hzip22 |
let alice = surreal.table("user").where("auth.name", "=", "admin").fetch("auth").first().await
if alice.isSome():
echo alice.get()
{
"auth": {
"id": "auth:ayp1w74u5oo8m4w2neyi",
"name": "admin"
},
"email": "alice@example.com",
"id": "user:5d7pgwag7i7uymigdako",
"name": "alice"
}
INSERT
https://surrealdb.com/docs/surrealql/statements/insert
insert single row
surreal.table("type").insert(%*{
"bool":true,
"string": "alice",
"int": 1,
"float": 3.14,
"datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz")
})
.await
insert rows
surreal.table("type").insert(%*[
{
"bool":true,
"string": "alice",
"int": 1,
"float": 3.14,
"datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz")
},
{
"bool":false,
"string": "bob",
"int": 2,
"float": 1.11,
"datetime": (now() + initDuration(days=1)).format("yyyy-MM-dd'T'HH:mm:sszzz")
}
])
.await
insertId
let id:SurrealId = surreal.table("type").insertId(%*{
"bool":true,
"string": "alice",
"int": 1,
"float": 3.14,
"datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz")
})
.await
echo id.rawId()
type:9nxye3dons0juyelv5if
UPDATE
https://surrealdb.com/docs/surrealql/statements/update
let id = surreal.table("type").insertId(%*{
"bool":true,
"string": "alice",
"int": 1,
"float": 3.14,
"datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz")
})
.await
surreal.table("type").where("id", "=", id).update(%*{"string": "bob"}).await
let res = surreal.table("type").find(id).await
if res.isSome():
echo res.get()
{
"bool":true,
"datetime":"2023-06-06T02:34:25Z",
"float":3.14,
"id":"type:hp0aqct78btlcyr1ho06",
"int":1,
"string":"bob"
}
DELETE
https://surrealdb.com/docs/surrealql/statements/delete
delete all
let id = surreal.table("type").insertId(%*{
"bool":true,
"string": "alice",
"int": 1,
"float": 3.14,
"datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz")
})
.await
surreal.table("type").delete().await
echo surreal.table("type").get().await
@[]
delete row
surreal.table("type").insert(%*[
{
"bool":true,
"string": "alice",
"int": 1,
"float": 3.14,
"datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz")
},
{
"bool":false,
"string": "bob",
"int": 2,
"float": 1.11,
"datetime": (now() + initDuration(days=1)).format("yyyy-MM-dd'T'HH:mm:sszzz")
}
])
.await
let row = surreal.table("type").where("string", "=", "alice").first().await.get()
let rowId = SurrealId.new(row["id"].getStr())
surreal.table("type").where("id", "=", rowId).delete().await
echo surreal.table("type").get().await
@[
{
"bool":false,
"datetime":"2023-06-07T03:39:14Z",
"float":"1.11",
"id":"type:y93d6ig6iyrvcez0lc9k",
"int":2,
"string":"bob"
}
]
Raw Query
raw() returns RawQuerySurrealDb type.
You can use get(), first(), exec() and info() for raw query.
get() returns all rows of result.
first() returns first row of result.
exec() executes query and not return any variables.
info() executes query and returns all data of response.
let define = """
REMOVE TABLE type;
DEFINE TABLE type SCHEMAFULL;
DEFINE FIELD index ON TABLE type TYPE int;
DEFINE INDEX types_index ON TABLE type COLUMNS index UNIQUE;
DEFINE FIELD bool ON TABLE type TYPE bool;
DEFINE FIELD datetime ON TABLE type TYPE datetime;
DEFINE FIELD float ON TABLE type TYPE float;
DEFINE FIELD int ON TABLE type TYPE int;
DEFINE FIELD string ON TABLE type TYPE string;
"""
surreal.raw(define).exec().await()
echo surreal.raw("INFO FOR TABLE type").info().await
>> [
{
"time":"32.762515ms",
"status":"OK",
"result":{
"ev":{},
"fd":{
"bool":"DEFINE FIELD bool ON type TYPE bool",
"datetime":"DEFINE FIELD datetime ON type TYPE datetime",
"float":"DEFINE FIELD float ON type TYPE float",
"index":"DEFINE FIELD index ON type TYPE int",
"int":"DEFINE FIELD int ON type TYPE int",
"string":"DEFINE FIELD string ON type TYPE string"
},
"ft":{},
"ix":{
"types_index":"DEFINE INDEX types_index ON type FIELDS index UNIQUE"
}
}
}
]
var rows = surreal.raw("SELECT * FROM type").get().await
let max = rows.len + 1
surreal.raw(
"""
CREATE type CONTENT {
index: ?,
bool: true,
datetime: ?,
float: 1.11,
int: 1,
string: "aaa"
}
""",
$max, now().format("yyyy-MM-dd'T'HH:mm:sszzz")
)
.exec()
.await
rows = surreal.raw("SELECT * FROM type").get().await
echo rows
>> @[
{
"bool":true,
"datetime":"2023-06-06T04:01:32Z",
"float":1.11,
"id":"type:f1qxioacts8cydbmchpy",
"index":1,
"int":1,
"string":"aaa"
}
]
Prepared Statement
SurrealDB の prepared statement は documents/surrealdb/prepared_statement.md にまとめています。prepare(), get(), first(), exec(), close(), flushStmt(stmt), clearStmtCache(), withConn() が利用できます。
let stmt = surreal.prepare("""SELECT * FROM "type" WHERE "id" = ?""")
let row = await stmt.first(@["type:9nxye3dons0juyelv5if"])
await stmt.close()
Aggregates
count
surreal.table("user").insert(%*[
{"name": "alice", "email": "alice@example.com"},
{"name": "bob", "email": "bob@example.com"},
{"name": "charlie", "email": "charlie@example.com"},
])
.await
let count = surreal.table("user").count().await
echo count
>> 3
max
surreal.table("user").insert(%*[
{"name": "alice", "email": "alice@example.com", "index": 1},
{"name": "bob", "email": "bob@example.com", "index": 2},
{"name": "charlie", "email": "charlie@example.com", "index": 3},
])
.await
let max = surreal.table("user").max("index").await
echo max
>> 3
min
surreal.table("user").insert(%*[
{"name": "alice", "email": "alice@example.com", "index": 1},
{"name": "bob", "email": "bob@example.com", "index": 2},
{"name": "charlie", "email": "charlie@example.com", "index": 3},
])
.await
let min = surreal.table("user").min("index").await
echo min
>> 1