1use crate::{
2 collection_schema::is_embedding_function_default, default_batch_size, default_construction_ef,
3 default_construction_ef_spann, default_initial_lambda, default_m, default_m_spann,
4 default_merge_threshold, default_nreplica_count, default_num_centers_to_merge_to,
5 default_num_samples_kmeans, default_num_threads, default_reassign_neighbor_count,
6 default_resize_factor, default_search_ef, default_search_ef_spann, default_search_nprobe,
7 default_search_rng_epsilon, default_search_rng_factor, default_space, default_split_threshold,
8 default_sync_threshold, default_write_nprobe, default_write_rng_epsilon,
9 default_write_rng_factor,
10};
11use crate::{
12 HnswConfiguration, HnswParametersFromSegmentError, InternalHnswConfiguration,
13 InternalSpannConfiguration, Metadata, Segment, SpannConfiguration, UpdateHnswConfiguration,
14 UpdateSpannConfiguration,
15};
16use chroma_error::{ChromaError, ErrorCodes};
17use serde::{Deserialize, Serialize};
18use thiserror::Error;
19
20#[derive(Deserialize, Serialize, Clone, Debug, Copy)]
21pub enum KnnIndex {
22 #[serde(alias = "hnsw")]
23 Hnsw,
24 #[serde(alias = "spann")]
25 Spann,
26}
27
28pub fn default_default_knn_index() -> KnnIndex {
29 KnnIndex::Hnsw
30}
31
32#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
33#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
34#[serde(tag = "type")]
35pub enum EmbeddingFunctionConfiguration {
36 #[serde(rename = "legacy")]
37 Legacy,
38 #[serde(rename = "known")]
39 Known(EmbeddingFunctionNewConfiguration),
40 #[serde(rename = "unknown")]
41 Unknown,
42}
43
44impl EmbeddingFunctionConfiguration {
45 pub fn is_default(&self) -> bool {
46 match self {
47 EmbeddingFunctionConfiguration::Legacy => false,
48 EmbeddingFunctionConfiguration::Unknown => false,
49 EmbeddingFunctionConfiguration::Known(config) => config.name == "default",
50 }
51 }
52}
53
54#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
55#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
56pub struct EmbeddingFunctionNewConfiguration {
57 pub name: String,
58 pub config: serde_json::Value,
59}
60
61#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
62#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
63#[serde(rename_all = "snake_case")]
64pub enum VectorIndexConfiguration {
65 Hnsw(InternalHnswConfiguration),
66 Spann(InternalSpannConfiguration),
67}
68
69impl VectorIndexConfiguration {
70 pub fn update(&mut self, vector_index: &VectorIndexConfiguration) {
71 match (self, vector_index) {
72 (VectorIndexConfiguration::Hnsw(hnsw), VectorIndexConfiguration::Hnsw(hnsw_new)) => {
73 *hnsw = hnsw_new.clone();
74 }
75 (
76 VectorIndexConfiguration::Spann(spann),
77 VectorIndexConfiguration::Spann(spann_new),
78 ) => {
79 *spann = spann_new.clone();
80 }
81 (VectorIndexConfiguration::Hnsw(_), VectorIndexConfiguration::Spann(_)) => {
82 }
85 (VectorIndexConfiguration::Spann(_), VectorIndexConfiguration::Hnsw(_)) => {
86 }
89 }
90 }
91}
92impl From<InternalHnswConfiguration> for VectorIndexConfiguration {
93 fn from(config: InternalHnswConfiguration) -> Self {
94 VectorIndexConfiguration::Hnsw(config)
95 }
96}
97
98impl From<InternalSpannConfiguration> for VectorIndexConfiguration {
99 fn from(config: InternalSpannConfiguration) -> Self {
100 VectorIndexConfiguration::Spann(config)
101 }
102}
103
104fn default_vector_index_config() -> VectorIndexConfiguration {
105 VectorIndexConfiguration::Hnsw(InternalHnswConfiguration::default())
106}
107
108#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
109#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
110pub struct InternalCollectionConfiguration {
111 #[serde(default = "default_vector_index_config")]
112 pub vector_index: VectorIndexConfiguration,
113 pub embedding_function: Option<EmbeddingFunctionConfiguration>,
114}
115
116impl InternalCollectionConfiguration {
117 pub fn from_legacy_metadata(
118 metadata: Metadata,
119 ) -> Result<Self, HnswParametersFromSegmentError> {
120 let hnsw = InternalHnswConfiguration::from_legacy_segment_metadata(&Some(metadata))?;
121 Ok(Self {
122 vector_index: VectorIndexConfiguration::Hnsw(hnsw),
123 embedding_function: None,
124 })
125 }
126
127 pub fn default_hnsw() -> Self {
128 Self {
129 vector_index: VectorIndexConfiguration::Hnsw(InternalHnswConfiguration::default()),
130 embedding_function: None,
131 }
132 }
133
134 pub fn default_spann() -> Self {
135 Self {
136 vector_index: VectorIndexConfiguration::Spann(InternalSpannConfiguration::default()),
137 embedding_function: None,
138 }
139 }
140
141 pub fn is_default(&self) -> bool {
143 if !is_embedding_function_default(&self.embedding_function) {
144 return false;
145 }
146
147 match &self.vector_index {
149 VectorIndexConfiguration::Hnsw(hnsw_config) => {
150 hnsw_config.ef_construction == default_construction_ef()
151 && hnsw_config.ef_search == default_search_ef()
152 && hnsw_config.max_neighbors == default_m()
153 && hnsw_config.num_threads == default_num_threads()
154 && hnsw_config.batch_size == default_batch_size()
155 && hnsw_config.sync_threshold == default_sync_threshold()
156 && hnsw_config.resize_factor == default_resize_factor()
157 && hnsw_config.space == default_space()
158 }
159 VectorIndexConfiguration::Spann(spann_config) => {
160 spann_config.search_nprobe == default_search_nprobe()
161 && spann_config.search_rng_factor == default_search_rng_factor()
162 && spann_config.search_rng_epsilon == default_search_rng_epsilon()
163 && spann_config.write_nprobe == default_write_nprobe()
164 && spann_config.nreplica_count == default_nreplica_count()
165 && spann_config.write_rng_factor == default_write_rng_factor()
166 && spann_config.write_rng_epsilon == default_write_rng_epsilon()
167 && spann_config.split_threshold == default_split_threshold()
168 && spann_config.num_samples_kmeans == default_num_samples_kmeans()
169 && spann_config.initial_lambda == default_initial_lambda()
170 && spann_config.reassign_neighbor_count == default_reassign_neighbor_count()
171 && spann_config.merge_threshold == default_merge_threshold()
172 && spann_config.num_centers_to_merge_to == default_num_centers_to_merge_to()
173 && spann_config.ef_construction == default_construction_ef_spann()
174 && spann_config.ef_search == default_search_ef_spann()
175 && spann_config.max_neighbors == default_m_spann()
176 && spann_config.space == default_space()
177 }
178 }
179 }
180
181 pub fn get_hnsw_config_with_legacy_fallback(
182 &self,
183 segment: &Segment,
184 ) -> Result<Option<InternalHnswConfiguration>, HnswParametersFromSegmentError> {
185 self.get_hnsw_config_from_legacy_metadata(&segment.metadata)
186 }
187
188 pub fn get_hnsw_config_from_legacy_metadata(
189 &self,
190 metadata: &Option<Metadata>,
191 ) -> Result<Option<InternalHnswConfiguration>, HnswParametersFromSegmentError> {
192 if let Some(config) = self.get_hnsw_config() {
193 let config_from_metadata =
194 InternalHnswConfiguration::from_legacy_segment_metadata(metadata)?;
195
196 if config == InternalHnswConfiguration::default() && config != config_from_metadata {
197 return Ok(Some(config_from_metadata));
198 }
199
200 return Ok(Some(config));
201 }
202
203 Ok(None)
204 }
205
206 pub fn get_spann_config(&self) -> Option<InternalSpannConfiguration> {
207 match &self.vector_index {
208 VectorIndexConfiguration::Spann(config) => Some(config.clone()),
209 _ => None,
210 }
211 }
212
213 fn get_hnsw_config(&self) -> Option<InternalHnswConfiguration> {
214 match &self.vector_index {
215 VectorIndexConfiguration::Hnsw(config) => Some(config.clone()),
216 _ => None,
217 }
218 }
219
220 pub fn update(&mut self, configuration: &InternalUpdateCollectionConfiguration) {
221 if let Some(vector_index) = &configuration.vector_index {
224 match vector_index {
225 UpdateVectorIndexConfiguration::Hnsw(hnsw_config) => {
226 if let VectorIndexConfiguration::Hnsw(current_config) = &mut self.vector_index {
227 if let Some(update_config) = hnsw_config {
228 if let Some(ef_search) = update_config.ef_search {
229 current_config.ef_search = ef_search;
230 }
231 if let Some(max_neighbors) = update_config.max_neighbors {
232 current_config.max_neighbors = max_neighbors;
233 }
234 if let Some(num_threads) = update_config.num_threads {
235 current_config.num_threads = num_threads;
236 }
237 if let Some(resize_factor) = update_config.resize_factor {
238 current_config.resize_factor = resize_factor;
239 }
240 if let Some(sync_threshold) = update_config.sync_threshold {
241 current_config.sync_threshold = sync_threshold;
242 }
243 if let Some(batch_size) = update_config.batch_size {
244 current_config.batch_size = batch_size;
245 }
246 }
247 }
248 }
249 UpdateVectorIndexConfiguration::Spann(spann_config) => {
250 if let VectorIndexConfiguration::Spann(current_config) = &mut self.vector_index
251 {
252 if let Some(update_config) = spann_config {
253 if let Some(search_nprobe) = update_config.search_nprobe {
254 current_config.search_nprobe = search_nprobe;
255 }
256 if let Some(ef_search) = update_config.ef_search {
257 current_config.ef_search = ef_search;
258 }
259 }
260 }
261 }
262 }
263 }
264 if let Some(embedding_function) = &configuration.embedding_function {
266 self.embedding_function = Some(embedding_function.clone());
267 }
268 }
269
270 pub fn try_from_config(
271 value: CollectionConfiguration,
272 default_knn_index: KnnIndex,
273 metadata: Option<Metadata>,
274 ) -> Result<Self, CollectionConfigurationToInternalConfigurationError> {
275 let mut hnsw: Option<HnswConfiguration> = value.hnsw;
276 let spann: Option<SpannConfiguration> = value.spann;
277
278 if hnsw.is_none() && spann.is_none() {
282 let hnsw_config_from_metadata =
283 InternalHnswConfiguration::from_legacy_segment_metadata(&metadata).map_err(|e| {
284 CollectionConfigurationToInternalConfigurationError::HnswParametersFromSegmentError(
285 e,
286 )
287 })?;
288 hnsw = Some(hnsw_config_from_metadata.into());
289 }
290
291 match (hnsw, spann) {
292 (Some(_), Some(_)) => Err(CollectionConfigurationToInternalConfigurationError::MultipleVectorIndexConfigurations),
293 (Some(hnsw), None) => {
294 match default_knn_index {
295 KnnIndex::Spann => {
299 let internal_config = if let Some(space) = hnsw.space {
300 InternalSpannConfiguration {
301 space,
302 ..Default::default()
303 }
304 } else {
305 InternalSpannConfiguration::default()
306 };
307
308 Ok(InternalCollectionConfiguration {
309 vector_index: VectorIndexConfiguration::Spann(internal_config),
310 embedding_function: value.embedding_function,
311 })
312 },
313 KnnIndex::Hnsw => {
314 let hnsw: InternalHnswConfiguration = hnsw.into();
315 Ok(InternalCollectionConfiguration {
316 vector_index: hnsw.into(),
317 embedding_function: value.embedding_function,
318 })
319 }
320 }
321 }
322 (None, Some(spann)) => {
323 match default_knn_index {
324 KnnIndex::Hnsw => {
328 let internal_config = if let Some(space) = spann.space {
329 InternalHnswConfiguration {
330 space,
331 ..Default::default()
332 }
333 } else {
334 InternalHnswConfiguration::default()
335 };
336 Ok(InternalCollectionConfiguration {
337 vector_index: VectorIndexConfiguration::Hnsw(internal_config),
338 embedding_function: value.embedding_function,
339 })
340 }
341 KnnIndex::Spann => {
342 let spann: InternalSpannConfiguration = spann.into();
343 Ok(InternalCollectionConfiguration {
344 vector_index: spann.into(),
345 embedding_function: value.embedding_function,
346 })
347 }
348 }
349 }
350 (None, None) => {
351 let vector_index = match default_knn_index {
352 KnnIndex::Hnsw => InternalHnswConfiguration::default().into(),
353 KnnIndex::Spann => InternalSpannConfiguration::default().into(),
354 };
355 Ok(InternalCollectionConfiguration {
356 vector_index,
357 embedding_function: value.embedding_function,
358 })
359 }
360 }
361 }
362}
363
364impl TryFrom<CollectionConfiguration> for InternalCollectionConfiguration {
365 type Error = CollectionConfigurationToInternalConfigurationError;
366
367 fn try_from(value: CollectionConfiguration) -> Result<Self, Self::Error> {
368 match (value.hnsw, value.spann) {
369 (Some(_), Some(_)) => Err(Self::Error::MultipleVectorIndexConfigurations),
370 (Some(hnsw), None) => {
371 let hnsw: InternalHnswConfiguration = hnsw.into();
372 Ok(InternalCollectionConfiguration {
373 vector_index: hnsw.into(),
374 embedding_function: value.embedding_function,
375 })
376 }
377 (None, Some(spann)) => {
378 let spann: InternalSpannConfiguration = spann.into();
379 Ok(InternalCollectionConfiguration {
380 vector_index: spann.into(),
381 embedding_function: value.embedding_function,
382 })
383 }
384 (None, None) => Ok(InternalCollectionConfiguration {
385 vector_index: InternalHnswConfiguration::default().into(),
386 embedding_function: value.embedding_function,
387 }),
388 }
389 }
390}
391
392#[derive(Debug, Error)]
393pub enum CollectionConfigurationToInternalConfigurationError {
394 #[error("Multiple vector index configurations provided")]
395 MultipleVectorIndexConfigurations,
396 #[error("Failed to parse hnsw parameters from segment metadata")]
397 HnswParametersFromSegmentError(#[from] HnswParametersFromSegmentError),
398}
399
400impl ChromaError for CollectionConfigurationToInternalConfigurationError {
401 fn code(&self) -> ErrorCodes {
402 match self {
403 Self::MultipleVectorIndexConfigurations => ErrorCodes::InvalidArgument,
404 Self::HnswParametersFromSegmentError(_) => ErrorCodes::InvalidArgument,
405 }
406 }
407}
408
409#[derive(Default, Deserialize, Serialize, Debug, Clone)]
410#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
411#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
412pub struct CollectionConfiguration {
413 pub hnsw: Option<HnswConfiguration>,
414 pub spann: Option<SpannConfiguration>,
415 pub embedding_function: Option<EmbeddingFunctionConfiguration>,
416}
417
418impl From<InternalCollectionConfiguration> for CollectionConfiguration {
419 fn from(value: InternalCollectionConfiguration) -> Self {
420 Self {
421 hnsw: match value.vector_index.clone() {
422 VectorIndexConfiguration::Hnsw(config) => Some(config.into()),
423 _ => None,
424 },
425 spann: match value.vector_index {
426 VectorIndexConfiguration::Spann(config) => Some(config.into()),
427 _ => None,
428 },
429 embedding_function: value.embedding_function,
430 }
431 }
432}
433
434#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
435#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
436#[serde(rename_all = "snake_case")]
437pub enum UpdateVectorIndexConfiguration {
438 Hnsw(Option<UpdateHnswConfiguration>),
439 Spann(Option<UpdateSpannConfiguration>),
440}
441
442impl From<UpdateHnswConfiguration> for UpdateVectorIndexConfiguration {
443 fn from(config: UpdateHnswConfiguration) -> Self {
444 UpdateVectorIndexConfiguration::Hnsw(Some(config))
445 }
446}
447
448impl From<UpdateSpannConfiguration> for UpdateVectorIndexConfiguration {
449 fn from(config: UpdateSpannConfiguration) -> Self {
450 UpdateVectorIndexConfiguration::Spann(Some(config))
451 }
452}
453
454#[derive(Debug, Error)]
455pub enum UpdateCollectionConfigurationToInternalConfigurationError {
456 #[error("Multiple vector index configurations provided")]
457 MultipleVectorIndexConfigurations,
458}
459
460impl ChromaError for UpdateCollectionConfigurationToInternalConfigurationError {
461 fn code(&self) -> ErrorCodes {
462 match self {
463 Self::MultipleVectorIndexConfigurations => ErrorCodes::InvalidArgument,
464 }
465 }
466}
467
468#[derive(Deserialize, Serialize, Debug, Clone)]
469#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
470#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
471pub struct UpdateCollectionConfiguration {
472 pub hnsw: Option<UpdateHnswConfiguration>,
473 pub spann: Option<UpdateSpannConfiguration>,
474 pub embedding_function: Option<EmbeddingFunctionConfiguration>,
475}
476
477#[derive(Deserialize, Serialize, Debug, Clone)]
478#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
479pub struct InternalUpdateCollectionConfiguration {
480 pub vector_index: Option<UpdateVectorIndexConfiguration>,
481 pub embedding_function: Option<EmbeddingFunctionConfiguration>,
482}
483
484#[derive(Debug, Error)]
485pub enum UpdateCollectionConfigurationToInternalUpdateConfigurationError {
486 #[error("Multiple vector index configurations provided")]
487 MultipleVectorIndexConfigurations,
488}
489
490impl ChromaError for UpdateCollectionConfigurationToInternalUpdateConfigurationError {
491 fn code(&self) -> ErrorCodes {
492 match self {
493 Self::MultipleVectorIndexConfigurations => ErrorCodes::InvalidArgument,
494 }
495 }
496}
497
498impl TryFrom<UpdateCollectionConfiguration> for InternalUpdateCollectionConfiguration {
499 type Error = UpdateCollectionConfigurationToInternalUpdateConfigurationError;
500
501 fn try_from(value: UpdateCollectionConfiguration) -> Result<Self, Self::Error> {
502 match (value.hnsw, value.spann) {
503 (Some(_), Some(_)) => Err(Self::Error::MultipleVectorIndexConfigurations),
504 (Some(hnsw), None) => Ok(InternalUpdateCollectionConfiguration {
505 vector_index: Some(UpdateVectorIndexConfiguration::Hnsw(Some(hnsw))),
506 embedding_function: value.embedding_function,
507 }),
508 (None, Some(spann)) => Ok(InternalUpdateCollectionConfiguration {
509 vector_index: Some(UpdateVectorIndexConfiguration::Spann(Some(spann))),
510 embedding_function: value.embedding_function,
511 }),
512 (None, None) => Ok(InternalUpdateCollectionConfiguration {
513 vector_index: None,
514 embedding_function: value.embedding_function,
515 }),
516 }
517 }
518}
519
520#[cfg(test)]
521mod tests {
522
523 use crate::hnsw_configuration::HnswConfiguration;
524 use crate::hnsw_configuration::Space;
525 use crate::spann_configuration::SpannConfiguration;
526 use crate::{test_segment, CollectionUuid, Metadata};
527
528 use super::*;
529
530 #[test]
531 fn metadata_overrides_parameter() {
532 let mut metadata = Metadata::new();
533 metadata.insert(
534 "hnsw:construction_ef".to_string(),
535 crate::MetadataValue::Int(1),
536 );
537
538 let mut segment = test_segment(CollectionUuid::new(), crate::SegmentScope::VECTOR);
539 segment.metadata = Some(metadata);
540
541 let config = InternalCollectionConfiguration::default_hnsw();
542 let overridden_config = config
543 .get_hnsw_config_with_legacy_fallback(&segment)
544 .unwrap()
545 .unwrap();
546
547 assert_eq!(overridden_config.ef_construction, 1);
548 }
549
550 #[test]
551 fn metadata_ignored_when_config_is_not_default() {
552 let mut metadata = Metadata::new();
553 metadata.insert(
554 "hnsw:construction_ef".to_string(),
555 crate::MetadataValue::Int(1),
556 );
557
558 let mut segment = test_segment(CollectionUuid::new(), crate::SegmentScope::VECTOR);
559 segment.metadata = Some(metadata);
560
561 let config = InternalCollectionConfiguration {
562 vector_index: VectorIndexConfiguration::Hnsw(InternalHnswConfiguration {
563 ef_construction: 2,
564 ..Default::default()
565 }),
566 embedding_function: None,
567 };
568
569 let overridden_config = config
570 .get_hnsw_config_with_legacy_fallback(&segment)
571 .unwrap()
572 .unwrap();
573
574 assert_eq!(overridden_config.ef_construction, 2);
576 }
577
578 #[test]
579 fn test_hnsw_config_with_hnsw_default() {
580 let hnsw_config = HnswConfiguration {
581 max_neighbors: Some(16),
582 ef_construction: Some(100),
583 ef_search: Some(10),
584 batch_size: Some(100),
585 num_threads: Some(4),
586 sync_threshold: Some(500),
587 resize_factor: Some(1.2),
588 space: Some(Space::Cosine),
589 };
590
591 let collection_config = CollectionConfiguration {
592 hnsw: Some(hnsw_config.clone()),
593 spann: None,
594 embedding_function: None,
595 };
596
597 let internal_config_result = InternalCollectionConfiguration::try_from_config(
598 collection_config,
599 KnnIndex::Hnsw,
600 None,
601 );
602
603 assert!(internal_config_result.is_ok());
604 let internal_config = internal_config_result.unwrap();
605
606 let expected_vector_index = VectorIndexConfiguration::Hnsw(hnsw_config.into());
607 assert_eq!(internal_config.vector_index, expected_vector_index);
608 }
609
610 #[test]
611 fn test_hnsw_config_with_spann_default() {
612 let hnsw_config = HnswConfiguration {
613 max_neighbors: Some(16),
614 ef_construction: Some(100),
615 ef_search: Some(10),
616 batch_size: Some(100),
617 num_threads: Some(4),
618 sync_threshold: Some(500),
619 resize_factor: Some(1.2),
620 space: Some(Space::Cosine),
621 };
622
623 let collection_config = CollectionConfiguration {
624 hnsw: Some(hnsw_config.clone()),
625 spann: None,
626 embedding_function: None,
627 };
628
629 let internal_config_result = InternalCollectionConfiguration::try_from_config(
630 collection_config,
631 KnnIndex::Spann,
632 None,
633 );
634
635 assert!(internal_config_result.is_ok());
636 let internal_config = internal_config_result.unwrap();
637
638 let expected_vector_index = VectorIndexConfiguration::Spann(InternalSpannConfiguration {
639 space: hnsw_config.space.unwrap_or(Space::L2),
640 ..Default::default()
641 });
642 assert_eq!(internal_config.vector_index, expected_vector_index);
643 }
644
645 #[test]
646 fn test_spann_config_with_spann_default() {
647 let spann_config = SpannConfiguration {
648 ef_construction: Some(100),
649 ef_search: Some(10),
650 max_neighbors: Some(16),
651 search_nprobe: Some(1),
652 write_nprobe: Some(1),
653 space: Some(Space::Cosine),
654 reassign_neighbor_count: Some(64),
655 split_threshold: Some(200),
656 merge_threshold: Some(100),
657 };
658
659 let collection_config = CollectionConfiguration {
660 hnsw: None,
661 spann: Some(spann_config.clone()),
662 embedding_function: None,
663 };
664
665 let internal_config_result = InternalCollectionConfiguration::try_from_config(
666 collection_config,
667 KnnIndex::Spann,
668 None,
669 );
670
671 assert!(internal_config_result.is_ok());
672 let internal_config = internal_config_result.unwrap();
673
674 let expected_vector_index = VectorIndexConfiguration::Spann(spann_config.into());
675 assert_eq!(internal_config.vector_index, expected_vector_index);
676 }
677
678 #[test]
679 fn test_spann_config_with_hnsw_default() {
680 let spann_config = SpannConfiguration {
681 ef_construction: Some(100),
682 ef_search: Some(10),
683 max_neighbors: Some(16),
684 search_nprobe: Some(1),
685 write_nprobe: Some(1),
686 space: Some(Space::Cosine),
687 reassign_neighbor_count: Some(64),
688 split_threshold: Some(200),
689 merge_threshold: Some(100),
690 };
691
692 let collection_config = CollectionConfiguration {
693 hnsw: None,
694 spann: Some(spann_config.clone()),
695 embedding_function: None,
696 };
697
698 let internal_config_result = InternalCollectionConfiguration::try_from_config(
699 collection_config,
700 KnnIndex::Hnsw,
701 None,
702 );
703
704 let expected_vector_index = VectorIndexConfiguration::Hnsw(InternalHnswConfiguration {
705 space: spann_config.space.unwrap_or(Space::L2),
706 ..Default::default()
707 });
708 assert_eq!(
709 internal_config_result.unwrap().vector_index,
710 expected_vector_index
711 );
712 }
713
714 #[test]
715 fn test_no_config_with_metadata_default_hnsw() {
716 let metadata = Metadata::new();
717 let collection_config = CollectionConfiguration {
718 hnsw: None,
719 spann: None,
720 embedding_function: None,
721 };
722
723 let internal_config_result = InternalCollectionConfiguration::try_from_config(
724 collection_config,
725 KnnIndex::Hnsw,
726 Some(metadata),
727 );
728
729 assert!(internal_config_result.is_ok());
730 let internal_config = internal_config_result.unwrap();
731
732 assert_eq!(
733 internal_config.vector_index,
734 VectorIndexConfiguration::Hnsw(InternalHnswConfiguration::default())
735 );
736 }
737
738 #[test]
739 fn test_no_config_with_metadata_default_spann() {
740 let metadata = Metadata::new();
741 let collection_config = CollectionConfiguration {
742 hnsw: None,
743 spann: None,
744 embedding_function: None,
745 };
746
747 let internal_config_result = InternalCollectionConfiguration::try_from_config(
748 collection_config,
749 KnnIndex::Spann,
750 Some(metadata),
751 );
752
753 assert!(internal_config_result.is_ok());
754 let internal_config = internal_config_result.unwrap();
755
756 assert_eq!(
757 internal_config.vector_index,
758 VectorIndexConfiguration::Spann(InternalSpannConfiguration::default())
759 );
760 }
761
762 #[test]
763 fn test_legacy_metadata_with_hnsw_config() {
764 let mut metadata = Metadata::new();
765 metadata.insert(
766 "hnsw:space".to_string(),
767 crate::MetadataValue::Str("cosine".to_string()),
768 );
769 metadata.insert(
770 "hnsw:construction_ef".to_string(),
771 crate::MetadataValue::Int(1),
772 );
773
774 let collection_config = CollectionConfiguration {
775 hnsw: None,
776 spann: None,
777 embedding_function: None,
778 };
779
780 let internal_config_result = InternalCollectionConfiguration::try_from_config(
781 collection_config,
782 KnnIndex::Hnsw,
783 Some(metadata),
784 );
785
786 assert!(internal_config_result.is_ok());
787 let internal_config = internal_config_result.unwrap();
788
789 assert_eq!(
790 internal_config.vector_index,
791 VectorIndexConfiguration::Hnsw(InternalHnswConfiguration {
792 space: Space::Cosine,
793 ef_construction: 1,
794 ..Default::default()
795 })
796 );
797 }
798
799 #[test]
800 fn test_legacy_metadata_with_spann_config() {
801 let mut metadata = Metadata::new();
802 metadata.insert(
803 "hnsw:space".to_string(),
804 crate::MetadataValue::Str("cosine".to_string()),
805 );
806 metadata.insert(
807 "hnsw:construction_ef".to_string(),
808 crate::MetadataValue::Int(1),
809 );
810
811 let collection_config = CollectionConfiguration {
812 hnsw: None,
813 spann: None,
814 embedding_function: None,
815 };
816
817 let internal_config_result = InternalCollectionConfiguration::try_from_config(
818 collection_config,
819 KnnIndex::Spann,
820 Some(metadata),
821 );
822
823 assert!(internal_config_result.is_ok());
824
825 let internal_config = internal_config_result.unwrap();
826
827 assert_eq!(
828 internal_config.vector_index,
829 VectorIndexConfiguration::Spann(InternalSpannConfiguration {
830 space: Space::Cosine,
831 ..Default::default()
832 })
833 );
834 }
835
836 #[test]
837 fn test_update_collection_configuration_with_hnsw() {
838 let mut config = InternalCollectionConfiguration {
839 vector_index: VectorIndexConfiguration::Hnsw(InternalHnswConfiguration {
840 space: Space::Cosine,
841 ..Default::default()
842 }),
843 embedding_function: Some(EmbeddingFunctionConfiguration::Known(
844 EmbeddingFunctionNewConfiguration {
845 name: "test".to_string(),
846 config: serde_json::Value::Null,
847 },
848 )),
849 };
850 let update_config = UpdateCollectionConfiguration {
851 hnsw: Some(UpdateHnswConfiguration {
852 ef_search: Some(1),
853 ..Default::default()
854 }),
855 spann: None,
856 embedding_function: None,
857 };
858 config.update(&update_config.try_into().unwrap());
859 assert_eq!(
860 config.vector_index,
861 VectorIndexConfiguration::Hnsw(InternalHnswConfiguration {
862 space: Space::Cosine,
863 ef_search: 1,
864 ..Default::default()
865 })
866 );
867
868 assert_eq!(
869 config.embedding_function,
870 Some(EmbeddingFunctionConfiguration::Known(
871 EmbeddingFunctionNewConfiguration {
872 name: "test".to_string(),
873 config: serde_json::Value::Null,
874 },
875 ))
876 );
877 }
878
879 #[test]
880 fn test_update_collection_configuration_with_spann() {
881 let mut config = InternalCollectionConfiguration {
882 vector_index: VectorIndexConfiguration::Spann(InternalSpannConfiguration {
883 space: Space::Cosine,
884 ..Default::default()
885 }),
886 embedding_function: Some(EmbeddingFunctionConfiguration::Known(
887 EmbeddingFunctionNewConfiguration {
888 name: "test".to_string(),
889 config: serde_json::Value::Null,
890 },
891 )),
892 };
893 let update_config = UpdateCollectionConfiguration {
894 hnsw: None,
895 spann: Some(UpdateSpannConfiguration {
896 ef_search: Some(1),
897 ..Default::default()
898 }),
899 embedding_function: None,
900 };
901 config.update(&update_config.try_into().unwrap());
902 assert_eq!(
903 config.vector_index,
904 VectorIndexConfiguration::Spann(InternalSpannConfiguration {
905 space: Space::Cosine,
906 ef_search: 1,
907 ..Default::default()
908 })
909 );
910
911 assert_eq!(
912 config.embedding_function,
913 Some(EmbeddingFunctionConfiguration::Known(
914 EmbeddingFunctionNewConfiguration {
915 name: "test".to_string(),
916 config: serde_json::Value::Null,
917 },
918 ))
919 );
920 }
921
922 #[test]
923 fn test_update_collection_configuration_with_embedding_function() {
924 let mut config = InternalCollectionConfiguration {
925 vector_index: VectorIndexConfiguration::Hnsw(InternalHnswConfiguration::default()),
926 embedding_function: Some(EmbeddingFunctionConfiguration::Known(
927 EmbeddingFunctionNewConfiguration {
928 name: "test".to_string(),
929 config: serde_json::Value::Null,
930 },
931 )),
932 };
933 let emb_fn_config = EmbeddingFunctionNewConfiguration {
934 name: "test2".to_string(),
935 config: serde_json::Value::Object(serde_json::Map::from_iter([(
936 "test".to_string(),
937 serde_json::Value::String("test".to_string()),
938 )])),
939 };
940 let update_config = UpdateCollectionConfiguration {
941 hnsw: None,
942 spann: None,
943 embedding_function: Some(EmbeddingFunctionConfiguration::Known(emb_fn_config)),
944 };
945 config.update(&update_config.try_into().unwrap());
946 assert_eq!(
947 config.embedding_function,
948 Some(EmbeddingFunctionConfiguration::Known(
949 EmbeddingFunctionNewConfiguration {
950 name: "test2".to_string(),
951 config: serde_json::Value::Object(serde_json::Map::from_iter([(
952 "test".to_string(),
953 serde_json::Value::String("test".to_string()),
954 )])),
955 },
956 ))
957 );
958 }
959}