Skip to main content

πŸ“š Satori Rust SDK

This library allows you to easily and efficiently interact with the Satori database via WebSockets, supporting CRUD operations, real-time notifications, advanced queries, and graph-like relations.

✨ Main Features

  • Ultra-fast CRUD operations ⚑
  • Advanced queries using field_array πŸ”
  • Real-time notifications πŸ“’
  • Graph-like relations (vertices and references) πŸ•ΈοΈ
  • Data encryption and decryption πŸ”

πŸš€ Installation

Add the following to your Cargo.toml:
[dependencies]
satori-client = "0.1.4"
tokio = { version = "1.36", features = ["full"] }

🏁 Basic Usage

use satori_client::Satori;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = Satori::connect(
        "username".to_string(),
        "password".to_string(),
        "ws://localhost:8000".to_string()
    ).await?;

    // Example: set data
    client.set(serde_json::json!({
        "key": "user:123",
        "data": { "name": "John", "email": "[email protected]" },
        "type": "user"
    })).await?;

    Ok(())
}

πŸ—ƒοΈ CRUD Operations

Create Data

client.set(serde_json::json!({
    "key": "user:123",
    "data": { "name": "John", "email": "[email protected]" },
    "type": "user"
})).await?;

Read Data

let user = client.get(serde_json::json!({ "key": "user:123" })).await?;

Modify a Field

client.put(serde_json::json!({
    "key": "user:123",
    "replace_field": "name",
    "replace_value": "Peter"
})).await?;

Delete Data

client.delete(serde_json::json!({ "key": "user:123" })).await?;

🧩 Advanced Queries with field_array πŸ”

You can perform operations on multiple objects that meet certain conditions using the field_array field:
let results = client.get(serde_json::json!({
    "field_array": [
        { "field": "email", "value": "[email protected]" }
    ]
})).await?;
  • field_array is an array of conditions { "field": ..., "value": ... }.
  • You can combine it with "one": true to get only the first matching result.

πŸ”” Real-time Notifications

Receive automatic updates when an object changes:
client.set_notify("user:123", |data| {
    println!("User updated! {:?}", data);
}).await?;

πŸ•ΈοΈ Relations and Graphs

You can create relationships between objects (vertices):
client.set_vertex(serde_json::json!({
    "key": "user:123",
    "vertex": "friend:456",
    "relation": "friend",
    "encryption_key": "secret"
})).await?;
And traverse the graph with DFS:
client.dfs(serde_json::json!({
    "node": "user:123",
    "encryption_key": "secret"
})).await?;
Get all neighbors of an object:
client.get_vertex(serde_json::json!({
    "key": "user:123",
    "encryption_key": "secret",
    "relation": "friends"
})).await?;
Remove a specific neighbor:
client.delete_vertex(serde_json::json!({
    "key": "user:123",
    "vertex": "user:512",
    "encryption_key": "secret"
})).await?;

πŸ” Encryption and Security

Easily encrypt and decrypt data:
client.encrypt(serde_json::json!({
    "key": "user:123",
    "encryption_key": "secret"
})).await?;

client.decrypt(serde_json::json!({
    "key": "user:123",
    "encryption_key": "secret"
})).await?;

πŸ“¦ Array Manipulation Methods

Below are the available methods to manipulate arrays in the Satori database using the Rust client:

πŸ”Ή push

Adds a value to an existing array in an object.
client.push(serde_json::json!({
    "key": "user:123",
    "array": "friends",
    "value": "user:456"
})).await?;

πŸ”Ή pop

Removes the last element from an array in an object.
client.pop(serde_json::json!({
    "key": "user:123",
    "array": "friends"
})).await?;

πŸ”Ή splice

Modifies an array in an object (for example, to cut or replace elements).
client.splice(serde_json::json!({
    "key": "user:123",
    "array": "friends"
})).await?;

πŸ”Ή remove

