Skip to content

Commit

Permalink
docs: init user documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
crwen committed Dec 25, 2024
1 parent f60ba3f commit ffcb618
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
Cargo.lock
db_path
bindings/python/target
guide/book
11 changes: 11 additions & 0 deletions guide/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[book]
authors = ["crwen"]
language = "en"
multilingual = false
src = "src"
title = "The Tonbo Guide"

[output.html]
git-repository-url = "https://github.com/tonbo-io/tonbo"
[output.html.playground]
runnable = false
11 changes: 11 additions & 0 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Summary

[Introduction](./introduction.md)

- [Getting started](./start.md)
- [Examples](./examples/index.md)
- [Using Tonbo](./examples/declare.md)
- [Integrate with Datafusio](./examples/datafusion.md)
- [Using under Wasm](./examples/wasm.md)
- [Contribution](./contribution/index.md)
- [Building](./contribution/build.md)
3 changes: 3 additions & 0 deletions guide/src/contribution/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Building Tonbo

TODO
Empty file added guide/src/contribution/index.md
Empty file.
3 changes: 3 additions & 0 deletions guide/src/examples/datafusion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Integrate with Datafusio

TODO
102 changes: 102 additions & 0 deletions guide/src/examples/declare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Using Tonbo

define your schema

```rust
use tonbo::Record;

/// Use macro to define schema of column family just like ORM
/// It provides type-safe read & write API
#[derive(Record, Debug)]
pub struct User {
#[record(primary_key)]
name: String,
email: Option<String>,
age: u8,
bytes: Bytes,
}
```

```rust
use std::ops::Bound;

use bytes::Bytes;
use fusio::path::Path;
use futures_util::stream::StreamExt;
use tokio::fs;
use tonbo::{executor::tokio::TokioExecutor, DbOption, Projection, Record, DB};


#[tokio::main]
async fn main() {
// make sure the path exists
let _ = fs::create_dir_all("./db_path/users").await;

let options = DbOption::new(
Path::from_filesystem_path("./db_path/users").unwrap(),
&UserSchema,
);
// pluggable async runtime and I/O
let db = DB::new(options, TokioExecutor::current(), UserSchema)
.await
.unwrap();

// insert with owned value
db.insert(User {
name: "Alice".into(),
email: Some("[email protected]".into()),
age: 22,
bytes: Bytes::from(vec![0, 1, 2]),
})
.await
.unwrap();

{
// tonbo supports transaction
let txn = db.transaction().await;

// get from primary key
let name = "Alice".into();

// get the zero-copy reference of record without any allocations.
let user = txn
.get(
&name,
// tonbo supports pushing down projection
Projection::All,
)
.await
.unwrap();
assert!(user.is_some());
assert_eq!(user.unwrap().get().age, Some(22));

{
let upper = "Blob".into();
// range scan of user
let mut scan = txn
.scan((Bound::Included(&name), Bound::Excluded(&upper)))
// tonbo supports pushing down projection
.projection(vec![1, 3])
// push down limitation
.limit(1)
.take()
.await
.unwrap();
while let Some(entry) = scan.next().await.transpose().unwrap() {
assert_eq!(
entry.value(),
Some(UserRef {
name: "Alice",
email: Some("[email protected]"),
age: None,
bytes: Some(&[0, 1, 2]),
})
);
}
}

// commit transaction
txn.commit().await.unwrap();
}
}
```
1 change: 1 addition & 0 deletions guide/src/examples/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Examples of using Tonbo
72 changes: 72 additions & 0 deletions guide/src/examples/wasm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

# Using under Wasm

This is the Wasm example of tonbo showing how to use tonbo under Wasm.

## `Cargo.toml`

Since only limited features of tokio can be used in wasm, we need to disable tokio and use `wasm` feature in tonbo.

```toml
fusio = { git = "https://github.com/tonbo-io/fusio.git", rev = "216eb446fb0a0c6e5e85bfac51a6f6ed8e5ed606", package = "fusio", version = "0.3.3", features = [
"dyn",
"fs",
] }
tonbo = { git = "https://github.com/tonbo-io/tonbo", default-features = false, features = ["wasm"] }
```

## Create DB

Tonbo provide [OPFS(origin private file system)](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) as storage backend, but the path is a little different. You should use `Path::from_opfs_path` or `Path::parse` rather than `Path::from_filesystem_path` and it is not permitted to use paths that temporarily step outside the sandbox with something like `../foo` or `./bar`.

```rust
use fusio::path::Path;
use tonbo::{executor::opfs::OpfsExecutor, DbOption, DB};

async fn main() {

let options = DbOption::new(
Path::from_opfs_path("db_path/users").unwrap(),
&UserSchema,
);
let db = DB::<User, OpfsExecutor>::new(options, OpfsExecutor::new(), UserSchema)
.await
.unwrap();
}
```

## Operations on DB

After create `DB` instance, you can operate it as usual

