mod types;
mod in_memory;
mod file;
#[cfg(feature = "qdrant")]
mod qdrant;
pub use types::*;
pub use in_memory::InMemoryVectorStore;
pub use file::{FileVectorStore, FileConfig};
#[cfg(feature = "qdrant")]
pub use qdrant::{QdrantVectorStore, QdrantConfig};
use async_trait::async_trait;
use anyhow::Result;
#[async_trait]
pub trait VectorStore: Send + Sync {
async fn upsert(&self, documents: Vec<EmbeddedDocument>) -> Result<UpsertStats>;
async fn search(
&self,
query_embedding: Vec<f32>,
filter: Option<Filter>,
top_k: usize,
) -> Result<Vec<SearchResult>>;
async fn delete(&self, ids: Vec<String>) -> Result<DeleteStats>;
async fn get(&self, ids: Vec<String>) -> Result<Vec<EmbeddedDocument>>;
async fn count(&self, filter: Option<Filter>) -> Result<usize>;
async fn health_check(&self) -> Result<HealthStatus>;
fn backend_name(&self) -> &'static str;
fn dimensions(&self) -> Option<usize> {
None
}
}
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() || a.is_empty() {
return 0.0;
}
let dot_product: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm_a == 0.0 || norm_b == 0.0 {
return 0.0;
}
dot_product / (norm_a * norm_b)
}
pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() {
return f32::MAX;
}
a.iter()
.zip(b.iter())
.map(|(x, y)| (x - y).powi(2))
.sum::<f32>()
.sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosine_similarity_identical() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
assert!((cosine_similarity(&a, &b) - 1.0).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![0.0, 1.0, 0.0];
assert!(cosine_similarity(&a, &b).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_opposite() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![-1.0, 0.0, 0.0];
assert!((cosine_similarity(&a, &b) + 1.0).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_empty() {
let a: Vec<f32> = vec![];
let b: Vec<f32> = vec![];
assert_eq!(cosine_similarity(&a, &b), 0.0);
}
#[test]
fn test_cosine_similarity_different_lengths() {
let a = vec![1.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
assert_eq!(cosine_similarity(&a, &b), 0.0);
}
#[test]
fn test_euclidean_distance_same_point() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0, 3.0];
assert!(euclidean_distance(&a, &b) < 1e-6);
}
#[test]
fn test_euclidean_distance_unit() {
let a = vec![0.0, 0.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
assert!((euclidean_distance(&a, &b) - 1.0).abs() < 1e-6);
}
}