Skip to main content

blog/
blog.rs

1use serde::{Deserialize, Serialize};
2use tito::{
3    types::{
4        DBUuid, TitoEngine, TitoIndexBlockType,
5        TitoIndexConfig, TitoIndexField, TitoModelTrait,
6    },
7    TiKV, TitoError, TitoModelOptions,
8};
9
10#[derive(Default, Debug, Clone, Serialize, Deserialize)]
11struct Tag {
12    id: String,
13    name: String,
14    description: String,
15}
16
17#[derive(Default, Debug, Clone, Serialize, Deserialize)]
18struct Post {
19    id: String,
20    title: String,
21    content: String,
22    author: String,
23    tag_ids: Vec<String>,
24    #[serde(default)]
25    tags: Vec<Tag>,
26}
27
28impl TitoModelTrait for Tag {
29    fn indexes(&self) -> Vec<TitoIndexConfig> {
30        vec![TitoIndexConfig {
31            condition: true,
32            name: "tag-by-name".to_string(),
33            fields: vec![TitoIndexField {
34                name: "name".to_string(),
35                r#type: TitoIndexBlockType::String,
36            }],
37        }]
38    }
39
40    fn table(&self) -> String {
41        "tag".to_string()
42    }
43
44    fn id(&self) -> String {
45        self.id.clone()
46    }
47}
48
49impl TitoModelTrait for Post {
50    fn relationships(&self) -> Vec<tito::types::TitoRelationshipConfig> {
51        vec![tito::types::TitoRelationshipConfig {
52            source_field_name: "tag_ids".to_string(),
53            destination_field_name: "tags".to_string(),
54            model: "tag".to_string(),
55        }]
56    }
57
58    fn indexes(&self) -> Vec<TitoIndexConfig> {
59        vec![
60            TitoIndexConfig {
61                condition: true,
62                name: "post-by-author".to_string(),
63                fields: vec![TitoIndexField {
64                    name: "author".to_string(),
65                    r#type: TitoIndexBlockType::String,
66                }],
67            },
68            TitoIndexConfig {
69                condition: true,
70                name: "post-by-tag".to_string(),
71                fields: vec![TitoIndexField {
72                    name: "tag_ids".to_string(),
73                    r#type: TitoIndexBlockType::String,
74                }],
75            },
76        ]
77    }
78
79    fn table(&self) -> String {
80        "post".to_string()
81    }
82
83    fn id(&self) -> String {
84        self.id.clone()
85    }
86}
87
88#[tokio::main]
89async fn main() -> Result<(), TitoError> {
90    let tito_db = TiKV::connect(vec!["127.0.0.1:2379"]).await?;
91
92    let post_model = tito_db.clone().model::<Post>(TitoModelOptions::default());
93    let tag_model = tito_db.clone().model::<Tag>(TitoModelOptions::default());
94
95    let tech_tag = tito_db
96        .transaction(|tx| {
97            let tag_model = tag_model.clone();
98            let tag = Tag {
99                id: DBUuid::new_v4().to_string(),
100                name: "Technology".to_string(),
101                description: "All about tech".to_string(),
102            };
103            let tag_clone = tag.clone();
104            async move {
105                tag_model.set(tag_clone).execute(&tx).await?;
106                Ok::<_, TitoError>(tag)
107            }
108        })
109        .await?;
110
111    let rust_tag = tito_db
112        .transaction(|tx| {
113            let tag_model = tag_model.clone();
114            let tag = Tag {
115                id: DBUuid::new_v4().to_string(),
116                name: "Rust".to_string(),
117                description: "Rust programming".to_string(),
118            };
119            let tag_clone = tag.clone();
120            async move {
121                tag_model.set(tag_clone).execute(&tx).await?;
122                Ok::<_, TitoError>(tag)
123            }
124        })
125        .await?;
126
127    println!("Created tags: {}, {}", tech_tag.name, rust_tag.name);
128
129    let post = tito_db
130        .transaction(|tx| {
131            let post_model = post_model.clone();
132            let post = Post {
133                id: DBUuid::new_v4().to_string(),
134                title: "Using Rust with TiKV".to_string(),
135                content: "Examples of using Rust with TiKV...".to_string(),
136                author: "Alice".to_string(),
137                tag_ids: vec![tech_tag.id.clone(), rust_tag.id.clone()],
138                tags: Vec::new(),
139            };
140            let post_clone = post.clone();
141            async move {
142                post_model.set(post_clone).execute(&tx).await?;
143                Ok::<_, TitoError>(post)
144            }
145        })
146        .await?;
147
148    println!("Created post: {}", post.title);
149
150    let post_with_tags = post_model
151        .get(&post.id)
152        .relationship("tags")
153        .execute(None)
154        .await?;
155
156    println!("Post: {}", post_with_tags.title);
157    println!("Tags:");
158    for tag in &post_with_tags.tags {
159        println!("- {}", tag.name);
160    }
161
162    let mut query = post_model.query_by_index("post-by-author");
163    let results = query
164        .value("Alice".to_string())
165        .relationship("tags")
166        .execute(None)
167        .await?;
168
169    println!("\nAlice's posts:");
170    for p in &results.items {
171        println!("- {} (tags: {})", p.title, p.tags.iter().map(|t| t.name.clone()).collect::<Vec<_>>().join(", "));
172    }
173
174    Ok(())
175}