bbqvec 0.0.10

Scalable, embeddable, vector storage for approximate K-nearest-neighbors (AKNN)
Documentation
use anyhow::Result;
use std::{collections::HashMap, path::PathBuf};

use serde::{Deserialize, Serialize};

use crate::{
    quantization::Quantization, vector_file::VectorFile, Basis, Bitmap, Vector, VectorBackend, ID,
};

#[derive(Default)]
pub struct DiskBackend<Q: Quantization> {
    dir: PathBuf,
    metadata: DiskMetadata,
    vector_files: HashMap<usize, VectorFile<Q>>,
    token: u64,
}

#[derive(Serialize, Deserialize, Default)]
pub(crate) struct DiskMetadata {
    pub dimensions: usize,
    pub quantization: String,
    pub vecs_per_file: usize,
    pub vec_files: Vec<usize>,
}

const DEFAULT_VECS_PER_FILE: usize = 200_000;

impl<Q: Quantization> DiskBackend<Q> {
    pub fn open(path: PathBuf, dimensions: usize) -> Result<Self> {
        let mut token: u64 = rand::random();
        if token == 0 {
            token = 1;
        }
        let mut s = Self {
            dir: path,
            metadata: DiskMetadata {
                dimensions,
                quantization: Q::name().into(),
                vecs_per_file: DEFAULT_VECS_PER_FILE,
                vec_files: Vec::new(),
            },
            token,
            ..Default::default()
        };
        s.open_files()?;
        Ok(s)
    }

    fn open_files(&mut self) -> Result<()> {
        let metadata_path = self.dir.join("metadata.json");
        if !metadata_path.exists() {
            return self.create_new();
        }
        let metadata_contents = std::fs::read_to_string(&metadata_path)?;
        let metadata: DiskMetadata = serde_json::from_str(&metadata_contents)?;
        self.metadata = metadata;
        for vf in self.metadata.vec_files.iter() {
            let vector_file = VectorFile::<Q>::create_or_open(
                self.make_pagefile_path(vf),
                self.metadata.dimensions,
                self.metadata.vecs_per_file,
            )?;
            self.vector_files.insert(*vf, vector_file);
        }
        Ok(())
    }

    fn create_new(&mut self) -> Result<()> {
        std::fs::create_dir_all(self.dir.clone())?;
        self.save_metadata()
    }

    fn save_metadata(&self) -> Result<()> {
        let metadata_path = self.dir.join("metadata.json");
        Ok(serde_json::to_writer(
            &std::fs::File::create(metadata_path)?,
            &self.metadata,
        )?)
    }

    fn make_pagefile_path(&self, key: &usize) -> PathBuf {
        self.dir.join(format!("{:x}.vec", key))
    }
}

impl<Q: Quantization> VectorBackend for DiskBackend<Q> {
    fn put_vector(&mut self, id: ID, v: &Vector) -> Result<()> {
        todo!()
    }

    fn compute_similarity(&self, target: &Vector, target_id: ID) -> Result<f32> {
        todo!()
    }

    fn info(&self) -> crate::backend::BackendInfo {
        todo!()
    }

    fn iter_vector_ids(&self) -> impl Iterator<Item = ID> {
        0..(self.metadata.vecs_per_file * self.metadata.vec_files.len()) as ID
    }

    fn vector_exists(&self, id: ID) -> bool {
        todo!()
    }

    fn close(self) -> Result<()> {
        todo!()
    }

    fn load_bases(&self) -> Result<Option<Vec<Basis>>> {
        todo!()
    }

    fn load_bitmap<B: Bitmap>(&mut self, basis: usize, index: i32) -> Result<Option<B>> {
        todo!()
    }

    fn save_bases(&mut self, _bases: &[Basis]) -> Result<()> {
        Ok(())
    }

    fn save_bitmap(&mut self, _basis: usize, _index: usize, _bitmap: &impl Bitmap) -> Result<()> {
        Ok(())
    }

    fn sync(&self) -> Result<()> {
        for v in self.vector_files.values() {
            v.flush()?
        }
        self.save_metadata()
    }
}