Removes a specific value from an array in an object.
client.remove(serde_json::json!({
    "key": "user:123",
    "array": "friends",
    "value": "user:456"
})).await?;
## πŸ€– AI Methods


### πŸ”Ή ann

Perform an Aproximate Nearest Neighbors search

```rust
client.ann(serde_json::json!({
    "key": "user:123", //or vector: [f32]
    "top_k" : 5 //return the top 5 neighbors
})).await?;
```

### πŸ”Ή query

Make a query to the db in natural language

```rust
client.query(serde_json::json!({
    "query": "Insert the vertex user:2 to user:123",
    "backend": "openai:gpt-4o-mini"
})).await?;
```

### πŸ”Ή ask

Ask questions about your data

```rust
client.ask(serde_json::json!({
    "question": "How many users over 25 do we have",
    "backend": "openai:gpt-4o-mini"
})).await?;
```

### πŸ”Ή set_middleware
Make the LLM analyze incoming querys and decide if it must reject them, accept them or modify them.
```rust
client.set_middleware(serde_json::json!({
    "operation": "SET",
    "middleware": "Only accept requests that have the amount field specified, and convert its value to dollars"
})).await?;
```


### Notes on AI

In the functions where `backend` must be specified this parameter must passed with the following format: `openai:model-name` or `ollama:model-name`. If you're using OpenAI as your backend you must specify the `OPENAI_API_KEY` env variable. If backend isn't specified `openai:gpt-4o-mini` will be used as default.

The trained embedding model will be at the root of your db in a folder called `satori_semantic_model`.

You can train your embedding model manually whenever you want to but Satori will automatically fine-tune your model with any new updates and use this updated model for all emebedding operations.

## Analytics

### πŸ”Ή get_operations

Returns all operations executed on the database.

### πŸ”Ή get_access_frequency

Returns the number of times an object has been queried or accessed.
```rust
client.get_access_frequency(serde_json::json!({
    "key": "user:1",
})).await?;
```

---

# πŸ¦€ Schema Struct (Data Model) β€” Rust

You can use the `Schema` struct to model your data in an object-oriented way:

```rust
use satori_client::{Satori, Schema};

let satori = Satori::connect("username", "password", "ws://localhost:1234").await.unwrap();

let mut user = Schema::new(&satori, "user", Some("my_key".into()), Some(json!({ "name": "Anna" })));

user.set().await.unwrap();
```

It includes useful methods such as:

- `set`, `delete`, `encrypt`, `decrypt`, `set_vertex`, `get_vertex`, `delete_vertex`, `dfs`
- Array methods: `push`, `pop`, `splice`, `remove`

## πŸ“ Complete Example

```rust
use satori_client::Satori;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = Satori::connect(
        "username".to_string(),
        "password".to_string(),
        "ws://localhost:8000".to_string()
    ).await?;

    client.set(serde_json::json!({
        "key": "user:1",
        "data": { "name": "Carlos", "age": 30 },
        "type": "user"
    })).await?;

    client.set_notify("user:1", |data| {
        println!("Real-time update: {:?}", data);
    }).await?;

    Ok(())
}
```

---

## 🧠 Key Concepts

- **key**: Unique identifier of the object.
- **type**: Object type (e.g., 'user').
- **field_array**: Advanced filters for bulk operations.
- **notifications**: Subscription to real-time changes.
- **vertices**: Graph-like relationships between objects.

---

## Responses

All responses obbey the following pattern:

```ts
{
  data: any //the requested data if any
  message: string //status message
  type: string //SUCCESS || ERROR
}
```

AI responses obbey a different patern:

## ask

```ts
{
  response: string //response to the question
}
```

## query

```ts
{
  result: string //response from the operation made in the db
  status: string //status
}
```

## ann

```ts
{
  results: array //response from the operation made in the db
}
```

## πŸ’¬ Questions or Suggestions?

Feel free to open an issue or contribute! With ❀️ from the Satori team.

---