Within my company, we organize bi-weekly tech-sharing sessions where colleagues have the opportunity to share any technology they find interesting or have learned about. Inspired by these sessions, I have decided to take it a step further and transform my presentations into articles for my blog. This has led me to introduce a new tech-sharing series, where I can share the valuable knowledge with a wider audience.
TL;DR
SurrealDB is a multi-model database system written in Rust. It provides faster performance and stability by leveraging Rust’s memory safety and optimizations. It supports three modes: Embedded Mode using RocksDB for high-performance key-value data, Distributed Mode using TiKV or FoundationDB for transaction-based queries with ACID compliance, and In-web browser mode using IndexedDB for offline-capable web applications. SurrealDB supports multiple server-side and client-side programming languages. It introduces SurrealQL, a query language similar to traditional SQL, with features like record creation, selection, and relationship handling. Other key features include future data types and full-text search with analyzers.
What is SurrealDB?
It is a new database system which first release tag was found in December 2019 and stabilised on September 2023. The software is written in Rust and it provides faster performance and stability due to Rust’s memory safe and optimisations. The current release (as of 24 March, 2024) is 1.3.0.
Architecture
Is SurrealDB completely reinvent the wheel? Much not. SurrealDB only rewrites the logical part, they call it “Query Layer”, such as query planning, data processing and the communications to “Storage Layer”.
Depends on the usage, SurrealDB can provides three modes,
- Embedded Mode SurrealDB uses RocksDB in this mode, which provides high-performance database for key-value data. It is optimised for many CPU cores and fast storage. It is based on a log-structured merge-tree or LSM Tree data structure.
- Distributed Mode TiKV or FoundationDB will be used for transaction-based query with ACID compliance. A distributed database provides scaling support and fault tolerance.
- In web browser There is a database engine embedded into web browsers called IndexedDB. It is also a transaction database system. Since all data is stored in web browsers, developers can create applications that can work online and offline.
Supported Languages / SDK
As of 24 March, 2024, SurrealDB supports and will support the following languages.
Server-side
- Rust
- Node JS / Deno
- Python
- Go
- .NET
- Java
- C (Future)
- Ruby (Future)
- PHP (Future)
Client-side
- Javascript
- SSR frameworks (Future)
- Flutter (Future)
Familiar Syntax
SurrealDB creates a query language called SurrealQL and it is like traditional SQL.
CREATE
We need to use INSERT INTO
in order to insert new records in traditional DBs. However, SurrealQL uses CREATE
to add new records.
-- Traditional
INSERT INTO authors (first_name, last_name) VALUES ("John", "Doe");
/*
| ID | first_name | last_name |
| 1 | John | Doe |
*/
-- SurrealQL
CREATE author SET
first_name = 'John',
last_name = 'Doe',
;
/*
[
{
"id": "author:n8hnlo73e3u702mn6jbb",
"first_name": "John",
"last_name": "Doe"
}
]
*/
SurrealQL uses random string as the record ID by default and it also supports other record ID types natively.
--- Text Record IDs
CREATE author:john SET first_name = 'John';
CREATE author:`8424486b-85b3-4448-ac8d-5d51083391c7` SET first_name = 'John';
CREATE author:⟨8424486b-85b3-4448-ac8d-5d51083391c7⟩ SET first_name = 'John';
--- Numeric Record IDs
CREATE author:1 SET first_name = 'John';
-- Note:
CREATE author SET id = "9876";
-- becomes: author:⟨9876⟩
LET $now = time::now();
--- Object-based Record IDs
CREATE temperature:{ location: 'London', date: $now } SET
location = 'London',
date = $now,
temperature = 23.7
;
--- Array-based Record IDs
CREATE temperature:['London', $now] SET
location = 'London',
date = $now,
temperature = 23.7
;
SELECT
There is no big difference between traditional SQL and SurrealQL.
-- Traditional
SELECT * FROM author;
-- SurrealQL
SELECT * FROM author;
But when we want to query a model with its id. The query becomes:
-- Traditional
SELECT * FROM author = 1;
-- SurrealQL
-- Select ID directly
SELECT * FROM author:john;
-- Extended
SELECT * FROM author WHERE id = "author:john";
Relationships
When you created a record with record linkings, you may need to use FETCH
instead of joining tables. Let SurrealDB does that for you.
SELECT * FROM article WHERE author.age < 30;
[
// ...
{
// These attributes are record IDs
"account": "account:not_a_company",
"author": "author:52u81v4c0zrh0foylxoq",
// The rest of attributes
"created_at": "2023-11-26T00:47:59.963204425Z",
"id": "author:ken",
"text": "Donec eleifend, nunc vitae commodo accumsan, mauris est fringilla.",
"title": "Lorem ipsum dolor"
},
// ...
]
After adding FETCH
keyword and the attributes you want to retrieve.
SELECT * FROM article FETCH author, account;
[
// ...
{
// SurrealDB helps you replace the record IDs with the actual record
"account": {
"created_at": "2023-11-26T00:37:18.221926586Z",
"id": "account:not_a_company",
"name": "Not A Company Inc."
},
"author": {
"admin": true,
"age": 18,
"id": "author:ken",
"name": {
"first": "Ken",
"full": "Ken Lai",
"last": "Lai"
},
"signup_at": "2023-11-26T00:47:29.495798424Z"
},
// The rest of attributes
"created_at": "2023-11-26T00:47:59.963204425Z",
"id": "article:7akl7s4c1lfzj2pl94eq",
"text": "Donec eleifend, nunc vitae commodo accumsan, mauris est fringilla.",
"title": "Lorem ipsum dolor"
}
// ...
]
Other Key Features
Data Type - Future
This data type works like generated columns VIRTUAL STORED
in traditional database.
UPDATE product SET
name = "SurrealDB",
launch_at = "2021-11-01",
countdown = <future> { launch_at - time::now() }
;
Full-text search
SurrealDB provides ANALYZER
to help you working on text processing and searching.
Here are two examples for demonstrating how tokenisers and filters work.
-- Input text: A quick brown fox jumps over a lazy dog.
DEFINE ANALYZER example_class TOKENIZERS class;
-- ["A", "quick", "brown", "fox", ...]
DEFINE ANALYZER example_ngram TOKENIZERS class FILTERS lowercase, ngram(1,3);
/*
[
"a", "q", "u", "i", "c", "k",
"qu", "ui", "ic", "ck",
"qui", "uic", "ick", ...
]
*/