```rust
let txn = db.transaction().await;

// get from primary key
let name = "Alice".into();

let user = txn.get(&name, Projection::All).await.unwrap();

let upper = "Blob".into();
// range scan of user
let mut scan = txn
.scan((Bound::Included(&name), Bound::Excluded(&upper)))
// tonbo supports pushing down projection
.projection(vec![1])
// push down limitation
.limit(1)
.take()
.await
.unwrap();

while let Some(entry) = scan.next().await.transpose().unwrap() {
assert_eq!(
entry.value(),
Some(UserRef {
name: "Alice",
email: Some("[email protected]"),
age: None,
})
);
}
```
11 changes: 11 additions & 0 deletions guide/src/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# The Tonbo user guide
Welcome to the tonbo user guide! This book is about [tonbo](https://github.com/tonbo-io/tonbo). Tonbo is an embedded, persistent database offering fast KV-like methods for conveniently writing and scanning type-safe structured data. Tonbo can be used to build data-intensive applications, including other types of databases.


The rough order of material in this user guide is as follows:
1. Getting started
2. Examples on using tonbo
3. How to make contributions to Tonbo


If you want to learn the design of tonbo, you can see this [blog](https://tonbo.io/blog/introducing-tonbo).
166 changes: 166 additions & 0 deletions guide/src/start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
## Installation

To get started using tonbo you should make sure you have Rust installed on your system. If you haven't alreadly done yet, try following the instructions [here](https://www.rust-lang.org/tools/install).

## Adding dependencies

```toml
fusio = { git = "https://github.com/tonbo-io/fusio.git", rev = "216eb446fb0a0c6e5e85bfac51a6f6ed8e5ed606", package = "fusio", version = "0.3.3", features = [
"dyn",
"fs",
] }
tokio = { version = "1", features = ["full"] }
tonbo = { git = "https://github.com/tonbo-io/tonbo" }
```

## Defining Schema

You can use `Record` macro to define schema of column family just like ORM. Tonbo will generate all relevant files for you at compile time.

```rust
use tonbo::Record;

#[derive(Record, Debug)]
pub struct User {
#[record(primary_key)]
name: String,
email: Option<String>,
age: u8,
bytes: Bytes,
}
```

- `Record`: Declare this struct as a Tonbo Schema
- `#[record(primary_key)]`: Declare this key as primary key. Compound primary key is not supported now.
- `Option` type represents this field can be null, otherwise it can not be null.

Now, Tonbo support these types:

- Number type: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`
- Boolean type: `bool`
- String type: `bool`
- Bytes: `bytes::Bytes`

## Create DB

After define you schema, you can create `DB` with a customized `DbOption`

```rust
use std::fs;
use fusio::path::Path;
use tonbo::{executor::tokio::TokioExecutor, DbOption, DB};

#[tokio::main]
async fn main() {
// make sure the path exists
fs::create_dir_all("./db_path/users").unwrap();

let options = DbOption::new(
Path::from_filesystem_path("./db_path/users").unwrap(),
&UserSchema,
);
let db = DB::<User, TokioExecutor>::new(options, TokioExecutor::current(), UserSchema)
.await
.unwrap();
}
```

`UserSchema` is a struct that tonbo generates for you in the compile time, so you do not need to import it.

## Read/Write data

After create `DB`, you can execute `insert`, `remove`, `get` now. But remember that you will get a `UserRef` object rather than the `User`, if you get record from tonbo. This is a struct that tonbo generates for you in the compile time.

```rust
db.insert(User {
name: "Alice".into(),
email: Some("[email protected]".into()),
age: 22,
})
.await
.unwrap();

let age = db
.get(&"Alice".into(), |entry| {
// entry.get() will get a `UserRef`
let user = entry.get();
println!("{:#?}", user);
user.age
})
.await
.unwrap();
assert!(age.is_some());
assert_eq!(age, Some(22));
```

## Using transaction

Tonbo supports transaction. You can also push down filter, limit and projection operators in query.

```rust
let txn = db.transaction().await;

// get from primary key
let name = "Alice".into();

// get the zero-copy reference of record without any allocations.
let user = txn.get(&name, Projection::All).await.unwrap();

let upper = "Blob".into();
// range scan of user
let mut scan = txn
.scan((Bound::Included(&name), Bound::Excluded(&upper)))
// tonbo supports pushing down projection
.projection(vec![1])
// push down limitation
.limit(1)
.take()
.await
.unwrap();

while let Some(entry) = scan.next().await.transpose().unwrap() {
assert_eq!(
entry.value(),
Some(UserRef {
name: "Alice",
email: Some("[email protected]"),
age: None,
})
);
}
```

## Using S3 backends

Tonbo supports various storage backends, such as OPFS, S3, and maybe more in the future. You can use `DbOption::level_path` to specify which backend to use.

For local storage, you can use `FsOptions::Local` as the parameter. And you can use `FsOptions::S3` for S3 storage. After create `DB`, you can then operator it like normal.

```rust
use fusio::{path::Path, remotes::aws::AwsCredential};
use fusio_dispatch::FsOptions;
use tonbo::{executor::tokio::TokioExecutor, DbOption, DB};

#[tokio::main]
async fn main() {
let fs_option = FsOptions::S3 {
bucket: "wasm-data".to_string(),
credential: Some(AwsCredential {
key_id: "key_id".to_string(),
secret_key: "secret_key".to_string(),
token: None,
}),
endpoint: None,
sign_payload: None,
checksum: None,
region: Some("region".to_string()),
};

let options = DbOption::new(Path::from_filesystem_path("s3_path").unwrap(), &UserSchema)
.level_path(2, "l2", fs_option);

let db = DB::<User, TokioExecutor>::new(options, TokioExecutor::current(), UserSchema)
.await
.unwrap();
}
```

0 comments on commit ffcb618

Please sign in to comment.