tito 0.1.2

A flexible database layer built on top of TiKV with powerful indexing strategies and relationship modeling
Documentation
# Tito

> ⚠️ **WARNING: This project is in early development and NOT PRODUCTION READY.** Use at your own risk. The API may change without notice, and there may be bugs and performance issues. Not recommended for critical applications.

Tito is a powerful, flexible database layer built on top of TiKV, providing robust indexing strategies, relationship modeling, and transaction management capabilities for Rust applications.

## Features

- **Powerful Indexing Strategies**: Define custom indexes with conditional logic for efficient querying
- **Relationship Modeling**: Create and manage embedded relationships between data models
- **Transaction Management**: Full ACID-compliant transaction support
- **Job Queue**: Built-in persistent job queue with retries and scheduling capabilities
- **Async Workers**: Tokio-powered workers for concurrent job processing
- **Type Safety**: Leverages Rust's type system for safety and performance
- **Flexible Query API**: Query data using intuitive builder pattern

## Installation

Add Tito to your project:

```toml
[dependencies]
tito = "0.1.2"
```

## Quick Start

### Setting up a Connection

```rust
let tikv_client = connect(TitoUtilsConnectInput {
    payload: TitoUtilsConnectPayload {
        uri: "127.0.0.1:2379".to_string(),
    },
}).await?;

// Initialize config
let configs = TitoConfigs {
    is_read_only: Arc::new(AtomicBool::new(false)),
};

// Create transaction manager
let tx_manager = TransactionManager::new(Arc::new(tikv_client));
```

### Creating a Model

```rust
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct User {
    id: String,
    name: String,
    email: String,
}

impl TitoModelTrait for User {
    fn get_embedded_relationships(&self) -> Vec<tito::types::TitoEmbeddedRelationshipConfig> {
        vec![] // No relationships for this simple model
    }

    fn get_indexes(&self) -> Vec<TitoIndexConfig> {
        vec![TitoIndexConfig {
            condition: true,
            name: "by_email".to_string(),
            fields: vec![TitoIndexField {
                name: "email".to_string(),
                r#type: TitoIndexBlockType::String,
            }],
            custom_generator: None,
        }]
    }

    fn get_table_name(&self) -> String {
        "users".to_string()
    }

    fn has_event(&self) -> bool {
        false
    }

    fn get_id(&self) -> String {
        self.id.clone()
    }
}

// Create model
let user_model = TitoModel::<User>::new(configs, tx_manager.clone());
```

### Basic CRUD Operations

```rust
// Create a user
let user_id = DBUuid::new_v4().to_string();
let user = User {
    id: user_id.clone(),
    name: "John Doe".to_string(),
    email: "john@example.com".to_string(),
};

// Create user with transaction
let saved_user = tx_manager
    .transaction(|tx| {
        let model = &user_model;
        async move { model.build(user, &tx).await }
    })
    .await?;

// Find user by ID
let found_user = user_model.find_by_id(&user_id, vec![]).await?;

// Update user
let updated_user = User {
    id: user_id.clone(),
    name: "John Updated".to_string(),
    email: "john_updated@example.com".to_string(),
};

tx_manager
    .transaction(|tx| {
        let model = &user_model;
        async move { model.update(updated_user, &tx).await }
    })
    .await?;

// Delete user
tx_manager
    .transaction(|tx| {
        let model = &user_model;
        async move { model.delete_by_id(&user_id, &tx).await }
    })
    .await?;
```

## Using the Query Builder

Tito v0.1.2 introduces a powerful query builder pattern for easier and more readable querying.

### Basic Query

```rust
// Find user by email
let mut query = user_model.query_by_index("by_email");
let result = query
    .value("john@example.com")
    .execute()
    .await?;

// Check if we found a user
if let Some(user) = result.items.first() {
    println!("Found user: {}", user.name);
}
```

### Query with Relationships

