use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimilaritySearchResult<T> {
pub item: T,
pub similarity: f32,
pub metadata: SimilarityMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SimilarityMetadata {
#[serde(default)]
pub embedding_model: String,
pub embedding_timestamp: Option<chrono::DateTime<chrono::Utc>>,
#[serde(default)]
pub context: serde_json::Value,
}
#[must_use]
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() || a.is_empty() {
return 0.0;
}
let mut dot_product = 0.0f32;
let mut norm_a_sq = 0.0f32;
let mut norm_b_sq = 0.0f32;
for (&x, &y) in a.iter().zip(b.iter()) {
dot_product += x * y;
norm_a_sq += x * x;
norm_b_sq += y * y;
}
if norm_a_sq <= 0.0 || norm_b_sq <= 0.0 {
return 0.0;
}
let similarity = dot_product / (norm_a_sq.sqrt() * norm_b_sq.sqrt());
(similarity + 1.0) / 2.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosine_similarity() {
let vec1 = vec![1.0, 2.0, 3.0];
let vec2 = vec![1.0, 2.0, 3.0];
let similarity = cosine_similarity(&vec1, &vec2);
assert!((similarity - 1.0).abs() < 0.001);
let vec3 = vec![1.0, 0.0];
let vec4 = vec![0.0, 1.0];
let similarity = cosine_similarity(&vec3, &vec4);
assert!((similarity - 0.5).abs() < 0.001);
let vec5 = vec![1.0, 2.0, 3.0];
let vec6 = vec![-1.0, -2.0, -3.0];
let similarity = cosine_similarity(&vec5, &vec6);
assert!((similarity - 0.0).abs() < 0.001);
let vec7 = vec![1.0, 2.0];
let vec8 = vec![1.0, 2.0, 3.0];
let similarity = cosine_similarity(&vec7, &vec8);
assert_eq!(similarity, 0.0);
}
#[test]
fn test_cosine_similarity_empty() {
let vec1: Vec<f32> = vec![];
let vec2: Vec<f32> = vec![];
assert_eq!(cosine_similarity(&vec1, &vec2), 0.0);
let vec3 = vec![1.0, 2.0, 3.0];
assert_eq!(cosine_similarity(&vec1, &vec3), 0.0);
assert_eq!(cosine_similarity(&vec3, &vec1), 0.0);
}
#[test]
fn test_cosine_similarity_zero_magnitude() {
let vec1 = vec![0.0, 0.0, 0.0];
let vec2 = vec![1.0, 2.0, 3.0];
assert_eq!(cosine_similarity(&vec1, &vec2), 0.0);
let vec3 = vec![1.0, 2.0, 3.0];
let vec4 = vec![0.0, 0.0, 0.0];
assert_eq!(cosine_similarity(&vec3, &vec4), 0.0);
let vec5 = vec![0.0, 0.0, 0.0];
let vec6 = vec![0.0, 0.0, 0.0];
assert_eq!(cosine_similarity(&vec5, &vec6), 0.0);
}
}