Data Sets
Most of Rust apps operate with data which is readily available in memory. Business apps are stateless, loading too much data is an anti-pattern.
Depending of the capabilities of the underlying data storage layer, you should look to create as many requests and retrieve as little data as possible.
DataSet
is is a representation of collection of records - stored remotely.
ReadableDataSet and WritableDataSet
DORM provides two traits: ReadableDataSet
and WritableDataSet
. DORM also provides
several implementations of these traits:
sql::Table
- implements bothReadableDataSet
andWritableDataSet
.sql::Query
- implementsReadableDataSet
only.
Design of DORM allow you to extend those into NoSQL (like MongoDB or GraphQL sources) as well as custom RestAPI sources. Those extensions do not need to be part of DORM, they can be implemented as separate crates.
Operating with Data sets
At the very basic level - you can iterate through a readable data set.
#![allow(unused)] fn main() { let set_of_clients = Client::table(); for client in set_of_clients.get().await? { println!("{}", client.name); } }
But quite often the advanced persistence layer could allow us to do much more. DORM approach is to provide ways how one Data Set can yield a different Data Set. Here are some examples:
- Get a set containing subset of records - by filtering.
- Converting
sql::table
intosql::query
for further manipulation. - Execute operation over set, such as calculate sum of a field from all records, or create comma-separated list of values.
- Modify or delete multiple records.
DORM prefers to off-load operation execution to the persistence layer, but because this may increase complexity, DORM also provides a way to abstract this complexity away.
Example - Lazy expression fields
In our introduction example, we came across an aggregate field: total
:
#![allow(unused)] fn main() { table .has_many("line_items", "order_id", || Box::new(LineItem::table())) .with_expression("total", |t| { let item = t.sub_line_items(); item.sum(item.total()).render_chunk() }) }
Lets examine more generally what is happening here. Use of has_many
creates
This is a very simple example of a lazy expression field. It is a field that is calculated by a closure. The closure is passed a reference to the table. The closure can then use the table to create a new field.
The above example is equivalent to this SQL:
SELECT id,
(SELECT SUM((SELECT price FROM product WHERE id = product_id) * quantity)
FROM order_line WHERE order_line.order_id = ord.id) AS total
FROM ord
In this example, we are using a sub-query to calculate the total. The sub-query is
created by calling sub_line_items()
on the table. This method returns a new table
that is a subset of the original table. The sub-query is then used to create a new
field called total
that is a sum of the price and quantity.
The implementation
of sql::Table
however provides ability to create new Data Sets from existing ones.