1use derive_builder::Builder;
16use reqwest_middleware::ClientWithMiddleware;
17use serde::{Deserialize, Serialize};
18
19use crate::mochow::{client::IntoRequest, config::ClientConfiguration};
20
21use super::{FieldType, IndexSchema, PartitionType, TableState};
22
23#[derive(Debug, Clone, Builder, Serialize)]
29pub struct CreateTableArgs {
30 #[builder(setter(into))]
31 pub database: String,
32 #[builder(setter(into))]
33 pub table: String,
34
35 #[builder(setter(into, strip_option))]
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub description: Option<String>,
39
40 #[builder(setter(into))]
44 pub replication: u32,
45
46 #[builder(default, setter(into))]
50 pub partition: Partition,
51
52 #[builder(default = "Some(false)", setter(strip_option))]
54 #[serde(rename = "enableDynamicField", skip_serializing_if = "Option::is_none")]
55 pub enable_dynamic_field: Option<bool>,
56
57 #[builder(default, setter(into))]
59 pub schema: TableSchema,
60}
61
62#[derive(Debug, Clone, Builder, Default, Serialize, Deserialize)]
63pub struct Partition {
64 #[builder(default, setter(into))]
66 #[serde(rename = "partitionType")]
67 pub partition_type: PartitionType,
68
69 #[builder(default, setter(into))]
71 #[serde(rename = "partitionNum")]
72 pub partition_num: u32,
73}
74
75#[derive(Debug, Clone, Builder, Default, Serialize, Deserialize)]
76pub struct TableSchema {
77 #[builder(default, setter(into))]
79 #[serde(default)]
80 pub fields: Vec<FieldSchema>,
81 #[builder(default, setter(into))]
83 #[serde(default)]
84 pub indexes: Vec<IndexSchema>,
85}
86
87#[derive(Debug, Clone, Builder, Serialize, Deserialize)]
88pub struct FieldSchema {
89 #[builder(default, setter(into))]
92 #[serde(default, rename = "fieldName")]
93 pub field_name: String,
94
95 #[builder(setter(into))]
97 #[serde(rename = "fieldType")]
98 pub field_type: FieldType,
99
100 #[builder(default, setter(into))]
103 #[serde(default, rename = "primaryKey")]
104 pub primary_key: bool,
105
106 #[builder(default, setter(into))]
110 #[serde(default, rename = "partitionKey")]
111 pub partition_key: bool,
112
113 #[builder(default, setter(into))]
115 #[serde(default, rename = "autoIncrement")]
116 pub auto_increment: bool,
117
118 #[builder(default, setter(into))]
120 #[serde(default, rename = "notNull")]
121 pub not_null: bool,
122
123 #[builder(default, setter(into))]
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub dimension: Option<u32>,
127}
128
129#[derive(Debug, Clone, Builder, Serialize)]
133pub struct DropTableArgs {
134 #[builder(setter(into))]
135 pub database: String,
136 #[builder(setter(into))]
137 pub table: String,
138}
139
140#[derive(Debug, Clone, Builder, Serialize)]
144pub struct ListTableArgs {
145 #[builder(setter(into))]
146 pub database: String,
147}
148
149#[derive(Debug, Clone, Deserialize)]
150pub struct ListTableResponse {
151 pub code: i32,
152 pub msg: String,
153
154 #[serde(default)]
155 pub tables: Vec<String>,
156}
157
158#[derive(Debug, Clone, Builder, Serialize)]
162pub struct DescriptTableArgs {
163 #[builder(setter(into))]
164 pub database: String,
165 #[builder(setter(into))]
166 pub table: String,
167}
168
169#[derive(Debug, Clone, Deserialize)]
170pub struct DescriptTable {
171 pub database: String,
172 pub table: String,
173
174 #[serde(rename = "createTime")]
175 pub create_time: String,
176 pub description: String,
177 pub replication: u32,
178
179 pub partition: Partition,
180
181 #[serde(rename = "enableDynamicField")]
182 pub enable_dynamic_field: bool,
183
184 pub state: TableState,
185
186 #[serde(default)]
187 pub aliases: Vec<String>,
188
189 #[serde(default)]
190 pub schema: TableSchema,
191}
192
193#[derive(Debug, Clone, Deserialize)]
194pub struct DescriptTableResponse {
195 pub code: i32,
196 pub msg: String,
197
198 pub table: DescriptTable,
199}
200
201#[derive(Debug, Clone, Builder, Serialize)]
205pub struct AddFieldArgs {
206 #[builder(setter(into))]
207 pub database: String,
208 #[builder(setter(into))]
209 pub table: String,
210 #[builder(default, setter(into))]
212 pub schema: TableSchema,
213}
214
215#[derive(Debug, Clone, Builder, Serialize)]
219pub struct StatsTableArgs {
220 #[builder(setter(into))]
221 pub database: String,
222 #[builder(setter(into))]
223 pub table: String,
224}
225
226#[derive(Debug, Clone, Deserialize)]
227pub struct StatsTableResponse {
228 pub code: i32,
229 pub msg: String,
230
231 #[serde(rename = "rowCount")]
232 pub row_count: u64,
233 #[serde(rename = "memorySizeInByte")]
234 pub memory_size_in_byte: u64,
235 #[serde(rename = "diskSizeInByte")]
236 pub disk_size_in_byte: u64,
237}
238
239#[derive(Debug, Clone, Builder, Serialize)]
243pub struct AliasTableArgs {
244 #[builder(setter(into))]
245 pub database: String,
246 #[builder(setter(into))]
247 pub table: String,
248 #[builder(setter(into))]
249 pub alias: String,
250}
251
252#[derive(Debug, Clone, Builder, Serialize)]
256pub struct UnaliasTableArgs {
257 #[builder(setter(into))]
258 pub database: String,
259 #[builder(setter(into))]
260 pub table: String,
261 #[builder(setter(into))]
262 pub alias: String,
263}
264
265impl IntoRequest for CreateTableArgs {
266 fn into_request(
267 self,
268 config: &ClientConfiguration,
269 client: &ClientWithMiddleware,
270 ) -> reqwest_middleware::RequestBuilder {
271 let url = format!("{}/{}/table?create", config.endpoint, config.version);
272 client.post(url).json(&self)
273 }
274}
275
276impl IntoRequest for DropTableArgs {
277 fn into_request(
278 self,
279 config: &ClientConfiguration,
280 client: &ClientWithMiddleware,
281 ) -> reqwest_middleware::RequestBuilder {
282 let url = format!("{}/{}/table", config.endpoint, config.version);
283 client.delete(url).json(&self)
284 }
285}
286
287impl IntoRequest for ListTableArgs {
288 fn into_request(
289 self,
290 config: &ClientConfiguration,
291 client: &ClientWithMiddleware,
292 ) -> reqwest_middleware::RequestBuilder {
293 let url = format!("{}/{}/table?list", config.endpoint, config.version);
294 client.post(url).json(&self)
295 }
296}
297
298impl IntoRequest for DescriptTableArgs {
299 fn into_request(
300 self,
301 config: &ClientConfiguration,
302 client: &ClientWithMiddleware,
303 ) -> reqwest_middleware::RequestBuilder {
304 let url = format!("{}/{}/table?desc", config.endpoint, config.version);
305 client.post(url).json(&self)
306 }
307}
308
309impl IntoRequest for AddFieldArgs {
310 fn into_request(
311 self,
312 config: &ClientConfiguration,
313 client: &ClientWithMiddleware,
314 ) -> reqwest_middleware::RequestBuilder {
315 let url = format!("{}/{}/table?addField", config.endpoint, config.version);
316 client.post(url).json(&self)
317 }
318}
319
320impl IntoRequest for StatsTableArgs {
321 fn into_request(
322 self,
323 config: &ClientConfiguration,
324 client: &ClientWithMiddleware,
325 ) -> reqwest_middleware::RequestBuilder {
326 let url = format!("{}/{}/table?stats", config.endpoint, config.version);
327 client.post(url).json(&self)
328 }
329}
330
331impl IntoRequest for AliasTableArgs {
332 fn into_request(
333 self,
334 config: &ClientConfiguration,
335 client: &ClientWithMiddleware,
336 ) -> reqwest_middleware::RequestBuilder {
337 let url = format!("{}/{}/table?alias", config.endpoint, config.version);
338 client.post(url).json(&self)
339 }
340}
341
342impl IntoRequest for UnaliasTableArgs {
343 fn into_request(
344 self,
345 config: &ClientConfiguration,
346 client: &ClientWithMiddleware,
347 ) -> reqwest_middleware::RequestBuilder {
348 let url = format!("{}/{}/table?unalias", config.endpoint, config.version);
349 client.post(url).json(&self)
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use anyhow::Result;
356
357 use super::super::{
358 AutoBuildPolicyBuilder, HNSWIndexParam, IndexSchemaBuilder, VectorIndexParams,
359 };
360 use super::*;
361 use crate::mochow::api::{AutoBuildPolicyType, IndexType, MetricType};
362 use crate::mochow::{TESTDATABSE, TESTTABLE, UTCLIENT};
363
364 #[test]
365 fn create_table_args_serialize_test() -> Result<()> {
366 let args = CreateTableArgsBuilder::default()
367 .database("test_db")
368 .table("test_table")
369 .description("this is description".to_string())
370 .replication(3 as u32)
371 .enable_dynamic_field(true)
372 .partition(
373 PartitionBuilder::default()
374 .partition_num(1 as u32)
375 .partition_type(PartitionType::HASH)
376 .build()?,
377 )
378 .schema(
379 TableSchemaBuilder::default()
380 .fields(vec![FieldSchemaBuilder::default()
381 .field_name("name")
382 .field_type(FieldType::TEXT)
383 .dimension(0)
384 .build()?])
385 .indexes(vec![IndexSchemaBuilder::default()
386 .index_name("name")
387 .index_type(IndexType::HNSW)
388 .params(VectorIndexParams::HNSW(HNSWIndexParam {
389 m: 8,
390 ef_construction: 200,
391 }))
392 .metric_type(MetricType::L2)
393 .auto_build(true)
394 .auto_build_policy(
395 AutoBuildPolicyBuilder::default()
396 .policy_type(AutoBuildPolicyType::PERIODICAL)
397 .build()?,
398 )
399 .build()?])
400 .build()?,
401 )
402 .build()?;
403 let json = serde_json::to_value(args)?;
404 assert_eq!(
405 json,
406 serde_json::json!({
407 "database": "test_db",
408 "table": "test_table",
409 "enableDynamicField": true,
410 "replication": 3,
411 "description": "this is description",
412 "partition": {
413 "partitionType": "HASH",
414 "partitionNum": 1,
415 },
416 "schema": {
417 "fields": [
418 {
419 "fieldName": "name",
420 "fieldType": "TEXT",
421 "partitionKey": false,
422 "primaryKey": false,
423 "autoIncrement": false,
424 "notNull": false,
425 "dimension": 0,
426 }
427 ],
428 "indexes": [
429 {
430 "autoBuild": true,
431 "autoBuildPolicy": {
432 "periodInSecond":0,
433 "policyType": "PERIODICAL",
434 "rowCountIncrement":0,
435 "rowCountIncrementRatio": 0.0
436 },
437 "field": "",
438 "indexName": "name",
439 "indexType": "HNSW",
440 "metricType": "L2",
441 "params": {
442 "M":8,
443 "efConstruction": 200
444 }
445 }
446 ]
447 }
448 })
449 );
450 println!("{:?}", json);
451 Ok(())
452 }
453
454 #[test]
455 fn descript_table_response_deserialize_test() -> Result<()> {
456 let data = r#"
457 {
458 "database": "test_db",
459 "table": "test_table",
460 "createTime": "2024-02-02T12:02:08Z",
461 "enableDynamicField": true,
462 "state": "NORMAL",
463 "replication": 3,
464 "description": "this is description",
465 "partition": {
466 "partitionType": "HASH",
467 "partitionNum": 1
468 },
469 "schema": {
470 "fields": [
471 {
472 "fieldName": "name",
473 "fieldType": "TEXT",
474 "partitionKey": false,
475 "primaryKey": false,
476 "autoIncrement": false,
477 "notNull": false,
478 "dimension": 0
479 }
480 ],
481 "indexes": [
482 {
483 "autoBuild": true,
484 "autoBuildPolicy": {
485 "periodInSecond":0,
486 "policyType": "TIMING",
487 "rowCountIncrement":0,
488 "rowCountIncrementRatio": 0.0,
489 "timing": ""
490 },
491 "field": "",
492 "indexName": "name",
493 "indexType": "HNSW",
494 "metricType": "L2",
495 "state": "NORMAL",
496 "params": {
497 "M":8,
498 "efConstruction": 20
499 }
500 }
501 ]
502 }
503 }
504 "#;
505
506 let v: DescriptTable = serde_json::from_str(&data)?;
507 println!("{:?}", v);
508 Ok(())
509 }
510
511 #[tokio::test]
512 async fn test_create_table() -> Result<()> {
513 let fields = vec![
514 FieldSchemaBuilder::default()
515 .field_name("id")
516 .field_type(FieldType::STRING)
517 .primary_key(true)
518 .partition_key(true)
519 .not_null(true)
520 .build()?,
521 FieldSchemaBuilder::default()
522 .field_name("bookName")
523 .field_type(FieldType::STRING)
524 .not_null(true)
525 .build()?,
526 FieldSchemaBuilder::default()
527 .field_name("author")
528 .field_type(FieldType::STRING)
529 .build()?,
530 FieldSchemaBuilder::default()
531 .field_name("page")
532 .field_type(FieldType::UINT32)
533 .build()?,
534 FieldSchemaBuilder::default()
535 .field_name("vector")
536 .field_type(FieldType::FLOAT_VECTOR)
537 .not_null(true)
538 .dimension(3)
539 .build()?,
540 ];
541 let indexes = vec![
542 IndexSchemaBuilder::default()
543 .index_name("book_name_idx")
544 .field("bookName")
545 .index_type(IndexType::SECONDARY_INDEX)
546 .build()?,
547 IndexSchemaBuilder::default()
548 .index_name("vector_idx")
549 .field("vector")
550 .index_type(IndexType::HNSW)
551 .metric_type(MetricType::L2)
552 .params(VectorIndexParams::HNSW(HNSWIndexParam {
553 m: 32,
554 ef_construction: 200,
555 }))
556 .build()?,
557 ];
558 let args = CreateTableArgsBuilder::default()
559 .database(TESTDATABSE.to_string())
560 .table(TESTTABLE.to_string())
561 .description("basic test".to_string())
562 .replication(3 as u32)
563 .partition(Partition {
564 partition_type: PartitionType::HASH,
565 partition_num: 3,
566 })
567 .schema(TableSchema {
568 fields: fields,
569 indexes: indexes,
570 })
571 .build()?;
572 let create_table_resp = UTCLIENT.create_table(&args).await?;
573 println!("{:?}", create_table_resp);
574 Ok(())
575 }
576
577 #[tokio::test]
578 async fn test_add_field() -> Result<()> {
579 let fields = vec![FieldSchemaBuilder::default()
580 .field_name("bookAlias")
581 .field_type(FieldType::STRING)
582 .build()?];
583 let args = AddFieldArgsBuilder::default()
584 .database(TESTDATABSE.to_string())
585 .table(TESTTABLE.to_string())
586 .schema(TableSchema {
587 fields: fields,
588 indexes: vec![],
589 })
590 .build()?;
591 let ret = UTCLIENT.add_field(&args).await?;
592 println!("{:?}", ret);
593 Ok(())
594 }
595
596 #[tokio::test]
597 async fn test_desc_table() -> Result<()> {
598 let ret = UTCLIENT.desc_table(&TESTDATABSE, &TESTTABLE).await?;
599 println!("{:?}", ret);
600 Ok(())
601 }
602
603 #[tokio::test]
604 async fn test_list_table() -> Result<()> {
605 let ret = UTCLIENT.list_table(&TESTDATABSE).await?;
606 println!("{:?}", ret);
607 Ok(())
608 }
609
610 #[tokio::test]
611 async fn test_stats_table() -> Result<()> {
612 let ret = UTCLIENT.show_table_stats(&TESTDATABSE, &TESTTABLE).await?;
613 println!("{:?}", ret);
614 Ok(())
615 }
616
617 #[tokio::test]
618 async fn test_alias_table() -> Result<()> {
619 let ret = UTCLIENT
620 .alias_table("book", "book_segments", "table_alias1")
621 .await?;
622 println!("{:?}", ret);
623 Ok(())
624 }
625
626 #[tokio::test]
627 async fn test_unalias_table() -> Result<()> {
628 let ret = UTCLIENT
629 .unalias_table("book", "book_segments", "table_alias1")
630 .await?;
631 println!("{:?}", ret);
632 Ok(())
633 }
634
635 #[tokio::test]
636 async fn test_drop_table() -> Result<()> {
637 let ret = UTCLIENT.drop_table(&TESTDATABSE, &TESTTABLE).await?;
638 println!("{:?}", ret);
639 Ok(())
640 }
641}