1use crate::similarity::{ScoreIndex, get_cache_attr, get_distance_fn, normalize};
15use anyhow::Result;
16use log::{debug, error, info};
17use rayon::prelude::*;
18use std::collections::HashSet;
19use std::collections::hash_map::DefaultHasher;
20use std::collections::{BinaryHeap, HashMap};
21use std::fs::File;
22use std::hash::{Hash, Hasher};
23use std::io::{BufReader, Write};
24
25use serde::{Deserialize, Serialize};
26
27#[derive(Debug, serde::Serialize, serde::Deserialize)]
43pub struct CacheDB {
44 pub collections: HashMap<String, Collection>,
46}
47
48#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
58pub struct SimilarityResult {
59 pub score: f32,
61 pub embedding: Embedding,
63}
64
65#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
83pub struct Collection {
84 pub dimension: usize,
86 pub distance: Distance,
88 #[serde(default)]
90 pub embeddings: Vec<Embedding>,
91}
92
93#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
117pub struct Embedding {
118 pub id: HashMap<String, String>,
120 pub vector: Vec<f32>,
122 pub metadata: Option<HashMap<String, String>>,
124}
125
126#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
144pub enum Distance {
145 #[serde(rename = "euclidean")]
147 Euclidean,
148 #[serde(rename = "cosine")]
150 Cosine,
151 #[serde(rename = "dot")]
153 DotProduct,
154}
155
156#[derive(Debug, thiserror::Error, PartialEq)]
161pub enum Error {
162 #[error("Collection already exists")]
163 UniqueViolation,
164
165 #[error("Embedding already exists")]
166 EmbeddingUniqueViolation,
167
168 #[error("Collection doesn't exist")]
169 NotFound,
170
171 #[error("The dimension of the vector doesn't match the dimension of the collection")]
172 DimensionMismatch,
173
174 #[error("Failed to initialize the logger")]
175 LoggerInitializationError,
176}
177
178#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
179pub struct CreateCollectionStruct {
196 pub collection_name: String,
198 pub dimension: usize,
200 pub distance: Distance,
202}
203
204#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
205
206pub struct InsertEmbeddingStruct {
210 pub collection_name: String,
212 pub embedding: Embedding,
214}
215
216#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
217pub struct CollectionHandlerStruct {
222 pub collection_name: String,
224}
225
226#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
227pub struct BatchInsertEmbeddingsStruct {
232 pub collection_name: String,
234 pub embeddings: Vec<Embedding>,
236}
237
238#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
239pub struct GetSimilarityStruct {
256 pub collection_name: String,
258 pub query_vector: Vec<f32>,
260 pub k: usize,
262}
263
264pub fn hash_map_id(id: &HashMap<String, String>) -> u64 {
267 let mut hasher = DefaultHasher::new();
268 for (key, value) in id {
269 key.hash(&mut hasher);
270 value.hash(&mut hasher);
271 }
272 hasher.finish()
273}
274
275impl Collection {
277 pub fn get_similarity(&self, query: &[f32], k: usize) -> Vec<SimilarityResult> {
288 debug!(
289 "Starting similarity computation with query vector of length {} and top k = {}",
290 query.len(),
291 k
292 );
293
294 let memo_attr = get_cache_attr(self.distance, query);
296 let distance_fn = get_distance_fn(self.distance);
297
298 debug!("Using distance function: {:?}", self.distance);
299 debug!("Memo attributes for distance function: {:?}", memo_attr);
300
301 let scores = self
303 .embeddings
304 .par_iter()
305 .enumerate()
306 .map(|(index, embedding)| {
307 let score = distance_fn(&embedding.vector, query, memo_attr);
308 ScoreIndex { score, index }
309 })
310 .collect::<Vec<_>>();
311 debug!("Calculated {} similarity scores", scores.len());
312 let mut heap = BinaryHeap::new();
314 for score_index in scores {
315 if heap.len() < k || score_index < *heap.peek().unwrap() {
317 heap.push(score_index);
318 if heap.len() > k {
319 heap.pop();
320 }
321 }
322 }
323 debug!("Top k heap size: {}", heap.len());
324
325 let result: Vec<SimilarityResult> = heap
327 .into_sorted_vec()
328 .into_iter()
329 .map(|ScoreIndex { score, index }| SimilarityResult {
330 score,
331 embedding: self.embeddings[index].clone(),
332 })
333 .collect();
334 info!(
335 "Similarity computed successfully'{}' ",
336 format!("{:?}", result)
337 );
338 result
339 }
340}
341
342impl CacheDB {
344 pub fn new() -> Self {
346 Self {
347 collections: HashMap::new(),
348 }
349 }
350 pub fn create_collection(
362 &mut self,
363 name: String,
364 dimension: usize,
365 distance: Distance,
366 ) -> Result<Collection, Error> {
367 if self.collections.contains_key(&name) {
369 error!("Collection: '{}', already exists", name);
370 return Err(Error::UniqueViolation);
371 }
372
373 let collection = Collection {
375 dimension,
376 distance,
377 embeddings: Vec::new(),
378 };
379 self.collections.insert(name.clone(), collection.clone());
380
381 info!(
382 "Created new collection with name: '{}', dimension: '{}', distance: '{:?}'",
383 name, dimension, distance
384 );
385 Ok(collection)
386 }
387
388 pub fn delete_collection(&mut self, name: &str) -> Result<(), Error> {
398 if !self.collections.contains_key(name) {
400 error!("Collection name: '{}', does not exist", name);
401 return Err(Error::NotFound);
402 }
403
404 self.collections.remove(name);
406
407 info!("Deleted collection: '{}'", name);
408 Ok(())
409 }
410
411 pub fn insert_into_collection(
422 &mut self,
423 collection_name: &str,
424 mut embedding: Embedding,
425 ) -> Result<(), Error> {
426 let collection = self
428 .collections
429 .get_mut(collection_name)
430 .ok_or(Error::NotFound)?;
431
432 let mut unique_ids: HashSet<u64> = collection
434 .embeddings
435 .iter()
436 .map(|e| hash_map_id(&e.id))
437 .collect();
438
439 if !unique_ids.insert(hash_map_id(&embedding.id)) {
441 error!(
442 "Embedding with ID '{}' already exists in collection '{}'",
443 format!("{:?}", embedding.id),
444 collection_name
445 );
446 return Err(Error::EmbeddingUniqueViolation);
447 }
448
449 if embedding.vector.len() != collection.dimension {
451 error!(
452 "Dimension mismatch: embedding vector length is '{}' but collection '{}' expects dimension '{}'",
453 embedding.vector.len(),
454 collection_name,
455 collection.dimension
456 );
457 return Err(Error::DimensionMismatch);
458 }
459
460 if collection.distance == Distance::Cosine {
462 embedding.vector = normalize(&embedding.vector);
463 }
464
465 collection.embeddings.push(embedding.clone());
467
468 info!(
469 "Embedding: '{:?}', successfully inserted into collection '{}'",
470 embedding, collection_name
471 );
472 Ok(())
473 }
474
475 pub fn update_collection(
486 &mut self,
487 collection_name: &str,
488 mut new_embeddings: Vec<Embedding>,
489 ) -> Result<(), Error> {
490 let collection = self
492 .collections
493 .get_mut(collection_name)
494 .ok_or(Error::NotFound)?;
495
496 for embedding in &mut new_embeddings {
498 let mut unique_ids: HashSet<u64> = collection
500 .embeddings
501 .iter()
502 .map(|e| hash_map_id(&e.id))
503 .collect();
504
505 if !unique_ids.insert(hash_map_id(&embedding.id)) {
507 error!(
508 "Embedding with ID '{}' already exists in collection '{}'",
509 format!("{:?}", embedding.id),
510 collection_name
511 );
512 return Err(Error::UniqueViolation);
513 }
514
515 if embedding.vector.len() != collection.dimension {
517 error!(
518 "Dimension mismatch: embedding vector length is '{}' but collection '{}' expects dimension '{}'",
519 embedding.vector.len(),
520 collection_name,
521 collection.dimension
522 );
523 return Err(Error::DimensionMismatch);
524 }
525
526 if collection.distance == Distance::Cosine {
528 embedding.vector = normalize(&embedding.vector);
529 }
530
531 collection.embeddings.push(embedding.clone());
533 }
534
535 info!(
536 "Embedding: '{:?}' successfully updated to collection '{}'",
537 new_embeddings, collection_name
538 );
539 Ok(())
540 }
541
542 pub fn get_collection(&self, collection_name: &str) -> Option<&Collection> {
552 match self.collections.get(collection_name) {
553 Some(collection) => {
554 info!("Collection '{}' found", collection_name);
555 Some(collection)
556 }
557 None => {
558 error!("Collection '{}' not found", collection_name);
559 None
560 }
561 }
562 }
563
564 pub fn get_embeddings(&self, collection_name: &str) -> Option<Vec<Embedding>> {
574 match self.collections.get(collection_name) {
575 Some(collection) => {
576 info!(
577 "Successfully retrieved embeddings for collection '{}'",
578 collection_name
579 );
580 Some(collection.embeddings.clone())
581 }
582 None => {
583 error!("Collection '{}' not found", collection_name);
584 None
585 }
586 }
587 }
588
589 pub fn save(&self, filepath: Option<&str>) -> Result<()> {
625 let file_content: String = serde_json::to_string_pretty(&self)?;
626
627 let filepath: &str = if let Some(filepath) = filepath {
629 filepath
630 } else {
631 "memvdb.json"
632 };
633
634 let mut file: File = File::create(filepath)?;
636 file.write_all(file_content.as_bytes())?;
637
638 info!("Database successfully saved to '{}'", filepath);
639 Ok(())
640 }
641
642 pub fn load(filepath: &str) -> Result<Self> {
678 let file: File = std::fs::OpenOptions::new().open(filepath)?;
679 let buffer: BufReader<File> = BufReader::new(file);
680
681 let db: CacheDB = serde_json::from_reader(buffer)?;
682 info!("Database successfully loaded from '{}'", filepath);
683 Ok(db)
684 }
685}
686
687#[cfg(test)]
688mod tests {
689 use super::*;
690
691 #[test]
692 fn test_create_collection_success_eucledean() {
693 let mut db = CacheDB::new();
694 let result = db.create_collection("test_collection".to_string(), 100, Distance::Euclidean);
695
696 assert!(result.is_ok());
697 let collection = result.unwrap();
698 assert_eq!(collection.dimension, 100);
699 assert_eq!(collection.distance, Distance::Euclidean);
700 assert!(db.collections.contains_key("test_collection"));
701 }
702
703 #[test]
704 fn test_create_collection_success_cosine() {
705 let mut db = CacheDB::new();
706 let result = db.create_collection("test_collection".to_string(), 100, Distance::Cosine);
707
708 assert!(result.is_ok());
709 let collection = result.unwrap();
710 assert_eq!(collection.dimension, 100);
711 assert_eq!(collection.distance, Distance::Cosine);
712 assert!(db.collections.contains_key("test_collection"));
713 }
714
715 #[test]
716 fn test_create_collection_success_dot_product() {
717 let mut db = CacheDB::new();
718 let result = db.create_collection("test_collection".to_string(), 100, Distance::DotProduct);
719
720 assert!(result.is_ok());
721 let collection = result.unwrap();
722 assert_eq!(collection.dimension, 100);
723 assert_eq!(collection.distance, Distance::DotProduct);
724 assert!(db.collections.contains_key("test_collection"));
725 }
726
727 #[test]
728 fn test_create_collection_already_exists() {
729 let mut db = CacheDB::new();
730 db.create_collection("test_collection".to_string(), 100, Distance::Euclidean)
731 .unwrap();
732
733 let result = db.create_collection("test_collection".to_string(), 200, Distance::Cosine);
734 assert!(result.is_err());
735 }
736
737 #[test]
738 fn test_insert_into_collection_success() {
739 let mut db = CacheDB::new();
740 let collection = Collection {
741 dimension: 3,
742 distance: Distance::Euclidean,
743 embeddings: Vec::new(),
744 };
745 db.collections
746 .insert("test_collection".to_string(), collection);
747 let mut metadata = HashMap::new();
748 metadata.insert("page".to_string(), "1".to_string());
749 metadata.insert(
750 "text".to_string(),
751 "This is a test metadata text".to_string(),
752 );
753
754 let mut id = HashMap::new();
755 id.insert("unique_id".to_string(), "1".to_string());
756
757 let embedding = Embedding {
758 id: id,
759 vector: vec![1.0, 2.0, 3.0],
760 metadata: Some(metadata),
761 };
762
763 let result = db.insert_into_collection("test_collection", embedding.clone());
764 assert!(result.is_ok());
765
766 let collection = db.collections.get("test_collection").unwrap();
768 assert_eq!(collection.embeddings.len(), 1);
769 assert_eq!(collection.embeddings[0], embedding);
770 }
771
772 #[test]
773 fn test_update_collection_success() {
774 let mut db = CacheDB::new();
775
776 let mut metadata = HashMap::new();
777 metadata.insert("page".to_string(), "1".to_string());
778 metadata.insert(
779 "text".to_string(),
780 "This is a test metadata text".to_string(),
781 );
782
783 let mut id = HashMap::new();
784 id.insert("unique_id".to_string(), "0".to_string());
785
786 let collection = Collection {
787 dimension: 3,
788 distance: Distance::Euclidean,
789 embeddings: vec![Embedding {
790 id: id,
791 vector: vec![1.0, 2.0, 3.0],
792 metadata: Some(metadata.clone()),
793 }],
794 };
795
796 db.collections
797 .insert("test_collection".to_string(), collection);
798
799 let mut id_1 = HashMap::new();
800 id_1.insert("unique_id".to_string(), "1".to_string());
801 let mut id_2 = HashMap::new();
802 id_2.insert("unique_id".to_string(), "2".to_string());
803
804 let new_embeddings = vec![
805 Embedding {
806 id: id_1, vector: vec![4.0, 5.0, 6.0],
808 metadata: Some(metadata.clone()),
809 },
810 Embedding {
811 id: id_2,
812 vector: vec![7.0, 8.0, 9.0],
813 metadata: Some(metadata.clone()),
814 },
815 ];
816
817 let result = db.update_collection("test_collection", new_embeddings.clone());
818 assert!(result.is_ok());
819
820 let collection = db.collections.get("test_collection").unwrap();
822 assert_eq!(collection.embeddings.len(), 3);
823 assert_eq!(collection.embeddings[1..], new_embeddings[..]);
824 }
825
826 #[test]
827 fn test_update_collection_duplicate_embedding() {
828 let mut db = CacheDB::new();
829 let mut metadata = HashMap::new();
830 metadata.insert("page".to_string(), "1".to_string());
831 metadata.insert(
832 "text".to_string(),
833 "This is a test metadata text".to_string(),
834 );
835
836 let mut id = HashMap::new();
837 id.insert("unique_id".to_string(), "0".to_string());
838
839 let collection = Collection {
840 dimension: 3,
841 distance: Distance::Euclidean,
842 embeddings: vec![Embedding {
843 id: id.clone(),
844 vector: vec![1.0, 2.0, 3.0],
845 metadata: Some(metadata.clone()),
846 }],
847 };
848 db.collections
849 .insert("test_collection".to_string(), collection);
850
851 let mut id_1 = HashMap::new();
852 id_1.insert("unique_id".to_string(), "1".to_string());
853 let mut id_2 = HashMap::new();
854 id_2.insert("unique_id".to_string(), "2".to_string());
855
856 let new_embeddings = vec![
857 Embedding {
858 id: id, vector: vec![4.0, 5.0, 6.0],
860 metadata: Some(metadata.clone()),
861 },
862 Embedding {
863 id: id_2,
864 vector: vec![7.0, 8.0, 9.0],
865 metadata: Some(metadata.clone()),
866 },
867 ];
868
869 let result = db.update_collection("test_collection", new_embeddings);
870 assert!(result.is_err());
871 assert_eq!(result.err(), Some(Error::UniqueViolation));
872 }
873
874 #[test]
875 fn test_update_collection_dimension_mismatch() {
876 let mut db = CacheDB::new();
877 let collection = Collection {
878 dimension: 3,
879 distance: Distance::Euclidean,
880 embeddings: Vec::new(),
881 };
882 db.collections
883 .insert("test_collection".to_string(), collection);
884
885 let mut metadata = HashMap::new();
886 metadata.insert("page".to_string(), "1".to_string());
887 metadata.insert(
888 "text".to_string(),
889 "This is a test metadata text".to_string(),
890 );
891
892 let mut id = HashMap::new();
893 id.insert("unique_id".to_string(), "0".to_string());
894
895 let new_embeddings = vec![Embedding {
896 id: id,
897 vector: vec![1.0, 2.0],
898 metadata: Some(metadata), }];
900
901 let result = db.update_collection("test_collection", new_embeddings);
902 assert!(result.is_err());
903 assert_eq!(result.err(), Some(Error::DimensionMismatch));
904 }
905
906 #[test]
907 fn test_delete_collection_success() {
908 let mut db = CacheDB::new();
909 db.collections.insert(
910 "test_collection".to_string(),
911 Collection {
912 dimension: 3,
913 distance: Distance::Euclidean,
914 embeddings: Vec::new(),
915 },
916 );
917
918 let result = db.delete_collection("test_collection");
919 assert!(result.is_ok());
920
921 assert!(!db.collections.contains_key("test_collection"));
923 }
924
925 #[test]
926 fn test_delete_collection_not_found() {
927 let mut db = CacheDB::new();
928
929 let result = db.delete_collection("non_existent_collection");
930 assert!(result.is_err());
931 assert_eq!(result.err(), Some(Error::NotFound));
932 }
933
934 #[test]
935 fn test_get_collection_success() {
936 let mut db = CacheDB::new();
937 let collection = Collection {
938 dimension: 3,
939 distance: Distance::Euclidean,
940 embeddings: Vec::new(),
941 };
942 db.collections
943 .insert("test_collection".to_string(), collection.clone());
944
945 let result = db.get_collection("test_collection");
946 assert!(result.is_some());
947
948 assert_eq!(result.unwrap(), &collection);
950 }
951
952 #[test]
953 fn test_get_collection_not_found() {
954 let db = CacheDB::new();
955
956 let result = db.get_collection("non_existent_collection");
957 assert!(result.is_none());
958 }
959
960 #[test]
961 fn test_get_embedding_success() {
962 let mut db = CacheDB::new();
963
964 let mut id = HashMap::new();
965 id.insert("unique_id".to_string(), "0".to_string());
966
967 let mut id_1 = HashMap::new();
968 id_1.insert("unique_id".to_string(), "1".to_string());
969
970 let mut id_2 = HashMap::new();
971 id_2.insert("unique_id".to_string(), "2".to_string());
972
973 let collection = Collection {
974 dimension: 3,
975 distance: Distance::Euclidean,
976 embeddings: vec![
977 Embedding {
978 id: id,
979 vector: vec![1.0, 1.0, 1.0],
980 metadata: None,
981 },
982 Embedding {
983 id: id_1,
984 vector: vec![2.0, 2.0, 2.0],
985 metadata: None,
986 },
987 Embedding {
988 id: id_2,
989 vector: vec![3.0, 3.0, 3.0],
990 metadata: None,
991 },
992 ],
993 };
994 db.collections
995 .insert("test_collection".to_string(), collection.clone());
996 let result = db.get_embeddings("test_collection");
997 assert!(result.is_some());
998 assert_eq!(result, Some(collection.embeddings));
999 }
1000
1001 #[test]
1002 fn test_get_embeddings_not_found() {
1003 let db = CacheDB::new();
1004
1005 let result = db.get_embeddings("non_existent_collection");
1006 assert!(result.is_none());
1007 }
1008
1009 #[test]
1010 fn test_get_similarity() {
1011 let mut id = HashMap::new();
1012 id.insert("unique_id".to_string(), "0".to_string());
1013
1014 let mut id_1 = HashMap::new();
1015 id_1.insert("unique_id".to_string(), "1".to_string());
1016
1017 let mut id_2 = HashMap::new();
1018 id_2.insert("unique_id".to_string(), "2".to_string());
1019
1020 let collection = Collection {
1021 dimension: 3,
1022 distance: Distance::Euclidean,
1023 embeddings: vec![
1024 Embedding {
1025 id: id.clone(),
1026 vector: vec![1.0, 1.0, 1.0],
1027 metadata: None,
1028 },
1029 Embedding {
1030 id: id_1.clone(),
1031 vector: vec![2.0, 2.0, 2.0],
1032 metadata: None,
1033 },
1034 Embedding {
1035 id: id_2.clone(),
1036 vector: vec![3.0, 3.0, 3.0],
1037 metadata: None,
1038 },
1039 ],
1040 };
1041
1042 let query = vec![0.0, 0.0, 0.0];
1044
1045 let expected_results = vec![
1047 SimilarityResult {
1048 score: 0.0,
1049 embedding: Embedding {
1050 id: id_1,
1051 vector: vec![2.0, 2.0, 2.0],
1052 metadata: None,
1053 },
1054 },
1055 SimilarityResult {
1056 score: 0.0,
1057 embedding: Embedding {
1058 id: id_2,
1059 vector: vec![3.0, 3.0, 3.0],
1060 metadata: None,
1061 },
1062 },
1063 SimilarityResult {
1064 score: 0.0,
1065 embedding: Embedding {
1066 id: id,
1067 vector: vec![1.0, 1.0, 1.0],
1068 metadata: None,
1069 },
1070 },
1071 ];
1072
1073 let results = collection.get_similarity(&query, 3);
1075
1076 assert_eq!(results, expected_results);
1078 }
1079}