```rust
// Post model with tag relationships
let mut query = post_model.query_by_index("post-by-author");
let posts = query
    .value("john")              // Author name
    .relationship("tags")       // Include tags relationship
    .limit(Some(10))            // Limit to 10 results
    .execute()
    .await?;

for post in posts.items {
    println!("Post: {} with {} tags", post.title, post.tags.len());
}
```

### Advanced Queries

```rust
// Find active users by role
let mut query = user_model.query_by_index("by_role_and_status");
let active_admins = query
    .value("admin")             // First index field (role)
    .value("active")            // Second index field (status)
    .limit(Some(20))            // Limit results
    .execute()
    .await?;
    
// Use cursor for pagination
if let Some(cursor) = active_admins.cursor {
    // Get next page using the same query with a cursor
    let mut next_page_query = user_model.query_by_index("by_role_and_status");
    let next_page = next_page_query
        .value("admin")
        .value("active")
        .cursor(Some(cursor))   // Pass the cursor
        .limit(Some(20))
        .execute()
        .await?;
}
```

### Reverse Order Query

```rust
// Get latest posts in reverse chronological order
let mut query = post_model.query_by_index("post-by-created");
let latest_posts = query
    .value("2023")              // Index by year
    .execute_reverse()          // Execute in reverse order
    .await?;
```

### Transaction-Specific Queries

```rust
tx_manager.transaction(|tx| async move {
    // Query within transaction context
    let mut query = user_model.query_by_index("by_email");
    let user = query
        .value("john@example.com")
        .execute_tx(&tx)        // Execute within transaction
        .await?;
        
    // Update user in same transaction
    if let Some(mut user) = user.items.first().cloned() {
        user.name = "John Smith".to_string();
        user_model.update(user, &tx).await?;
    }
    
    Ok::<_, TitoError>(())
}).await?;
```

## Advanced Features

### Conditional Indexing

Tito lets you conditionally index data based on business rules:

```rust
TitoIndexConfig {
    condition: self.status == "active", // Only index active items
    name: "by_active_status".to_string(),
    fields: vec![TitoIndexField {
        name: "status".to_string(),
        r#type: TitoIndexBlockType::String,
    }],
    custom_generator: None,
}
```

### Relationship Modeling

Define relationships between models and fetch related data efficiently:

```rust
// Post model with references to multiple tags
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct Post {
    id: String,
    title: String,
    content: String,
    author: String,
    tag_ids: Vec<String>, // Reference to related tag IDs
    #[serde(default)]
    tags: Vec<Tag>,       // Will be populated when relationship is fetched
}

impl TitoModelTrait for Post {
    fn get_embedded_relationships(&self) -> Vec<TitoEmbeddedRelationshipConfig> {
        // Define the relationship between posts and tags
        vec![TitoEmbeddedRelationshipConfig {
            source_field_name: "tag_ids".to_string(),
            destination_field_name: "tags".to_string(),
            model: "tag".to_string(),
        }]
    }

    // ... other implementation details
}

// Fetch a post with related tags using query builder
let mut query = post_model.query_by_index("post-by-id");
let post = query
    .value(post_id)
    .relationship("tags")
    .execute()
    .await?;
```

### Composite Indexes

Create and query using composite indexes:

```rust
// Define composite index
TitoIndexConfig {
    condition: true,
    name: "by_category_and_date".to_string(),
    fields: vec![
        TitoIndexField {
            name: "category".to_string(),
            r#type: TitoIndexBlockType::String,
        },
        TitoIndexField {
            name: "published_at".to_string(),
            r#type: TitoIndexBlockType::Number,
        },
    ],
    custom_generator: None,
}

// Query using composite index
let mut query = article_model.query_by_index("by_category_and_date");
let articles = query
    .value("technology")         // category value
    .value("20230101")           // published_at value as number
    .limit(Some(5))
    .execute()
    .await?;
```

## Examples

For more detailed examples, check out the examples directory:

- `crud.rs` - Basic CRUD operations
- `blog.rs` - More complex example with relationships and query builder

## Requirements

- Rust 2021 edition or later
- TiKV server running (local or remote)

## License

This project is licensed under the Apache License, Version 2.0.