use std::io;
use std::path::Path;
use crate::Builder;
use crate::distance::Distance;
use crate::hnsw::{Config, Hnsw};
use crate::payload::Payload;
use crate::persist;
pub struct LabeledResult<'a, L> {
pub id: usize,
pub distance: f32,
pub payload: &'a L,
pub embedding: &'a [f32],
}
pub struct LabeledIndex<D: Distance, L: Payload> {
pub inner: Hnsw<D>,
payloads: Vec<L>,
}
impl<D: Distance, L: Payload> LabeledIndex<D, L> {
pub fn new(config: Config, metric: D) -> Self {
Self {
payloads: Vec::with_capacity(config.capacity),
inner: Hnsw::new(config, metric),
}
}
pub fn from_builder(builder: Builder, metric: D) -> Self {
Self::new(builder.into_config(), metric)
}
pub fn insert(&mut self, embedding: Vec<f32>, payload: L) -> usize {
let id = self.inner.insert(embedding);
debug_assert_eq!(id, self.payloads.len());
self.payloads.push(payload);
id
}
pub fn search<'a>(
&'a self,
query: &[f32],
k: usize,
ef: usize,
) -> Vec<LabeledResult<'a, L>> {
self.inner
.search(query, k, ef)
.into_iter()
.map(|sr| LabeledResult {
id: sr.id,
distance: sr.distance,
payload: &self.payloads[sr.id],
embedding: self.inner.get_vector(sr.id),
})
.collect()
}
pub fn get_payload(&self, id: usize) -> &L { &self.payloads[id] }
pub fn get_embedding(&self, id: usize) -> &[f32] { self.inner.get_vector(id) }
pub fn len(&self) -> usize { self.inner.len() }
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
pub fn save(&self, path: impl AsRef<Path>) -> io::Result<()> {
persist::save_with_payload(&self.inner, &self.payloads, path)
}
pub fn load(path: impl AsRef<Path>, metric: D) -> io::Result<Self> {
let (inner, payloads) = persist::load_with_payload(path, metric)?;
Ok(Self { inner, payloads })
}
pub fn load_mmap(path: impl AsRef<Path>, metric: D) -> io::Result<Self> {
let (inner, payloads) = persist::load_mmap_with_payload(path, metric)?;
Ok(Self { inner, payloads })
}
}
impl Builder {
pub fn build_labeled<D: Distance, L: Payload>(self, metric: D) -> LabeledIndex<D, L> {
LabeledIndex::from_builder(self, metric)
}
}