1use serde::{Deserialize, Serialize};
2use tito::{
3 types::{
4 DBUuid, TitoEmbeddedRelationshipConfig, TitoEngine, TitoEventConfig, TitoIndexBlockType,
5 TitoIndexConfig, TitoIndexField, TitoModelTrait,
6 },
7 TiKV, TitoError, TitoModel,
8};
9
10#[derive(Default, Debug, Clone, Serialize, Deserialize)]
12struct Tag {
13 id: String,
14 name: String,
15 description: String,
16}
17
18#[derive(Default, Debug, Clone, Serialize, Deserialize)]
20struct Post {
21 id: String,
22 title: String,
23 content: String,
24 author: String,
25
26 tag_ids: Vec<String>,
28
29 #[serde(default)]
31 tags: Vec<Tag>,
32}
33
34impl TitoModelTrait for Tag {
36 fn get_embedded_relationships(&self) -> Vec<TitoEmbeddedRelationshipConfig> {
37 vec![]
39 }
40
41 fn get_indexes(&self) -> Vec<TitoIndexConfig> {
42 vec![TitoIndexConfig {
43 condition: true,
44 name: "tag-by-name".to_string(),
45 fields: vec![TitoIndexField {
46 name: "name".to_string(),
47 r#type: TitoIndexBlockType::String,
48 }],
49 custom_generator: None,
50 }]
51 }
52
53 fn get_table_name(&self) -> String {
54 "tag".to_string()
55 }
56
57 fn get_events(&self) -> Vec<TitoEventConfig> {
58 vec![]
59 }
60
61 fn get_id(&self) -> String {
62 self.id.clone()
63 }
64}
65
66impl TitoModelTrait for Post {
68 fn get_embedded_relationships(&self) -> Vec<TitoEmbeddedRelationshipConfig> {
69 vec![TitoEmbeddedRelationshipConfig {
71 source_field_name: "tag_ids".to_string(),
72 destination_field_name: "tags".to_string(),
73 model: "tag".to_string(),
74 }]
75 }
76
77 fn get_indexes(&self) -> Vec<TitoIndexConfig> {
78 vec![
79 TitoIndexConfig {
80 condition: true,
81 name: "post-by-author".to_string(),
82 fields: vec![TitoIndexField {
83 name: "author".to_string(),
84 r#type: TitoIndexBlockType::String,
85 }],
86 custom_generator: None,
87 },
88 TitoIndexConfig {
89 condition: true,
90 name: "post-by-title".to_string(),
91 fields: vec![TitoIndexField {
92 name: "title".to_string(),
93 r#type: TitoIndexBlockType::String,
94 }],
95 custom_generator: None,
96 },
97 TitoIndexConfig {
98 condition: true,
99 name: "post-by-tag".to_string(),
100 fields: vec![TitoIndexField {
101 name: "tag_ids".to_string(),
102 r#type: TitoIndexBlockType::String,
103 }],
104 custom_generator: None,
105 },
106 ]
107 }
108
109 fn get_table_name(&self) -> String {
110 "post".to_string()
111 }
112
113 fn get_events(&self) -> Vec<TitoEventConfig> {
114 vec![]
115 }
116
117 fn get_id(&self) -> String {
118 self.id.clone()
119 }
120}
121
122#[tokio::main]
123async fn main() -> Result<(), TitoError> {
124 let tito_db = TiKV::connect(vec!["127.0.0.1:2379"]).await?;
126
127 let post_model = tito_db.clone().model::<Post>();
129 let tag_model = tito_db.clone().model::<Tag>();
130
131 let tech_tag = tito_db
133 .transaction(|tx| {
134 let tag_model = tag_model.clone();
135 let tag = Tag {
136 id: DBUuid::new_v4().to_string(),
137 name: "Technology".to_string(),
138 description: "All about tech".to_string(),
139 };
140 let tag_clone = tag.clone();
141
142 async move {
143 tag_model.build(tag_clone, &tx).await?;
144 Ok::<_, TitoError>(tag)
145 }
146 })
147 .await?;
148
149 let travel_tag = tito_db
150 .transaction(|tx| {
151 let tag_model = tag_model.clone();
152 let tag = Tag {
153 id: DBUuid::new_v4().to_string(),
154 name: "Travel".to_string(),
155 description: "Adventures around the world".to_string(),
156 };
157 let tag_clone = tag.clone();
158
159 async move {
160 tag_model.build(tag_clone, &tx).await?;
161 Ok::<_, TitoError>(tag)
162 }
163 })
164 .await?;
165
166 let rust_tag = tito_db
167 .transaction(|tx| {
168 let tag_model = tag_model.clone();
169 let tag = Tag {
170 id: DBUuid::new_v4().to_string(),
171 name: "Rust".to_string(),
172 description: "Rust programming language".to_string(),
173 };
174 let tag_clone = tag.clone();
175
176 async move {
177 tag_model.build(tag_clone, &tx).await?;
178 Ok::<_, TitoError>(tag)
179 }
180 })
181 .await?;
182
183 let database_tag = tito_db
184 .transaction(|tx| {
185 let tag_model = tag_model.clone();
186 let tag = Tag {
187 id: DBUuid::new_v4().to_string(),
188 name: "Databases".to_string(),
189 description: "Database systems and technologies".to_string(),
190 };
191 let tag_clone = tag.clone();
192
193 async move {
194 tag_model.build(tag_clone, &tx).await?;
195 Ok::<_, TitoError>(tag)
196 }
197 })
198 .await?;
199
200 println!("Created tags:");
201 println!("- {}: {}", tech_tag.name, tech_tag.id);
202 println!("- {}: {}", travel_tag.name, travel_tag.id);
203 println!("- {}: {}", rust_tag.name, rust_tag.id);
204 println!("- {}: {}", database_tag.name, database_tag.id);
205
206 let post1 = tito_db
208 .transaction(|tx| {
209 let post_model = post_model.clone();
210 let post = Post {
211 id: DBUuid::new_v4().to_string(),
212 title: "Introduction to TiKV".to_string(),
213 content: "TiKV is a distributed key-value storage system...".to_string(),
214 author: "Alice".to_string(),
215 tag_ids: vec![tech_tag.id.clone(), database_tag.id.clone()],
216 tags: Vec::new(),
217 };
218 let post_clone = post.clone();
219
220 async move {
221 post_model.build(post_clone, &tx).await?;
222 Ok::<_, TitoError>(post)
223 }
224 })
225 .await?;
226
227 let post2 = tito_db
228 .transaction(|tx| {
229 let post_model = post_model.clone();
230 let post = Post {
231 id: DBUuid::new_v4().to_string(),
232 title: "Best cities to visit in Europe".to_string(),
233 content: "Europe offers a diverse range of cities...".to_string(),
234 author: "Bob".to_string(),
235 tag_ids: vec![travel_tag.id.clone()],
236 tags: Vec::new(),
237 };
238 let post_clone = post.clone();
239
240 async move {
241 post_model.build(post_clone, &tx).await?;
242 Ok::<_, TitoError>(post)
243 }
244 })
245 .await?;
246
247 let post3 = tito_db
248 .transaction(|tx| {
249 let post_model = post_model.clone();
250 let post = Post {
251 id: DBUuid::new_v4().to_string(),
252 title: "Using Rust with TiKV".to_string(),
253 content: "Here are some examples of using Rust with TiKV...".to_string(),
254 author: "Alice".to_string(),
255 tag_ids: vec![
256 tech_tag.id.clone(),
257 rust_tag.id.clone(),
258 database_tag.id.clone(),
259 ],
260 tags: Vec::new(),
261 };
262 let post_clone = post.clone();
263
264 async move {
265 post_model.build(post_clone, &tx).await?;
266 Ok::<_, TitoError>(post)
267 }
268 })
269 .await?;
270
271 println!("\nCreated posts:");
272 println!("1. {} (by {})", post1.title, post1.author);
273 println!("2. {} (by {})", post2.title, post2.author);
274 println!("3. {} (by {})", post3.title, post3.author);
275
276 let post_with_tags = post_model
278 .find_by_id(&post1.id, vec!["tags".to_string()])
279 .await?;
280 println!("\nPost with tags:");
281 println!("Title: {}", post_with_tags.title);
282 println!("Tags:");
283 for tag in &post_with_tags.tags {
284 println!("- {}", tag.name);
285 }
286
287 let mut tech_query = post_model.query_by_index("post-by-tag");
289 let tech_results = tech_query
290 .value(tech_tag.id.clone())
291 .relationship("tags")
292 .execute()
293 .await?;
294
295 println!("\nTechnology posts:");
296 for post in &tech_results.items {
297 println!("- {} (by {})", post.title, post.author);
298 println!(
299 " Tags: {}",
300 post.tags
301 .iter()
302 .map(|t| t.name.clone())
303 .collect::<Vec<_>>()
304 .join(", ")
305 );
306 }
307
308 let mut alice_query = post_model.query_by_index("post-by-author");
310 let alice_results = alice_query
311 .value("Alice".to_string())
312 .relationship("tags")
313 .execute()
314 .await?;
315
316 println!("\nAlice's posts:");
317 for post in &alice_results.items {
318 println!("- {}", post.title);
319 println!(
320 " Tags: {}",
321 post.tags
322 .iter()
323 .map(|t| t.name.clone())
324 .collect::<Vec<_>>()
325 .join(", ")
326 );
327 }
328
329 Ok(())
330}