mochow_sdk_rust/mochow/api/
index.rs

1/*
2 * Copyright 2024 Baidu, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 * either express or implied. See the License for the specific language governing permissions
12 * and limitations under the License.
13 */
14
15use derive_builder::Builder;
16use reqwest_middleware::ClientWithMiddleware;
17use serde::{Deserialize, Serialize};
18
19use crate::mochow::{client::IntoRequest, config::ClientConfiguration};
20
21use super::{AutoBuildPolicyType, IndexState, IndexType, MetricType};
22
23/**
24 * create index args response with [crate::mochow::api::CommonResponse]
25 *
26 * click <https://cloud.baidu.com/doc/VDB/s/Elrsob14o#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0> for more detail of every param
27 */
28
29#[derive(Debug, Clone, Builder, Serialize)]
30pub struct CreateIndexArgs {
31    #[builder(setter(into))]
32    pub database: String,
33    #[builder(setter(into))]
34    pub table: String,
35    #[builder(default, setter(into))]
36    #[serde(default)]
37    pub indexes: Vec<IndexSchema>,
38}
39
40/// index param
41#[derive(Debug, Clone, Builder, Serialize, Deserialize)]
42pub struct IndexSchema {
43    #[builder(default, setter(into))]
44    #[serde(default, rename = "indexName")]
45    pub index_name: String,
46
47    /// The currently supported types are as follows:
48    /// scalar index: SECONDARY,
49    /// vector index: HNSW, FLAT
50    #[builder(default, setter(into, strip_option))]
51    #[serde(default, rename = "indexType", skip_serializing_if = "Option::is_none")]
52    pub index_type: Option<IndexType>,
53
54    /// Distance measurement type of vector index,
55    /// L2: Euclidean distance,
56    /// IP: Inner product distance,
57    /// COSINE: Cosine distance.
58    #[builder(default, setter(into, strip_option))]
59    #[serde(
60        default,
61        rename = "metricType",
62        skip_serializing_if = "Option::is_none"
63    )]
64    pub metric_type: Option<MetricType>,
65
66    /// Parameter details of vector index
67    #[builder(default, setter(strip_option))]
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    pub params: Option<VectorIndexParams>,
70
71    /// The target field name that the index acts on
72    #[builder(default, setter(into))]
73    pub field: String,
74
75    /// is auto build index
76    #[builder(default, setter(into))]
77    #[serde(default, rename = "autoBuild")]
78    pub auto_build: bool,
79
80    /// index state,
81    /// this is not param in create index api, but response from server
82    #[builder(setter(skip))]
83    #[serde(default, skip_serializing)]
84    pub state: Option<IndexState>,
85
86    #[builder(default, setter(strip_option))]
87    #[serde(
88        default,
89        rename = "autoBuildPolicy",
90        skip_serializing_if = "Option::is_none"
91    )]
92    pub auto_build_policy: Option<AutoBuildPolicy>,
93
94    /// index major version: only increase when manually rebuilding the index is completed,
95    /// this is not param in create index api, but response from server
96    #[builder(default, setter(skip))]
97    #[serde(
98        default,
99        rename = "indexMajorVersion",
100        skip_serializing_if = "Option::is_none"
101    )]
102    pub index_major_version: Option<u64>,
103}
104
105///  auto build index strategy
106#[derive(Debug, Clone, Builder, Default, Serialize, Deserialize)]
107pub struct AutoBuildPolicy {
108    /// the policy type of the index:
109    /// timing: regularly build indexes, only build once,
110    /// periodical: periodically building indexes,
111    /// row_count_increment: build index based on new added rows.
112    #[builder(setter(into, strip_option))]
113    #[serde(
114        default,
115        rename = "policyType",
116        skip_serializing_if = "Option::is_none"
117    )]
118    pub policy_type: Option<AutoBuildPolicyType>,
119
120    /// time format: LOCAL(%Y-%m-%d %H:%M:%S) and UTC(%Y-%m-%dT%H:%M:%Z),
121    /// only build once when the policyType is 'timing' and it takes effect,
122    /// When policyType is 'periodic', the incoming time serves as the start time of the periodic policy.
123    #[builder(default, setter(into))]
124    #[serde(default, skip_serializing_if = "String::is_empty")]
125    pub timing: String,
126
127    /// periodInSecond: the interval of periodic building indexes,
128    /// build index periodically when policyType is 'periodic'.
129    #[builder(default, setter(into))]
130    #[serde(default, rename = "periodInSecond")]
131    pub period_in_second: u64,
132
133    /// build an index when the number of rows in a tablet increases or decreases by more than rowCountIncrement
134    #[builder(default, setter(into))]
135    #[serde(default, rename = "rowCountIncrement")]
136    pub row_count_increment: u64,
137
138    /// build index once when the percentage of rows added or decreased by the tablet is greater than rowCountIncrementRatio
139    #[builder(default, setter(into))]
140    #[serde(default, rename = "rowCountIncrementRatio")]
141    pub row_count_increment_ratio: f64,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145#[serde(untagged)]
146pub enum VectorIndexParams {
147    /// HNSWPQ index
148    HNSWPQ(HNSWPQIndexParam),
149    /// HNSW index
150    HNSW(HNSWIndexParam),
151    /// PUCK index
152    PUCK(PUCKIndexParam),
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct HNSWIndexParam {
157    /// range \[4, 128\],
158    /// neighboring nodes are connected to each node in the HNSW index graph
159    #[serde(rename = "M")]
160    pub m: u32,
161    /// range \[8, 1024\],
162    /// the number of temporary neighbor nodes used during build index
163    #[serde(rename = "efConstruction")]
164    pub ef_construction: u32,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct HNSWPQIndexParam {
169    /// range \[4, 128\],
170    /// neighboring nodes are connected to each node in the HNSW index graph
171    #[serde(rename = "M")]
172    pub m: u32,
173    /// range \[8, 1024\],
174    /// the number of temporary neighbor nodes used during build index
175    #[serde(rename = "efConstruction")]
176    pub ef_construction: u32,
177
178    /// range \[1, dim\]
179    /// the number of quantization subspaces, pq quantization correlation coefficient, and requires NSQ | dim. The larger the NSQ, the finer the quantization
180    #[serde(rename = "NSQ")]
181    pub nsq: u32,
182
183    /// range \[0.0f, 1.0f\]
184    /// the sampling rate of k-means and the total number of pq samples, 10000 + (rowCount - 10000)*sampleRate
185    #[serde(rename = "sampleRate")]
186    pub sample_rate: f64,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct PUCKIndexParam {
191    /// range \[1, 5000\],
192    /// the number of coarse cluster centers in the index
193    #[serde(rename = "coarseClusterCount")]
194    pub coarse_cluster_count: u32,
195    /// range \[1, 5000\],
196    /// the number of fine cluster centers under each coarse cluster center
197    #[serde(rename = "fineClusterCount")]
198    pub fine_cluster_count: u32,
199}
200
201/**
202 * descript index args response with [DescriptIndexResponse]
203 */
204#[derive(Debug, Clone, Builder, Serialize)]
205pub struct DescriptIndexArgs {
206    #[builder(setter(into))]
207    pub database: String,
208    #[builder(setter(into))]
209    pub table: String,
210    #[builder(setter(into))]
211    #[serde(rename = "indexName")]
212    pub index_name: String,
213}
214
215#[derive(Debug, Clone, Deserialize)]
216pub struct DescriptIndexResponse {
217    pub code: i32,
218    pub msg: String,
219
220    pub index: IndexSchema,
221}
222
223/**
224 * rebuild index args reponse with [crate::mochow::api::CommonResponse]
225 */
226#[derive(Debug, Clone, Builder, Serialize)]
227pub struct RebuildIndexArgs {
228    #[builder(setter(into))]
229    pub database: String,
230    #[builder(setter(into))]
231    pub table: String,
232    #[builder(setter(into))]
233    #[serde(rename = "indexName")]
234    pub index_name: String,
235}
236
237/**
238 * delete index args reponse with [crate::mochow::api::CommonResponse]
239 */
240#[derive(Debug, Clone, Builder, Serialize)]
241pub struct DeleteIndexArgs {
242    #[builder(setter(into))]
243    pub database: String,
244    #[builder(setter(into))]
245    pub table: String,
246    #[builder(setter(into))]
247    #[serde(rename = "indexName")]
248    pub index_name: String,
249}
250
251/**
252 * modify index args reponse with [crate::mochow::api::CommonResponse]
253 */
254#[derive(Debug, Clone, Builder, Serialize)]
255pub struct ModifyIndexArgs {
256    #[builder(setter(into))]
257    pub database: String,
258    #[builder(setter(into))]
259    pub table: String,
260    #[builder(setter(into))]
261    pub index: IndexSchema,
262}
263
264impl IntoRequest for CreateIndexArgs {
265    fn into_request(
266        self,
267        config: &ClientConfiguration,
268        client: &ClientWithMiddleware,
269    ) -> reqwest_middleware::RequestBuilder {
270        let url = format!("{}/{}/index?create", config.endpoint, config.version);
271        client.post(url).json(&self)
272    }
273}
274
275impl IntoRequest for DescriptIndexArgs {
276    fn into_request(
277        self,
278        config: &ClientConfiguration,
279        client: &ClientWithMiddleware,
280    ) -> reqwest_middleware::RequestBuilder {
281        let url = format!("{}/{}/index?desc", config.endpoint, config.version);
282        client.post(url).json(&self)
283    }
284}
285
286impl IntoRequest for RebuildIndexArgs {
287    fn into_request(
288        self,
289        config: &ClientConfiguration,
290        client: &ClientWithMiddleware,
291    ) -> reqwest_middleware::RequestBuilder {
292        let url = format!("{}/{}/index?rebuild", config.endpoint, config.version);
293        client.post(url).json(&self)
294    }
295}
296
297impl IntoRequest for DeleteIndexArgs {
298    fn into_request(
299        self,
300        config: &ClientConfiguration,
301        client: &ClientWithMiddleware,
302    ) -> reqwest_middleware::RequestBuilder {
303        let url = format!("{}/{}/index", config.endpoint, config.version);
304        client.delete(url).json(&self)
305    }
306}
307
308impl IntoRequest for ModifyIndexArgs {
309    fn into_request(
310        self,
311        config: &ClientConfiguration,
312        client: &ClientWithMiddleware,
313    ) -> reqwest_middleware::RequestBuilder {
314        let url = format!("{}/{}/index?modify", config.endpoint, config.version);
315        client.post(url).json(&self)
316    }
317}
318
319#[cfg(test)]
320mod tests {
321    use anyhow::Result;
322
323    use crate::mochow::{TESTDATABSE, TESTTABLE, UTCLIENT};
324
325    use super::*;
326    #[tokio::test]
327    async fn test_rebuild_index() -> Result<()> {
328        let ret = UTCLIENT
329            .rebuild_index(&TESTDATABSE, &TESTTABLE, "vector_idx")
330            .await?;
331        println!("rebuild index: {:?}", ret);
332        Ok(())
333    }
334
335    #[tokio::test]
336    async fn test_desc_index() -> Result<()> {
337        let ret = UTCLIENT
338            .desc_index(&TESTDATABSE, &TESTTABLE, "vector_idx_hnswpq")
339            .await?;
340        println!("desc index: {:?}", ret);
341        Ok(())
342    }
343
344    #[tokio::test]
345    async fn test_delete_index() -> Result<()> {
346        let ret = UTCLIENT
347            .delete_index(&TESTDATABSE, &TESTTABLE, "vector_idx_hnswpq")
348            .await?;
349        println!("delete index: {:?}", ret);
350        Ok(())
351    }
352
353    #[tokio::test]
354    async fn test_create_index_flat() -> Result<()> {
355        let indexes = vec![IndexSchemaBuilder::default()
356            .index_name("vector_idx_flat")
357            .field("vector")
358            .index_type(IndexType::FLAT)
359            .metric_type(MetricType::L2)
360            .build()?];
361        let args = CreateIndexArgsBuilder::default()
362            .database(TESTDATABSE.to_string())
363            .table(TESTTABLE.to_string())
364            .indexes(indexes)
365            .build()?;
366        let ret = UTCLIENT.create_index(&args).await?;
367        println!("create index: {:?}", ret);
368        Ok(())
369    }
370
371    /// remembder there is no index named 'vertor_idx' in the test table before test
372    #[tokio::test]
373    async fn test_create_index_hnsw() -> Result<()> {
374        let indexes = vec![IndexSchemaBuilder::default()
375            .index_name("vector_idx_hnsw")
376            .field("vector")
377            .index_type(IndexType::HNSW)
378            .metric_type(MetricType::L2)
379            .params(VectorIndexParams::HNSW(HNSWIndexParam {
380                m: 16,
381                ef_construction: 200,
382            }))
383            .build()?];
384        let args = CreateIndexArgsBuilder::default()
385            .database(TESTDATABSE.to_string())
386            .table(TESTTABLE.to_string())
387            .indexes(indexes)
388            .build()?;
389        let ret = UTCLIENT.create_index(&args).await?;
390        println!("create index: {:?}", ret);
391        Ok(())
392    }
393
394    #[tokio::test]
395    async fn test_create_index_hnswpq() -> Result<()> {
396        let indexes = vec![IndexSchemaBuilder::default()
397            .index_name("vector_idx_hnswpq")
398            .field("vector")
399            .index_type(IndexType::HNSWPQ)
400            .metric_type(MetricType::L2)
401            .params(VectorIndexParams::HNSWPQ(HNSWPQIndexParam {
402                m: 16,
403                ef_construction: 200,
404                nsq: 3,
405                sample_rate: 1.0,
406            }))
407            .build()?];
408        let args = CreateIndexArgsBuilder::default()
409            .database(TESTDATABSE.to_string())
410            .table(TESTTABLE.to_string())
411            .indexes(indexes)
412            .build()?;
413        let ret = UTCLIENT.create_index(&args).await?;
414        println!("create index: {:?}", ret);
415        Ok(())
416    }
417
418    #[tokio::test]
419    async fn test_create_index_puck() -> Result<()> {
420        let indexes = vec![IndexSchemaBuilder::default()
421            .index_name("vector_idx_puck")
422            .field("vector")
423            .index_type(IndexType::PUCK)
424            .metric_type(MetricType::L2)
425            .params(VectorIndexParams::PUCK(PUCKIndexParam {
426                coarse_cluster_count: 5,
427                fine_cluster_count: 5,
428            }))
429            .build()?];
430        let args = CreateIndexArgsBuilder::default()
431            .database(TESTDATABSE.to_string())
432            .table(TESTTABLE.to_string())
433            .indexes(indexes)
434            .build()?;
435        let ret = UTCLIENT.create_index(&args).await?;
436        println!("create index: {:?}", ret);
437        Ok(())
438    }
439
440    #[tokio::test]
441    async fn test_modify_index() -> Result<()> {
442        let index = IndexSchemaBuilder::default()
443            .index_name("vector_idx")
444            .auto_build(true)
445            .auto_build_policy(
446                AutoBuildPolicyBuilder::default()
447                    .policy_type(AutoBuildPolicyType::PERIODICAL)
448                    .period_in_second(5000 as u64)
449                    .build()?,
450            )
451            .build()?;
452        let args = ModifyIndexArgsBuilder::default()
453            .database(TESTDATABSE.to_string())
454            .table(TESTTABLE.to_string())
455            .index(index)
456            .build()?;
457        // let data = serde_json::to_string(&args)?;
458        // println!("json data: {}", data);
459        let ret = UTCLIENT.modify_index(&args).await?;
460        println!("modify index: {:?}", ret);
461        Ok(())
462    }
463}