Surreal simple querybuilder
A simple query-builder for the Surreal Query Language, for SurrealDB. Aims at being simple to use and not too verbose first.
Summary
Why a query-builder
Query builders allow you to dynamically build your queries with some compile time checks to ensure they result in valid SQL queries. Unlike ORMs, query-builders are built to be lightweight and easy to use, meaning you decide when and where to use one. You could stick to hard coded string for the simple queries but use a builder for complex ones that require parameters & variables and may change based on these variables for example.
While the crate is first meant as a query-building utility, it also comes with macros and generic types that may help you while managing you SQL models in your rust code. Refer to the node macro and the Foreign type example
SQL injections
The strings you pass to the query builder are not sanitized in any way. Please use
parameters in your queries like SET username = $username
with surrealdb parameters to avoid injection issues.
However the crate comes with utility functions to easily create parameterized fields, refer to the NodeBuilder
trait.
Examples
A complete example can be found in the test.rs
file. For an explanation of what each component in the crate does, refer to the chapters below.
The node
macro
The node
macro allows you to quickly create constants that match the fields of
your structs. It is not a derive macro to allow you to name the fields the way
you want.
use *;
node!;
This allows you to have compile time checked constants for your fields, allowing you to reference them while building your queries without fearing of making a typo or using a field you renamed long time ago.
The NodeBuilder
traits
These traits add a few utility functions to the String
and str
types that can
be used alongside the querybuilder for even more flexibility.
use *;
let my_label = "John".as_named_label;
assert_eq!;
let my_relation = my_label
.with
.with;
assert_eq!;
The QueryBuilder
type
It allows you to dynamically build complex or simple queries out of segments and easy to use methods.
use *;
let query = new
.select
.from
.build;
assert_eq!;
use *;
let should_fetch_authors = false;
let query = new
.select
.from
.if_then
.build;
assert_eq!;
let should_fetch_authors = true;
let query = new
.select
.from
.if_then
.build;
assert_eq!;
The ForeignKey
and Foreign
types
SurrealDB has the ability to fetch the data out of foreign keys. For example:
create Author:JussiAdlerOlsen set name = "Jussi Adler-Olsen";
create File set name = "Journal 64", author = Author:JussiAdlerOlsen;
select * from File;
select * from File fetch author;
which gives us
// without FETCH author
// with FETCH author
The "issue" with this functionality is that our results may either contain an ID
to the author, no value, or the fully fetched author with its data depending on
the query and whether it includes fetch
or not.
The ForeignKey
types comes to the rescue. It is an enum with 3 variants:
- The loaded data for when it was fetched
- The key data for when it was just an ID
- The unloaded data when it was null (if you wish to support missing data you must use the
#serde(default)
attribute to the field)
The type comes with an implementation of the Deserialize and Serialize serde traits
so that it can fallback to whatever data it finds or needs. However any type that
is referenced by a ForeignKey
must implement the IntoKey
trait that allows it
to safely serialize it into an ID during serialization.
/// For the tests, and as an example we are creating what could be an Account in
/// a simple database.