pub mod memory;
#[cfg(feature = "postgres")]
pub mod postgres;
#[cfg(feature = "sqlite")]
pub mod sqlite;
use crate::config::DbBackend;
use crate::model::{Chunk, Document, Scored};
use crate::{RagConfig, Result};
use async_trait::async_trait;
use std::sync::Arc;
#[async_trait]
pub trait VectorStore: Send + Sync {
async fn migrate(&self) -> Result<()>;
async fn upsert_document(&self, doc: &Document) -> Result<()>;
async fn find_document_by_hash(&self, hash: &str) -> Result<Option<String>>;
async fn insert_chunks(&self, chunks: &[Chunk]) -> Result<()>;
async fn vector_search(&self, query: &[f32], k: usize) -> Result<Vec<Scored>>;
async fn all_chunks(&self) -> Result<Vec<Chunk>>;
async fn count_chunks(&self) -> Result<usize>;
async fn count_documents(&self) -> Result<usize>;
async fn list_documents(&self) -> Result<Vec<Document>>;
async fn delete_document(&self, doc_id: &str) -> Result<()>;
async fn delete_documents_by_source(&self, source_uri: &str) -> Result<()>;
async fn clear(&self) -> Result<()>;
}
pub async fn from_config(cfg: &RagConfig) -> Result<Arc<dyn VectorStore>> {
let store: Arc<dyn VectorStore> = match cfg.db_backend {
DbBackend::Memory => Arc::new(memory::MemoryStore::new()),
DbBackend::Sqlite => {
#[cfg(feature = "sqlite")]
{
Arc::new(sqlite::SqliteStore::connect(&cfg.database_url, cfg.embed_dim).await?)
}
#[cfg(not(feature = "sqlite"))]
{
return Err(crate::RagError::FeatureDisabled(
"sqlite".into(),
"sqlite".into(),
));
}
}
DbBackend::Postgres => {
#[cfg(feature = "postgres")]
{
Arc::new(postgres::PostgresStore::connect(&cfg.database_url, cfg.embed_dim).await?)
}
#[cfg(not(feature = "postgres"))]
{
return Err(crate::RagError::FeatureDisabled(
"postgres".into(),
"postgres".into(),
));
}
}
};
store.migrate().await?;
Ok(store)
}
pub(crate) fn top_k_by_cosine(
query: &[f32],
candidates: impl IntoIterator<Item = (Chunk, Vec<f32>)>,
k: usize,
) -> Vec<Scored> {
let mut scored: Vec<Scored> = candidates
.into_iter()
.map(|(chunk, emb)| Scored::new(chunk, crate::math::cosine(query, &emb)))
.collect();
scored.sort_by(|a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(std::cmp::Ordering::Equal)
});
scored.truncate(k);
scored
}