Skip to content

SurrealDB - Multi-Model Database

Published: at 03:00 PM

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

SurrealDB 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,

Supported Languages / SDK

As of 24 March, 2024, SurrealDB supports and will support the following languages.

Server-side

Client-side

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() }
;

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", ...
]
*/