Improve Surreal<Any> documentation (#3506)

Co-authored-by: Obinna Ekwuno <obinnacodes@gmail.com>
This commit is contained in:
Rushmore Mushambi 2024-02-13 16:22:11 +02:00 committed by GitHub
parent 4dcbec0af0
commit 567a2a5856
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 136 additions and 85 deletions

View file

@ -17,7 +17,7 @@ dependencies = ["cargo-upgrade", "cargo-update"]
[tasks.docs]
category = "LOCAL USAGE"
command = "cargo"
env = { RUSTFLAGS = "--cfg surrealdb_unstable" }
env = { RUSTDOCFLAGS = "--cfg surrealdb_unstable" }
args = ["doc", "--open", "--no-deps", "--package", "surrealdb", "--features", "rustls,native-tls,protocol-ws,protocol-http,kv-mem,kv-speedb,kv-rocksdb,kv-tikv,http,scripting,jwks"]
# Test

View file

@ -1,87 +1,132 @@
//! Dynamic support for any engine
//!
//! SurrealDB supports various ways of storing and accessing your data. For storing data we support a number of
//! key value stores. These are RocksDB, SpeeDB, TiKV, FoundationDB and an in-memory store. We call these
//! local engines. RocksDB and SpeeDB are file-based, single node key value stores. TiKV and FoundationDB are
//! are distributed stores that can scale horizontally across multiple nodes. The in-memory store does not persist
//! your data, it only stores it in memory. All these can be embedded in your application, so you don't need to
//! spin up a SurrealDB server first in order to use them. We also support spinning up a server externally and then
//! access your database via WebSockets or HTTP. We call these remote engines.
//!
//! The Rust SDK abstracts away the implementation details of the engines to make them work in a unified way.
//! All these engines, whether they are local or remote, work exactly the same way using the same API. The only
//! difference in the API is the endpoint you use to access the engine. Normally you provide the scheme of the engine
//! you want to use as a type parameter to `Surreal::new`. This allows you detect, at compile, whether the engine
//! you are trying to use is enabled. If not, your code won't compile. This is awesome but it strongly couples your
//! application to the engine you are using. In order to change an engine you would need to update your code to
//! the new scheme and endpoint you need to use and recompile it. This is where the `any` engine comes in. We will
//! call it `Surreal<Any>` (the type it creates) to avoid confusion with the word any.
//!
//! `Surreal<Any>` allows you to use any engine as long as it was enabled when compiling. Unlike with the typed scheme,
//! the choice of the engine is made at runtime depending on the endpoint that you provide as a string. If you use an
//! environment variable to provide this endpoint string, you won't need to change your code in order to
//! switch engines. The downside to this is that you will get a runtime error if you forget to enable the engine you
//! want to use when compiling your code. On the other hand, this totally decouples your application from the engine
//! you are using and makes it possible to use whichever engine SurrealDB supports by simply changing the Cargo
//! features you enable when compiling. This enables some cool workflows.
//!
//! One of the common use cases we see is using SurrealDB as an embedded database using RocksDB as the local engine.
//! This is a nice way to boost the performance of your application when all you need is a single node. The downside
//! of this approach is that RocksDB is not written in Rust so you will need to install some external dependencies
//! on your development machine in order to successfully compile it. Some of our users have reported that
//! this is not exactly straight-forward on Windows. Another issue is that RocksDB is very resource intensive to
//! compile and it takes a long time. Both of these issues can be easily avoided by using `Surreal<Any>`. You can
//! develop using an in-memory engine but deploy using RocksDB. If you develop on Windows but deploy to Linux then
//! you completely avoid having to build RocksDB on Windows at all.
//!
//! # Getting Started
//!
//! You can start by declaring your `surrealdb` dependency like this in Cargo.toml
//!
//! ```toml
//! surrealdb = {
//! version = "1",
//!
//! # Disables the default features, which are `protocol-ws` and `rustls`.
//! # Not necessary but can reduce your compile times if you don't need those features.
//! default-features = false,
//!
//! # Unconditionally enables the in-memory store.
//! # Also not necessary but this will make `cargo run` just work.
//! # Without it, you would need `cargo run --features surrealdb/kv-mem` during development. If you use a build
//! # tool like `make` or `cargo make`, however, you can put that in your build step and avoid typing it manually.
//! features = ["kv-mem"],
//!
//! # Also not necessary but this makes it easy to switch between `stable`, `beta` and `nightly` crates, if need be.
//! # See https://surrealdb.com/blog/introducing-nightly-and-beta-rust-crates for more information on those crates.
//! package = "surrealdb"
//! }
//! ```
//!
//! You then simply need to instantiate `Surreal<Any>` instead of `Surreal<Db>` or `Surreal<Client>`.
//!
//! # Examples
//!
//! ```no_run
//! use serde::{Serialize, Deserialize};
//! use serde_json::json;
//! use std::borrow::Cow;
//! use surrealdb::sql;
//! use surrealdb::engine::any::connect;
//! use surrealdb::opt::auth::Root;
//!
//! #[derive(Serialize, Deserialize)]
//! struct Name {
//! first: Cow<'static, str>,
//! last: Cow<'static, str>,
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! struct Person {
//! title: Cow<'static, str>,
//! name: Name,
//! marketing: bool,
//! }
//! ```rust
//! use std::env;
//! use surrealdb::engine::any;
//! use surrealdb::engine::any::Any;
//! use surrealdb::opt::Resource;
//! use surrealdb::Surreal;
//!
//! #[tokio::main]
//! async fn main() -> surrealdb::Result<()> {
//! let db = connect("ws://localhost:8000").await?;
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Use the endpoint specified in the environment variable or default to `memory`.
//! // This makes it possible to use the memory engine during development but switch it
//! // to any other engine for deployment.
//! let endpoint = env::var("SURREALDB_ENDPOINT").unwrap_or_else(|_| "memory".to_owned());
//!
//! // Signin as a namespace, database, or root user
//! db.signin(Root {
//! username: "root",
//! password: "root",
//! }).await?;
//! // Create the Surreal instance. This will create `Surreal<Any>`.
//! let db = any::connect(endpoint).await?;
//!
//! // Select a specific namespace / database
//! // Specify the namespace and database to use
//! db.use_ns("namespace").use_db("database").await?;
//!
//! // Create a new person with a random ID
//! let created: Vec<Person> = db.create("person")
//! .content(Person {
//! title: "Founder & CEO".into(),
//! name: Name {
//! first: "Tobie".into(),
//! last: "Morgan Hitchcock".into(),
//! },
//! marketing: true,
//! })
//! .await?;
//!
//! // Create a new person with a specific ID
//! let created: Option<Person> = db.create(("person", "jaime"))
//! .content(Person {
//! title: "Founder & COO".into(),
//! name: Name {
//! first: "Jaime".into(),
//! last: "Morgan Hitchcock".into(),
//! },
//! marketing: false,
//! })
//! .await?;
//!
//! // Update a person record with a specific ID
//! let updated: Option<Person> = db.update(("person", "jaime"))
//! .merge(json!({"marketing": true}))
//! .await?;
//!
//! // Select all people records
//! let people: Vec<Person> = db.select("person").await?;
//!
//! // Perform a custom advanced query
//! let sql = r#"
//! SELECT marketing, count()
//! FROM type::table($table)
//! GROUP BY marketing
//! "#;
//!
//! let groups = db.query(sql)
//! .bind(("table", "person"))
//! .await?;
//! // Use the database like you normally would.
//! delete_user(&db, "jane").await?;
//!
//! Ok(())
//! }
//!
//! // Deletes a user from the user table in the database
//! async fn delete_user(db: &Surreal<Any>, username: &str) -> surrealdb::Result<()> {
//! db.delete(Resource::from(("user", username))).await?;
//! Ok(())
//! }
//! ```
//!
//! By doing something like this, you can use an in-memory database on your development machine and you can just run `cargo run`
//! without having to specify the environment variable first or spinning up an external server remotely to avoid RocksDB's
//! compilation cost. You also don't need to install any `C` or `C++` dependencies on your Windows machine. For the production
//! binary you simply need to build it using something like
//!
//! ```bash
//! cargo build --features surrealdb/kv-rocksdb --release
//! ```
//!
//! and export the `SURREALDB_ENDPOINT` environment variable when starting it.
//!
//! ```bash
//! export SURREALDB_ENDPOINT="rocksdb:/path/to/database/folder"
//! /path/to/binary
//! ```
//!
//! The example above shows how you can avoid compiling RocksDB on your development machine, thereby avoiding dependency hell
//! and paying the compilation cost during development. This is not the only benefit you can derive from using `Surreal<Any>`
//! though. It's still useful even when your engine isn't expensive to compile. For example, the remote engines use pure Rust
//! dependencies but you can still benefit from using `Surreal<Any>` by using the in-memory engine for development and deploy
//! using a remote engine like the WebSocket engine. This way you avoid having to spin up a SurrealDB server first when
//! developing and testing your application.
//!
//! For some applications where you allow users to determine the engine they want to use, you can enable multiple engines for
//! them when building, or even enable them all. To do this you simply need to comma separate the Cargo features.
//!
//! ```bash
//! cargo build --features surrealdb/protocol-ws,surrealdb/kv-rocksdb,surrealdb/kv-tikv --release
//! ```
//!
//! In this case, the binary you build will have support for accessing an external server via WebSockets, embedding the database
//! using RocksDB or using a distributed TiKV cluster.
#[cfg(not(target_arch = "wasm32"))]
mod native;
@ -224,17 +269,23 @@ impl Surreal<Any> {
/// // Instantiate an in-memory instance
/// let db = connect("mem://").await?;
///
/// // Instantiate an file-backed instance
/// let db = connect("file://temp.db").await?;
/// // Instantiate an file-backed instance (currently uses RocksDB)
/// let db = connect("file://path/to/database-folder").await?;
///
/// /// // Instantiate an RocksDB-backed instance
/// let db = connect("rocksdb://path/to/database-folder").await?;
///
/// // Instantiate an SpeeDB-backed instance
/// let db = connect("speedb://path/to/database-folder").await?;
///
/// // Instantiate an IndxDB-backed instance
/// let db = connect("indxdb://MyDatabase").await?;
/// let db = connect("indxdb://DatabaseName").await?;
///
/// // Instantiate a TiKV-backed instance
/// let db = connect("tikv://localhost:2379").await?;
///
/// // Instantiate a FoundationDB-backed instance
/// let db = connect("fdb://fdb.cluster").await?;
/// let db = connect("fdb://path/to/fdb.cluster").await?;
/// # Ok(())
/// # }
/// ```

View file

@ -168,7 +168,7 @@ pub struct Mem;
/// use surrealdb::Surreal;
/// use surrealdb::engine::local::File;
///
/// let db = Surreal::new::<File>("temp.db").await?;
/// let db = Surreal::new::<File>("path/to/database-folder").await?;
/// # Ok(())
/// # }
/// ```
@ -183,7 +183,7 @@ pub struct Mem;
/// use surrealdb::engine::local::File;
///
/// let config = Config::default().strict();
/// let db = Surreal::new::<File>(("temp.db", config)).await?;
/// let db = Surreal::new::<File>(("path/to/database-folder", config)).await?;
/// # Ok(())
/// # }
/// ```
@ -204,7 +204,7 @@ pub struct File;
/// use surrealdb::Surreal;
/// use surrealdb::engine::local::RocksDb;
///
/// let db = Surreal::new::<RocksDb>("temp.db").await?;
/// let db = Surreal::new::<RocksDb>("path/to/database-folder").await?;
/// # Ok(())
/// # }
/// ```
@ -219,7 +219,7 @@ pub struct File;
/// use surrealdb::engine::local::RocksDb;
///
/// let config = Config::default().strict();
/// let db = Surreal::new::<RocksDb>(("temp.db", config)).await?;
/// let db = Surreal::new::<RocksDb>(("path/to/database-folder", config)).await?;
/// # Ok(())
/// # }
/// ```
@ -240,7 +240,7 @@ pub struct RocksDb;
/// use surrealdb::Surreal;
/// use surrealdb::engine::local::SpeeDb;
///
/// let db = Surreal::new::<SpeeDb>("temp.db").await?;
/// let db = Surreal::new::<SpeeDb>("path/to/database-folder").await?;
/// # Ok(())
/// # }
/// ```
@ -255,7 +255,7 @@ pub struct RocksDb;
/// use surrealdb::engine::local::SpeeDb;
///
/// let config = Config::default().strict();
/// let db = Surreal::new::<SpeeDb>(("temp.db", config)).await?;
/// let db = Surreal::new::<SpeeDb>(("path/to/database-folder", config)).await?;
/// # Ok(())
/// # }
/// ```
@ -276,7 +276,7 @@ pub struct SpeeDb;
/// use surrealdb::Surreal;
/// use surrealdb::engine::local::IndxDb;
///
/// let db = Surreal::new::<IndxDb>("MyDatabase").await?;
/// let db = Surreal::new::<IndxDb>("DatabaseName").await?;
/// # Ok(())
/// # }
/// ```
@ -291,7 +291,7 @@ pub struct SpeeDb;
/// use surrealdb::engine::local::IndxDb;
///
/// let config = Config::default().strict();
/// let db = Surreal::new::<IndxDb>(("MyDatabase", config)).await?;
/// let db = Surreal::new::<IndxDb>(("DatabaseName", config)).await?;
/// # Ok(())
/// # }
/// ```
@ -348,7 +348,7 @@ pub struct TiKv;
/// use surrealdb::Surreal;
/// use surrealdb::engine::local::FDb;
///
/// let db = Surreal::new::<FDb>("fdb.cluster").await?;
/// let db = Surreal::new::<FDb>("path/to/fdb.cluster").await?;
/// # Ok(())
/// # }
/// ```
@ -363,7 +363,7 @@ pub struct TiKv;
/// use surrealdb::engine::local::FDb;
///
/// let config = Config::default().strict();
/// let db = Surreal::new::<FDb>(("fdb.cluster", config)).await?;
/// let db = Surreal::new::<FDb>(("path/to/fdb.cluster", config)).await?;
/// # Ok(())
/// # }
/// ```