1mod index_contract;
4
5use crate::error::{Error, Result};
6use crate::types::{DataType, IndexType, MetricType, QuantizeType};
7use serde::{Deserialize, Serialize};
8use serde_json::{json, Map, Value};
9use std::collections::BTreeSet;
10
11use index_contract::validate_index_configuration;
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct HnswIndexParam {
16 pub metric: MetricType,
17 pub m: u32,
18 pub ef_construction: u32,
19 pub quantize: QuantizeType,
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
24pub struct IVFIndexParam {
25 pub metric: MetricType,
26 pub n_list: u32,
27 pub n_iters: u32,
28 pub use_soar: bool,
29}
30
31pub type IvfIndexParam = IVFIndexParam;
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35pub struct IvfRabitqIndexParam {
36 pub metric: MetricType,
37 pub n_list: u32,
38 pub total_bits: u32,
39 pub sample_count: u32,
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct FlatIndexParam {
45 pub metric: MetricType,
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50pub struct DiskAnnIndexParam {
51 pub metric: MetricType,
52 pub max_degree: u32,
53 pub list_size: u32,
54 pub pq_chunk_num: u32,
55 pub alpha: f64,
56}
57
58pub type DiskANNIndexParam = DiskAnnIndexParam;
59
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
62pub struct VamanaIndexParam {
63 pub metric: MetricType,
64 pub max_degree: u32,
65 pub search_list_size: u32,
66 pub alpha: f64,
67 pub max_occlusion: u32,
68 pub saturate: bool,
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct InvertIndexParam {
74 pub enable_range_optimization: bool,
75 pub enable_wildcard: bool,
76}
77
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct FtsIndexParam {
81 pub tokenizer_name: String,
82 pub filters: Vec<String>,
83 pub extra_params: Option<String>,
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94pub struct IndexParams {
95 pub index_type: IndexType,
96 pub metric_type: MetricType,
97 pub quantize_type: QuantizeType,
98 #[serde(default)]
99 pub params: Map<String, Value>,
100}
101
102impl IndexParams {
103 pub fn hnsw(metric: MetricType, m: i32, ef_construction: i32) -> Result<Self> {
104 Self::hnsw_with_quantize(metric, m, ef_construction, QuantizeType::Undefined)
105 }
106
107 pub fn hnsw_with_quantize(
108 metric: MetricType,
109 m: i32,
110 ef_construction: i32,
111 quantize: QuantizeType,
112 ) -> Result<Self> {
113 if m <= 0 || ef_construction <= 0 {
114 return Err(Error::invalid_argument(
115 "HNSW m and ef_construction must be positive",
116 ));
117 }
118 let mut out = Self::new(IndexType::Hnsw, metric);
119 out.quantize_type = quantize;
120 out.params.insert("m".into(), json!(m));
121 out.params
122 .insert("ef_construction".into(), json!(ef_construction));
123 out.params.insert("quantize_type".into(), json!(quantize));
124 Ok(out)
125 }
126
127 pub fn ivf(metric: MetricType, n_list: i32, n_iters: i32, use_soar: bool) -> Result<Self> {
128 if n_list <= 0 || n_iters < 0 {
129 return Err(Error::invalid_argument(
130 "IVF n_list must be positive and n_iters non-negative",
131 ));
132 }
133 let mut out = Self::new(IndexType::Ivf, metric);
134 out.params.insert("n_list".into(), json!(n_list));
135 out.params.insert("n_iters".into(), json!(n_iters));
136 out.params.insert("use_soar".into(), json!(use_soar));
137 Ok(out)
138 }
139
140 pub fn ivf_rabitq(
141 metric: MetricType,
142 nlist: i32,
143 total_bits: i32,
144 sample_count: i32,
145 ) -> Result<Self> {
146 if nlist <= 0 || !(1..=9).contains(&total_bits) || sample_count < 0 {
147 return Err(Error::invalid_argument(
148 "IVF RaBitQ requires positive nlist, total_bits in 1..=9, and non-negative sample_count",
149 ));
150 }
151 let mut out = Self::new(IndexType::IvfRabitq, metric);
152 out.quantize_type = QuantizeType::Rabitq;
153 out.params.insert("n_list".into(), json!(nlist));
154 out.params.insert("total_bits".into(), json!(total_bits));
155 out.params
156 .insert("sample_count".into(), json!(sample_count));
157 out.params
158 .insert("quantize_type".into(), json!(QuantizeType::Rabitq));
159 Ok(out)
160 }
161
162 pub fn flat(metric: MetricType) -> Result<Self> {
163 Ok(Self::new(IndexType::Flat, metric))
164 }
165
166 pub fn diskann(
167 metric: MetricType,
168 max_degree: i32,
169 list_size: i32,
170 pq_chunk_num: i32,
171 ) -> Result<Self> {
172 if max_degree <= 0 || list_size <= 0 || pq_chunk_num < 0 {
173 return Err(Error::invalid_argument(
174 "DiskANN parameters are out of range",
175 ));
176 }
177 let mut out = Self::new(IndexType::Diskann, metric);
178 out.params.insert("max_degree".into(), json!(max_degree));
179 out.params.insert("list_size".into(), json!(list_size));
180 out.params
181 .insert("pq_chunk_num".into(), json!(pq_chunk_num));
182 out.params.insert("alpha".into(), json!(1.2));
183 Ok(out)
184 }
185
186 pub fn vamana(
187 metric: MetricType,
188 max_degree: i32,
189 search_list_size: i32,
190 alpha: f64,
191 ) -> Result<Self> {
192 Self::vamana_with_options(metric, max_degree, search_list_size, alpha, 0, false)
193 }
194
195 pub fn vamana_with_options(
201 metric: MetricType,
202 max_degree: i32,
203 search_list_size: i32,
204 alpha: f64,
205 max_occlusion: i32,
206 saturate: bool,
207 ) -> Result<Self> {
208 if max_degree <= 0
209 || search_list_size <= 0
210 || max_occlusion < 0
211 || !alpha.is_finite()
212 || alpha < 1.0
213 {
214 return Err(Error::invalid_argument(
215 "Vamana parameters are out of range",
216 ));
217 }
218 let mut out = Self::new(IndexType::Vamana, metric);
219 out.params.insert("max_degree".into(), json!(max_degree));
220 out.params
221 .insert("search_list_size".into(), json!(search_list_size));
222 out.params.insert("alpha".into(), json!(alpha));
223 out.params
224 .insert("max_occlusion".into(), json!(max_occlusion));
225 out.params.insert("saturate".into(), json!(saturate));
226 Ok(out)
227 }
228
229 pub fn hnsw_rabitq(metric: MetricType, m: i32, ef_construction: i32) -> Result<Self> {
230 Self::hnsw_rabitq_with_options(metric, m, ef_construction, 7, 16, 0)
231 }
232
233 pub fn hnsw_rabitq_with_options(
234 metric: MetricType,
235 m: i32,
236 ef_construction: i32,
237 total_bits: i32,
238 num_clusters: i32,
239 sample_count: i32,
240 ) -> Result<Self> {
241 if !(1..=9).contains(&total_bits) || num_clusters <= 0 || sample_count < 0 {
242 return Err(Error::invalid_argument(
243 "HNSW RaBitQ requires total_bits in 1..=9, positive num_clusters, and non-negative sample_count",
244 ));
245 }
246 let mut out = Self::hnsw_with_quantize(metric, m, ef_construction, QuantizeType::Rabitq)?;
247 out.index_type = IndexType::HnswRabitq;
248 out.params.insert("total_bits".into(), json!(total_bits));
249 out.params
250 .insert("num_clusters".into(), json!(num_clusters));
251 out.params
252 .insert("sample_count".into(), json!(sample_count));
253 Ok(out)
254 }
255
256 pub fn invert(enable_range_opt: bool, enable_wildcard: bool) -> Result<Self> {
257 let mut out = Self::new(IndexType::Invert, MetricType::Undefined);
258 out.params
259 .insert("enable_range_optimization".into(), json!(enable_range_opt));
260 out.params
261 .insert("enable_wildcard".into(), json!(enable_wildcard));
262 Ok(out)
263 }
264
265 pub fn fts(
275 tokenizer_name: Option<&str>,
276 filters: Option<&[&str]>,
277 extra_params: Option<&str>,
278 ) -> Result<Self> {
279 let tokenizer = tokenizer_name.unwrap_or("standard").trim();
280 if tokenizer.is_empty() {
281 return Err(Error::invalid_argument(
282 "FTS tokenizer name must not be empty",
283 ));
284 }
285 let mut out = Self::new(IndexType::Fts, MetricType::Undefined);
286 out.params.insert("tokenizer_name".into(), json!(tokenizer));
287 out.params.insert(
288 "filters".into(),
289 json!(filters.map_or_else(|| vec!["lowercase"], <[_]>::to_vec)),
290 );
291 if let Some(extra) = extra_params {
292 out.params.insert("extra_params".into(), json!(extra));
293 }
294 Ok(out)
295 }
296
297 pub fn new(index_type: IndexType, metric_type: MetricType) -> Self {
298 Self {
299 index_type,
300 metric_type,
301 quantize_type: QuantizeType::Undefined,
302 params: Map::new(),
303 }
304 }
305
306 pub fn index_type(&self) -> IndexType {
307 self.index_type
308 }
309 pub fn metric_type(&self) -> MetricType {
310 self.metric_type
311 }
312 pub fn quantize_type(&self) -> QuantizeType {
313 self.quantize_type
314 }
315 pub fn set_metric_type(&mut self, metric: MetricType) -> Result<()> {
316 if metric == MetricType::Undefined && self.index_type.is_vector_index() {
317 return Err(Error::invalid_argument("vector index requires a metric"));
318 }
319 self.metric_type = metric;
320 Ok(())
321 }
322 pub fn set_quantize_type(&mut self, quantize: QuantizeType) -> Result<()> {
323 self.quantize_type = quantize;
324 self.params.insert("quantize_type".into(), json!(quantize));
325 Ok(())
326 }
327 pub fn parameter(&self, name: &str) -> Option<&Value> {
328 self.params.get(name)
329 }
330 pub fn with_parameter(mut self, name: impl Into<String>, value: Value) -> Self {
331 self.params.insert(name.into(), value);
332 self
333 }
334}
335
336impl IndexType {
337 pub fn is_vector_index(self) -> bool {
338 matches!(
339 self,
340 Self::Hnsw
341 | Self::HnswRabitq
342 | Self::Ivf
343 | Self::IvfRabitq
344 | Self::Flat
345 | Self::Diskann
346 | Self::Vamana
347 )
348 }
349}
350
351#[derive(Debug, Clone)]
353pub struct IndexParamsBuilder {
354 params: IndexParams,
355}
356
357impl IndexParamsBuilder {
358 pub fn new(index_type: IndexType) -> Self {
359 Self {
360 params: IndexParams::new(index_type, MetricType::Undefined),
361 }
362 }
363 pub fn metric_type(mut self, metric: MetricType) -> Self {
364 self.params.metric_type = metric;
365 self
366 }
367 pub fn metric(self, metric: MetricType) -> Self {
368 self.metric_type(metric)
369 }
370 pub fn quantize_type(mut self, quantize: QuantizeType) -> Self {
371 self.params.quantize_type = quantize;
372 self
373 }
374 pub fn m(mut self, value: u32) -> Self {
375 self.params.params.insert("m".into(), json!(value));
376 self
377 }
378 pub fn ef_construction(mut self, value: u32) -> Self {
379 self.params
380 .params
381 .insert("ef_construction".into(), json!(value));
382 self
383 }
384 pub fn n_list(mut self, value: u32) -> Self {
385 self.params.params.insert("n_list".into(), json!(value));
386 self
387 }
388 pub fn n_iters(mut self, value: u32) -> Self {
389 self.params.params.insert("n_iters".into(), json!(value));
390 self
391 }
392 pub fn use_soar(mut self, value: bool) -> Self {
393 self.params.params.insert("use_soar".into(), json!(value));
394 self
395 }
396 pub fn max_degree(mut self, value: u32) -> Self {
397 self.params.params.insert("max_degree".into(), json!(value));
398 self
399 }
400 pub fn list_size(mut self, value: u32) -> Self {
401 self.params.params.insert("list_size".into(), json!(value));
402 self
403 }
404 pub fn search_list_size(mut self, value: u32) -> Self {
405 self.params
406 .params
407 .insert("search_list_size".into(), json!(value));
408 self
409 }
410 pub fn alpha(mut self, value: f64) -> Self {
411 self.params.params.insert("alpha".into(), json!(value));
412 self
413 }
414 pub fn max_occlusion(mut self, value: u32) -> Self {
415 self.params
416 .params
417 .insert("max_occlusion".into(), json!(value));
418 self
419 }
420 pub fn saturate(mut self, value: bool) -> Self {
421 self.params.params.insert("saturate".into(), json!(value));
422 self
423 }
424 pub fn pq_chunk_num(mut self, value: u32) -> Self {
425 self.params
426 .params
427 .insert("pq_chunk_num".into(), json!(value));
428 self
429 }
430 pub fn tokenizer(mut self, value: impl Into<String>) -> Self {
431 self.params
432 .params
433 .insert("tokenizer_name".into(), json!(value.into()));
434 self
435 }
436 pub fn parameter(mut self, name: impl Into<String>, value: Value) -> Self {
437 self.params.params.insert(name.into(), value);
438 self
439 }
440 pub fn build(self) -> Result<IndexParams> {
441 if self.params.index_type.is_vector_index()
442 && self.params.metric_type == MetricType::Undefined
443 {
444 return Err(Error::invalid_argument("vector index requires a metric"));
445 }
446 Ok(self.params)
447 }
448}
449
450#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
452pub struct FieldSchema {
453 pub name: String,
454 pub data_type: DataType,
455 pub nullable: bool,
456 pub dimension: u32,
457 pub index_params: Option<IndexParams>,
458}
459
460impl FieldSchema {
461 pub fn new(name: &str, data_type: DataType, nullable: bool, dimension: u32) -> Result<Self> {
462 validate_field_shape(name, data_type, dimension)?;
463 Ok(Self {
464 name: name.to_string(),
465 data_type,
466 nullable,
467 dimension,
468 index_params: None,
469 })
470 }
471 pub fn set_index_params(&mut self, params: &IndexParams) -> Result<()> {
472 validate_field_shape(&self.name, self.data_type, self.dimension)?;
473 validate_index_configuration(&self.name, self.data_type, self.dimension, params)?;
474 self.index_params = Some(params.clone());
475 Ok(())
476 }
477 pub fn name(&self) -> &str {
478 &self.name
479 }
480 pub fn data_type(&self) -> DataType {
481 self.data_type
482 }
483 pub fn dimension(&self) -> u32 {
484 self.dimension
485 }
486 pub fn is_nullable(&self) -> bool {
487 self.nullable
488 }
489 pub fn is_vector_field(&self) -> bool {
490 self.data_type.is_vector()
491 }
492 pub fn is_dense_vector(&self) -> bool {
493 self.data_type.is_dense_vector()
494 }
495 pub fn is_sparse_vector(&self) -> bool {
496 self.data_type.is_sparse_vector()
497 }
498 pub fn has_index(&self) -> bool {
499 self.index_params.is_some()
500 }
501 pub fn index_type(&self) -> IndexType {
502 self.index_params
503 .as_ref()
504 .map_or(IndexType::Undefined, IndexParams::index_type)
505 }
506 pub fn is_array_type(&self) -> bool {
507 self.data_type.is_array()
508 }
509 pub fn index_params(&self) -> Option<&IndexParams> {
510 self.index_params.as_ref()
511 }
512}
513
514#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
516pub struct VectorSchema {
517 pub name: String,
518 pub data_type: DataType,
519 pub dimension: u32,
520 pub index_params: Option<IndexParams>,
521}
522
523impl VectorSchema {
524 pub fn new(name: &str, data_type: DataType, dimension: u32) -> Result<Self> {
525 let field = FieldSchema::new(name, data_type, false, dimension)?;
526 if !field.is_vector_field() {
527 return Err(Error::invalid_argument(
528 "vector schema requires a vector data type",
529 ));
530 }
531 Ok(Self {
532 name: field.name,
533 data_type,
534 dimension,
535 index_params: None,
536 })
537 }
538 pub fn set_index_params(&mut self, params: &IndexParams) -> Result<()> {
539 let mut field = FieldSchema::new(&self.name, self.data_type, false, self.dimension)?;
540 field.set_index_params(params)?;
541 self.index_params = field.index_params;
542 Ok(())
543 }
544 pub fn name(&self) -> &str {
545 &self.name
546 }
547 pub fn dimension(&self) -> u32 {
548 self.dimension
549 }
550 pub fn data_type(&self) -> DataType {
551 self.data_type
552 }
553 pub fn has_index(&self) -> bool {
554 self.index_params.is_some()
555 }
556 pub fn index_type(&self) -> IndexType {
557 self.index_params
558 .as_ref()
559 .map_or(IndexType::Undefined, IndexParams::index_type)
560 }
561}
562
563#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
565pub struct CollectionSchema {
566 pub name: String,
567 pub fields: Vec<FieldSchema>,
568 pub vectors: Vec<VectorSchema>,
569 pub max_doc_count_per_segment: u64,
570}
571
572impl CollectionSchema {
573 pub fn new(name: &str) -> Result<Self> {
574 validate_name(name)?;
575 Ok(Self {
576 name: name.to_string(),
577 fields: Vec::new(),
578 vectors: Vec::new(),
579 max_doc_count_per_segment: 0,
580 })
581 }
582 pub fn builder(name: &str) -> CollectionSchemaBuilder {
583 CollectionSchemaBuilder::new(name)
584 }
585 pub fn name(&self) -> &str {
586 &self.name
587 }
588 pub fn add_field(&mut self, field: &FieldSchema) -> Result<()> {
589 validate_field_shape(&field.name, field.data_type, field.dimension)?;
590 self.ensure_unique(&field.name)?;
591 if let Some(params) = &field.index_params {
592 validate_index_configuration(&field.name, field.data_type, field.dimension, params)?;
593 }
594 if field.data_type.is_vector() {
595 self.vectors.push(VectorSchema {
596 name: field.name.clone(),
597 data_type: field.data_type,
598 dimension: field.dimension,
599 index_params: field.index_params.clone(),
600 });
601 } else {
602 self.fields.push(field.clone());
603 }
604 Ok(())
605 }
606 pub fn add_vector_field(&mut self, field: &VectorSchema) -> Result<()> {
607 validate_field_shape(&field.name, field.data_type, field.dimension)?;
608 self.ensure_unique(&field.name)?;
609 if !field.data_type.is_vector() {
610 return Err(Error::invalid_argument(
611 "vector field requires vector data type",
612 ));
613 }
614 if let Some(params) = &field.index_params {
615 validate_index_configuration(&field.name, field.data_type, field.dimension, params)?;
616 }
617 self.vectors.push(field.clone());
618 Ok(())
619 }
620 pub fn has_field(&self, name: &str) -> bool {
621 self.fields.iter().any(|f| f.name == name) || self.vectors.iter().any(|f| f.name == name)
622 }
623 pub fn field(&self, name: &str) -> Option<&FieldSchema> {
624 self.fields.iter().find(|f| f.name == name)
625 }
626 pub fn vector(&self, name: &str) -> Option<&VectorSchema> {
627 self.vectors.iter().find(|f| f.name == name)
628 }
629 pub fn has_index(&self, name: &str) -> bool {
630 self.fields
631 .iter()
632 .find(|f| f.name == name)
633 .and_then(|f| f.index_params.as_ref())
634 .is_some()
635 || self
636 .vectors
637 .iter()
638 .find(|f| f.name == name)
639 .and_then(|f| f.index_params.as_ref())
640 .is_some()
641 }
642 pub fn drop_field(&mut self, name: &str) -> Result<()> {
643 let before = self.fields.len() + self.vectors.len();
644 self.fields.retain(|f| f.name != name);
645 self.vectors.retain(|f| f.name != name);
646 if before == self.fields.len() + self.vectors.len() {
647 return Err(Error::not_found(format!("field '{name}' not found")));
648 }
649 Ok(())
650 }
651 pub fn add_index(&mut self, field_name: &str, params: &IndexParams) -> Result<()> {
652 if let Some(field) = self.fields.iter_mut().find(|f| f.name == field_name) {
653 return field.set_index_params(params);
654 }
655 if let Some(field) = self.vectors.iter_mut().find(|f| f.name == field_name) {
656 return field.set_index_params(params);
657 }
658 Err(Error::not_found(format!("field '{field_name}' not found")))
659 }
660 pub(crate) fn check_index_configuration(
661 &self,
662 field_name: &str,
663 params: &IndexParams,
664 ) -> Result<()> {
665 if let Some(field) = self.fields.iter().find(|field| field.name == field_name) {
666 return validate_index_configuration(
667 &field.name,
668 field.data_type,
669 field.dimension,
670 params,
671 );
672 }
673 if let Some(field) = self.vectors.iter().find(|field| field.name == field_name) {
674 return validate_index_configuration(
675 &field.name,
676 field.data_type,
677 field.dimension,
678 params,
679 );
680 }
681 Err(Error::not_found(format!("field '{field_name}' not found")))
682 }
683 pub fn drop_index(&mut self, field_name: &str) -> Result<()> {
684 if let Some(field) = self.fields.iter_mut().find(|f| f.name == field_name) {
685 field.index_params = None;
686 return Ok(());
687 }
688 if let Some(field) = self.vectors.iter_mut().find(|f| f.name == field_name) {
689 field.index_params = None;
690 return Ok(());
691 }
692 Err(Error::not_found(format!("field '{field_name}' not found")))
693 }
694 pub fn set_max_doc_count_per_segment(&mut self, count: u64) -> Result<()> {
695 if count == 0 {
696 self.max_doc_count_per_segment = 0;
697 Ok(())
698 } else {
699 Err(Error::not_supported(
700 "max_doc_count_per_segment requires a segmented storage executor",
701 ))
702 }
703 }
704 pub fn max_doc_count_per_segment(&self) -> u64 {
705 self.max_doc_count_per_segment
706 }
707 pub fn validate(&self) -> Result<()> {
708 validate_name(&self.name)?;
709 if self.max_doc_count_per_segment != 0 {
710 return Err(Error::not_supported(
711 "max_doc_count_per_segment requires a segmented storage executor",
712 ));
713 }
714 if self.fields.len() + self.vectors.len() == 0 {
715 return Err(Error::invalid_argument(
716 "collection schema must contain at least one field",
717 ));
718 }
719 let mut names = BTreeSet::new();
720 for field in &self.fields {
721 validate_field_shape(&field.name, field.data_type, field.dimension)?;
722 if field.data_type.is_vector() {
723 return Err(Error::invalid_argument(format!(
724 "vector field '{}' must be stored in the vector schema list",
725 field.name
726 )));
727 }
728 if !names.insert(&field.name) {
729 return Err(Error::invalid_argument(format!(
730 "duplicate field name '{}'",
731 field.name
732 )));
733 }
734 if let Some(params) = &field.index_params {
735 validate_index_configuration(
736 &field.name,
737 field.data_type,
738 field.dimension,
739 params,
740 )?;
741 }
742 }
743 for field in &self.vectors {
744 validate_field_shape(&field.name, field.data_type, field.dimension)?;
745 if !field.data_type.is_vector() {
746 return Err(Error::invalid_argument(format!(
747 "non-vector field '{}' must be stored in the scalar schema list",
748 field.name
749 )));
750 }
751 if !names.insert(&field.name) {
752 return Err(Error::invalid_argument(format!(
753 "duplicate field name '{}'",
754 field.name
755 )));
756 }
757 if let Some(params) = &field.index_params {
758 validate_index_configuration(
759 &field.name,
760 field.data_type,
761 field.dimension,
762 params,
763 )?;
764 }
765 }
766 Ok(())
767 }
768 pub fn digest(&self) -> String {
769 let bytes = serde_json::to_vec(self).unwrap_or_default();
770 format!("{:08x}", crc32fast::hash(&bytes))
771 }
772 pub fn fields(&self) -> &[FieldSchema] {
773 &self.fields
774 }
775 pub fn vectors(&self) -> &[VectorSchema] {
776 &self.vectors
777 }
778 fn ensure_unique(&self, name: &str) -> Result<()> {
779 if self.has_field(name) {
780 Err(Error::already_exists(format!(
781 "field '{name}' already exists"
782 )))
783 } else {
784 Ok(())
785 }
786 }
787}
788
789#[derive(Debug, Clone)]
791pub struct CollectionSchemaBuilder {
792 name: String,
793 fields: Vec<(FieldSchema, Option<IndexParams>)>,
794 max_doc_count_per_segment: Option<u64>,
795 deferred_error: Option<Error>,
796}
797
798impl CollectionSchemaBuilder {
799 pub fn new(name: &str) -> Self {
800 Self {
801 name: name.to_string(),
802 fields: Vec::new(),
803 max_doc_count_per_segment: None,
804 deferred_error: None,
805 }
806 }
807 pub fn add_field(mut self, field: FieldSchema) -> Self {
808 self.fields.push((field, None));
809 self
810 }
811 pub fn add_vector_field(
812 mut self,
813 name: &str,
814 data_type: DataType,
815 dimension: u32,
816 index_params: IndexParams,
817 ) -> Self {
818 match FieldSchema::new(name, data_type, false, dimension) {
819 Ok(field) => self.fields.push((field, Some(index_params))),
820 Err(error) => self.deferred_error = Some(error),
821 }
822 self
823 }
824 pub fn add_indexed_field(
825 mut self,
826 name: &str,
827 data_type: DataType,
828 index_params: IndexParams,
829 ) -> Self {
830 match FieldSchema::new(name, data_type, false, 0) {
831 Ok(field) => self.fields.push((field, Some(index_params))),
832 Err(error) => self.deferred_error = Some(error),
833 }
834 self
835 }
836 pub fn max_doc_count_per_segment(mut self, count: u64) -> Self {
837 self.max_doc_count_per_segment = Some(count);
838 self
839 }
840 pub fn build(self) -> Result<CollectionSchema> {
841 if let Some(error) = self.deferred_error {
842 return Err(error);
843 }
844 let mut schema = CollectionSchema::new(&self.name)?;
845 for (mut field, params) in self.fields {
846 if let Some(params) = params {
847 field.set_index_params(¶ms)?;
848 }
849 schema.add_field(&field)?;
850 }
851 if let Some(count) = self.max_doc_count_per_segment {
852 schema.set_max_doc_count_per_segment(count)?;
853 }
854 schema.validate()?;
855 Ok(schema)
856 }
857}
858
859#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
860pub struct AddColumnOption {
861 pub concurrency: u32,
865}
866
867#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
868pub struct AlterColumnOption {
869 pub concurrency: u32,
873}
874
875fn validate_name(name: &str) -> Result<()> {
876 if name.trim().is_empty() || name.contains('\0') {
877 return Err(Error::invalid_argument(
878 "name must be non-empty and contain no NUL byte",
879 ));
880 }
881 Ok(())
882}
883
884fn validate_field_shape(name: &str, data_type: DataType, dimension: u32) -> Result<()> {
888 validate_name(name)?;
889 if data_type == DataType::Undefined {
890 return Err(Error::invalid_argument("field data type must be defined"));
891 }
892 if data_type.is_vector() && !data_type.is_sparse_vector() && dimension == 0 {
893 return Err(Error::invalid_argument(
894 "dense vector dimension must be positive",
895 ));
896 }
897 let binary_alignment = match data_type {
898 DataType::VectorBinary32 => Some(32),
899 DataType::VectorBinary64 => Some(64),
900 _ => None,
901 };
902 if let Some(alignment) = binary_alignment {
903 if dimension % alignment != 0 {
904 return Err(Error::invalid_argument(format!(
905 "{data_type} dimension must be a multiple of {alignment}"
906 )));
907 }
908 }
909 if !data_type.is_vector() && dimension != 0 {
910 return Err(Error::invalid_argument(
911 "non-vector field dimension must be zero",
912 ));
913 }
914 Ok(())
915}