6bfc5b3a3c
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
- 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
548 lines
12 KiB
Markdown
548 lines
12 KiB
Markdown
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.
|
|
|
|
[back](../../README.md)
|
|
|
|
## index
|
|
<!--ts-->
|
|
* [Example: Query Builder for SurrealDB](#example-query-builder-for-surrealdb)
|
|
* [index](#index)
|
|
* [About SurrealDB](#about-surrealdb)
|
|
* [Create Connection](#create-connection)
|
|
* [SurrealId type](#surrealid-type)
|
|
* [SELECT](#select)
|
|
* [get](#get)
|
|
* [first](#first)
|
|
* [find](#find)
|
|
* [columns](#columns)
|
|
* [where](#where)
|
|
* [fetch](#fetch)
|
|
* [INSERT](#insert)
|
|
* [insert single row](#insert-single-row)
|
|
* [insert rows](#insert-rows)
|
|
* [insertId](#insertid)
|
|
* [UPDATE](#update)
|
|
* [DELETE](#delete)
|
|
* [delete all](#delete-all)
|
|
* [delete row](#delete-row)
|
|
* [Raw Query](#raw-query)
|
|
* [Prepared Statement](#prepared-statement)
|
|
* [Aggregates](#aggregates)
|
|
* [count](#count)
|
|
* [max](#max)
|
|
* [min](#min)
|
|
|
|
<!-- Created by https://github.com/ekalinin/github-markdown-toc -->
|
|
<!-- Added by: root, at: Mon Jul 17 07:46:29 UTC 2023 -->
|
|
|
|
<!--te-->
|
|
|
|
---
|
|
|
|
## About SurrealDB
|
|
[SurrealDB official docs](https://surrealdb.com/docs)
|
|
[SurrealDB Github](https://github.com/surrealdb/surrealdb)
|
|
|
|
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
|
|
[to index](#index)
|
|
|
|
```nim
|
|
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](https://itsumura-h.github.io/nim-allographer/query_builder/surreal/surreal_types.html#SurrealId)
|
|
[SurrealDB Official document](https://surrealdb.com/docs/surrealql/datamodel/ids)
|
|
|
|
```nim
|
|
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
|
|
[to index](#index)
|
|
|
|
https://surrealdb.com/docs/surrealql/statements/select
|
|
|
|
When it returns following table
|
|
|
|
```sql
|
|
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
|
|
|
|
```nim
|
|
import allographer/query_builder
|
|
|
|
echo surreal.table("user").get().await
|
|
```
|
|
|
|
```nim
|
|
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]`
|
|
|
|
```nim
|
|
let res = surreal.table("type").first().await
|
|
if res.isSome():
|
|
echo firstRes.get()
|
|
```
|
|
|
|
```nim
|
|
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
|
|
|
|
```nim
|
|
let id = SurrealId.new("type:64t0w6gpnye7tdf083ch")
|
|
let res = surreal.table("type").find(id).await
|
|
if res.isSome():
|
|
echo res.get()
|
|
```
|
|
|
|
```nim
|
|
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.
|
|
```nim
|
|
let columns = surreal.table("users").columns().await
|
|
columns == @["email", "name", "id"]
|
|
```
|
|
|
|
|
|
### where
|
|
user
|
|
|id|name|email|
|
|
|---|---|---|
|
|
|user:2jrkvgwpdr02p71sfu1f|alice|alice@example.com|
|
|
|
|
```nim
|
|
let res = surreal.table("user").where("name", "=", "alice").first().await
|
|
if res.isSome():
|
|
echo res.get()
|
|
```
|
|
|
|
```nim
|
|
{
|
|
"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|email|auth|
|
|
|---|---|---|---|
|
|
|user:2jrkvgwpdr02p71sfu1f|alice|alice@example.com|auth:pl93k823yinrm8hzip22|
|
|
|
|
```nim
|
|
let alice = surreal.table("user").where("auth.name", "=", "admin").fetch("auth").first().await
|
|
if alice.isSome():
|
|
echo alice.get()
|
|
```
|
|
|
|
```nim
|
|
{
|
|
"auth": {
|
|
"id": "auth:ayp1w74u5oo8m4w2neyi",
|
|
"name": "admin"
|
|
},
|
|
"email": "alice@example.com",
|
|
"id": "user:5d7pgwag7i7uymigdako",
|
|
"name": "alice"
|
|
}
|
|
```
|
|
|
|
## INSERT
|
|
[to index](#index)
|
|
|
|
https://surrealdb.com/docs/surrealql/statements/insert
|
|
|
|
### insert single row
|
|
```nim
|
|
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
|
|
```nim
|
|
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
|
|
```nim
|
|
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()
|
|
```
|
|
|
|
```nim
|
|
type:9nxye3dons0juyelv5if
|
|
```
|
|
|
|
## UPDATE
|
|
[to index](#index)
|
|
|
|
https://surrealdb.com/docs/surrealql/statements/update
|
|
|
|
```nim
|
|
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()
|
|
```
|
|
|
|
```nim
|
|
{
|
|
"bool":true,
|
|
"datetime":"2023-06-06T02:34:25Z",
|
|
"float":3.14,
|
|
"id":"type:hp0aqct78btlcyr1ho06",
|
|
"int":1,
|
|
"string":"bob"
|
|
}
|
|
```
|
|
|
|
## DELETE
|
|
[to index](#index)
|
|
|
|
https://surrealdb.com/docs/surrealql/statements/delete
|
|
|
|
## delete all
|
|
```nim
|
|
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
|
|
```
|
|
|
|
```nim
|
|
@[]
|
|
```
|
|
|
|
## delete row
|
|
```nim
|
|
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
|
|
```
|
|
|
|
```nim
|
|
@[
|
|
{
|
|
"bool":false,
|
|
"datetime":"2023-06-07T03:39:14Z",
|
|
"float":"1.11",
|
|
"id":"type:y93d6ig6iyrvcez0lc9k",
|
|
"int":2,
|
|
"string":"bob"
|
|
}
|
|
]
|
|
```
|
|
|
|
## Raw Query
|
|
[to index](#INDEX)
|
|
|
|
`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.
|
|
|
|
```nim
|
|
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
|
|
[to index](#INDEX)
|
|
|
|
SurrealDB の prepared statement は [`documents/surrealdb/prepared_statement.md`](./prepared_statement.md) にまとめています。`prepare()`, `get()`, `first()`, `exec()`, `close()`, `flushStmt(stmt)`, `clearStmtCache()`, `withConn()` が利用できます。
|
|
|
|
```nim
|
|
let stmt = surreal.prepare("""SELECT * FROM "type" WHERE "id" = ?""")
|
|
let row = await stmt.first(@["type:9nxye3dons0juyelv5if"])
|
|
await stmt.close()
|
|
```
|
|
|
|
## Aggregates
|
|
[to index](#INDEX)
|
|
|
|
### count
|
|
```nim
|
|
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
|
|
```
|
|
|
|
```sh
|
|
>> 3
|
|
```
|
|
|
|
### max
|
|
```nim
|
|
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
|
|
```nim
|
|
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
|
|
```
|