use std::collections::HashMap;
use manifoldb_core::PointId;
use serde_json::Value as JsonValue;
#[derive(Debug, Clone, PartialEq)]
pub enum Vector {
Dense(Vec<f32>),
Sparse(Vec<(u32, f32)>),
Multi(Vec<Vec<f32>>),
}
impl Vector {
#[must_use]
pub fn dimension(&self) -> usize {
match self {
Self::Dense(v) => v.len(),
Self::Sparse(v) => v.len(),
Self::Multi(v) => v.first().map_or(0, Vec::len),
}
}
#[must_use]
pub const fn is_dense(&self) -> bool {
matches!(self, Self::Dense(_))
}
#[must_use]
pub const fn is_sparse(&self) -> bool {
matches!(self, Self::Sparse(_))
}
#[must_use]
pub const fn is_multi(&self) -> bool {
matches!(self, Self::Multi(_))
}
#[must_use]
pub fn as_dense(&self) -> Option<&[f32]> {
match self {
Self::Dense(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn as_sparse(&self) -> Option<&[(u32, f32)]> {
match self {
Self::Sparse(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn as_multi(&self) -> Option<&[Vec<f32>]> {
match self {
Self::Multi(v) => Some(v),
_ => None,
}
}
}
impl From<Vec<f32>> for Vector {
fn from(v: Vec<f32>) -> Self {
Self::Dense(v)
}
}
impl From<Vec<(u32, f32)>> for Vector {
fn from(v: Vec<(u32, f32)>) -> Self {
Self::Sparse(v)
}
}
impl From<Vec<Vec<f32>>> for Vector {
fn from(v: Vec<Vec<f32>>) -> Self {
Self::Multi(v)
}
}
#[derive(Debug, Clone)]
pub struct PointStruct {
pub id: PointId,
pub payload: Option<JsonValue>,
pub vectors: HashMap<String, Vector>,
}
impl PointStruct {
#[must_use]
pub fn new(id: impl Into<PointId>) -> Self {
Self { id: id.into(), payload: None, vectors: HashMap::new() }
}
#[must_use]
pub fn with_payload(mut self, payload: impl Into<JsonValue>) -> Self {
self.payload = Some(payload.into());
self
}
#[must_use]
pub fn with_vector(mut self, name: impl Into<String>, vector: impl Into<Vector>) -> Self {
self.vectors.insert(name.into(), vector.into());
self
}
#[must_use]
pub fn with_vectors(mut self, vectors: impl IntoIterator<Item = (String, Vector)>) -> Self {
self.vectors.extend(vectors);
self
}
}
#[derive(Debug, Clone)]
pub struct ScoredPoint {
pub id: PointId,
pub score: f32,
pub payload: Option<JsonValue>,
pub vectors: Option<HashMap<String, Vector>>,
}
impl ScoredPoint {
#[must_use]
pub fn new(id: PointId, score: f32) -> Self {
Self { id, score, payload: None, vectors: None }
}
#[must_use]
pub fn with_payload(mut self, payload: JsonValue) -> Self {
self.payload = Some(payload);
self
}
#[must_use]
pub fn with_vectors(mut self, vectors: HashMap<String, Vector>) -> Self {
self.vectors = Some(vectors);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_vector_types() {
let dense = Vector::Dense(vec![0.1, 0.2, 0.3]);
assert!(dense.is_dense());
assert!(!dense.is_sparse());
assert!(!dense.is_multi());
assert_eq!(dense.dimension(), 3);
assert_eq!(dense.as_dense(), Some(&[0.1, 0.2, 0.3][..]));
let sparse = Vector::Sparse(vec![(0, 0.5), (10, 0.3)]);
assert!(sparse.is_sparse());
assert_eq!(sparse.dimension(), 2);
let multi = Vector::Multi(vec![vec![0.1, 0.2], vec![0.3, 0.4]]);
assert!(multi.is_multi());
assert_eq!(multi.dimension(), 2);
}
#[test]
fn test_vector_from() {
let dense: Vector = vec![0.1, 0.2, 0.3].into();
assert!(dense.is_dense());
let sparse: Vector = vec![(0u32, 0.5f32), (10, 0.3)].into();
assert!(sparse.is_sparse());
let multi: Vector = vec![vec![0.1, 0.2], vec![0.3, 0.4]].into();
assert!(multi.is_multi());
}
#[test]
fn test_point_struct_builder() {
let point = PointStruct::new(42u64)
.with_payload(json!({"title": "Test"}))
.with_vector("text", vec![0.1, 0.2, 0.3])
.with_vector("sparse", Vector::Sparse(vec![(100, 0.5)]));
assert_eq!(point.id.as_u64(), 42);
assert!(point.payload.is_some());
assert_eq!(point.vectors.len(), 2);
assert!(point.vectors.contains_key("text"));
assert!(point.vectors.contains_key("sparse"));
}
#[test]
fn test_scored_point() {
let scored =
ScoredPoint::new(PointId::new(1), 0.95).with_payload(json!({"title": "Best match"}));
assert_eq!(scored.id.as_u64(), 1);
assert!((scored.score - 0.95).abs() < 0.001);
assert!(scored.payload.is_some());
}
}