use crate::db::traits::StoreSearch;
use crate::db::SqliteStore;
use crate::embedding::cache::Vector;
use crate::search::executor_types::{RankedResult, RankedResults, SearchSource};
use crate::search::types::SearchMode;
use tracing::{debug, instrument, warn};
pub struct VectorExecutor;
impl VectorExecutor {
#[instrument(skip(store, query_embedding), fields(embedding_dim = query_embedding.len()))]
pub async fn execute(
store: &SqliteStore,
query_embedding: &Vector,
mode: SearchMode,
repo_id: i64,
worktree_id: Option<i64>,
limit: usize,
) -> Result<RankedResults, VectorError> {
if query_embedding.is_empty() {
warn!("Empty query embedding provided");
return Ok(RankedResults::empty(SearchSource::Vector));
}
let fetch_limit = (limit * 3) as i64;
debug!(
"Executing vector search (mode: {:?}, limit: {}, over-fetch: {})",
mode, limit, fetch_limit
);
let hits = store
.search_vector_by_id(repo_id, worktree_id, query_embedding, fetch_limit)
.await
.map_err(|e| VectorError::Database(e.to_string()))?;
let results: Vec<RankedResult> = hits
.into_iter()
.enumerate()
.map(|(i, hit)| RankedResult::new(hit.chunk_id, hit.score as f32, i + 1))
.collect();
debug!("Vector search returned {} results", results.len());
Ok(RankedResults::new(results, SearchSource::Vector))
}
}
#[derive(Debug, thiserror::Error)]
pub enum VectorError {
#[error("Database error: {0}")]
Database(String),
#[error("Embedding error: {0}")]
Embedding(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vector_executor_exists() {
let _executor = VectorExecutor;
}
}