Skip to main content

ceres_core/
search.rs

1//! Search service for semantic dataset queries.
2//!
3//! This module provides a high-level service for performing semantic searches
4//! across the dataset index using vector embeddings.
5
6use crate::traits::{DatasetStore, EmbeddingProvider};
7use crate::{AppError, SearchResult};
8
9/// Service for semantic search operations.
10///
11/// This service encapsulates the search business logic, coordinating between
12/// the embedding provider and the dataset store.
13///
14/// # Type Parameters
15///
16/// * `S` - Dataset store implementation (e.g., `DatasetRepository`)
17/// * `E` - Embedding provider implementation (e.g., `GeminiClient`)
18///
19/// # Example
20///
21/// ```ignore
22/// use ceres_core::search::SearchService;
23///
24/// let search_service = SearchService::new(repo, gemini);
25/// let results = search_service.search("climate data", 10).await?;
26///
27/// for result in results {
28///     println!("{}: {:.2}", result.dataset.title, result.similarity_score);
29/// }
30/// ```
31pub struct SearchService<S, E>
32where
33    S: DatasetStore,
34    E: EmbeddingProvider,
35{
36    store: S,
37    embedding: E,
38}
39
40impl<S, E> Clone for SearchService<S, E>
41where
42    S: DatasetStore + Clone,
43    E: EmbeddingProvider + Clone,
44{
45    fn clone(&self) -> Self {
46        Self {
47            store: self.store.clone(),
48            embedding: self.embedding.clone(),
49        }
50    }
51}
52
53impl<S, E> SearchService<S, E>
54where
55    S: DatasetStore,
56    E: EmbeddingProvider,
57{
58    /// Creates a new search service.
59    ///
60    /// # Arguments
61    ///
62    /// * `store` - Dataset store for vector search queries
63    /// * `embedding` - Embedding provider for generating query embeddings
64    pub fn new(store: S, embedding: E) -> Self {
65        Self { store, embedding }
66    }
67
68    /// Performs semantic search and returns ranked results.
69    ///
70    /// This method:
71    /// 1. Generates an embedding vector from the query text
72    /// 2. Searches the database using cosine similarity
73    /// 3. Returns results ordered by similarity (highest first)
74    ///
75    /// # Arguments
76    ///
77    /// * `query` - The search query text
78    /// * `limit` - Maximum number of results to return
79    ///
80    /// # Returns
81    ///
82    /// A vector of [`SearchResult`], ordered by similarity score (highest first).
83    ///
84    /// # Errors
85    ///
86    /// Returns an error if:
87    /// - The embedding generation fails (API error, network error, etc.)
88    /// - The database query fails
89    pub async fn search(&self, query: &str, limit: usize) -> Result<Vec<SearchResult>, AppError> {
90        let query_vector = self.embedding.generate(query).await?;
91        self.store.search(query_vector, limit).await
92    }
93}