1use std::collections::HashMap;
11use std::path::Path;
12
13use crate::{embed::Embedder, error::Result, vector_store::VecInfo};
14
15#[derive(Debug, Clone)]
19pub struct FtsQuery<'a> {
20 pub query: &'a str,
22 pub limit: usize,
24 pub path_prefix: Option<&'a Path>,
27}
28
29impl<'a> FtsQuery<'a> {
30 pub fn new(query: &'a str) -> Self {
31 Self {
32 query,
33 limit: 10,
34 path_prefix: None,
35 }
36 }
37
38 pub fn limit(mut self, n: usize) -> Self {
39 self.limit = n;
40 self
41 }
42
43 pub fn path_prefix(mut self, p: &'a Path) -> Self {
44 self.path_prefix = Some(p);
45 self
46 }
47}
48
49pub struct VectorQuery<'a> {
54 pub query: &'a str,
55 pub embedder: &'a dyn Embedder,
56 pub limit: usize,
57 pub path_prefix: Option<&'a Path>,
58}
59
60impl<'a> VectorQuery<'a> {
61 pub fn new(query: &'a str, embedder: &'a dyn Embedder) -> Self {
62 Self {
63 query,
64 embedder,
65 limit: 10,
66 path_prefix: None,
67 }
68 }
69
70 pub fn limit(mut self, n: usize) -> Self {
71 self.limit = n;
72 self
73 }
74
75 pub fn path_prefix(mut self, p: &'a Path) -> Self {
76 self.path_prefix = Some(p);
77 self
78 }
79}
80
81impl std::fmt::Debug for VectorQuery<'_> {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.debug_struct("VectorQuery")
84 .field("query", &self.query)
85 .field("limit", &self.limit)
86 .field("path_prefix", &self.path_prefix)
87 .finish_non_exhaustive()
88 }
89}
90
91pub struct HybridQuery<'a> {
95 pub query: &'a str,
96 pub embedder: Option<&'a dyn Embedder>,
97 pub limit: usize,
98 pub path_prefix: Option<&'a Path>,
99 pub rrf_k: f64,
100 pub weight_fts: f64,
101 pub weight_sem: f64,
102}
103
104impl<'a> HybridQuery<'a> {
105 pub fn new(query: &'a str) -> Self {
106 Self {
107 query,
108 embedder: None,
109 limit: 10,
110 path_prefix: None,
111 rrf_k: 60.0,
112 weight_fts: 1.0,
113 weight_sem: 1.0,
114 }
115 }
116
117 pub fn embedder(mut self, e: &'a dyn Embedder) -> Self {
118 self.embedder = Some(e);
119 self
120 }
121
122 pub fn limit(mut self, n: usize) -> Self {
123 self.limit = n;
124 self
125 }
126
127 pub fn path_prefix(mut self, p: &'a Path) -> Self {
128 self.path_prefix = Some(p);
129 self
130 }
131
132 pub fn rrf_k(mut self, k: f64) -> Self {
133 self.rrf_k = k;
134 self
135 }
136
137 pub fn weight_fts(mut self, w: f64) -> Self {
138 self.weight_fts = w;
139 self
140 }
141
142 pub fn weight_sem(mut self, w: f64) -> Self {
143 self.weight_sem = w;
144 self
145 }
146}
147
148impl std::fmt::Debug for HybridQuery<'_> {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 f.debug_struct("HybridQuery")
151 .field("query", &self.query)
152 .field("limit", &self.limit)
153 .field("path_prefix", &self.path_prefix)
154 .field("rrf_k", &self.rrf_k)
155 .field("weight_fts", &self.weight_fts)
156 .field("weight_sem", &self.weight_sem)
157 .finish_non_exhaustive()
158 }
159}
160
161#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
165pub struct Document {
166 pub id: i64,
168 pub body: String,
171 pub path: String,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub chunks: Option<Vec<(usize, usize, String)>>,
180}
181
182#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
184pub struct ChunkHit {
185 pub line_start: usize,
187 pub line_end: usize,
189 pub text: String,
191 pub score: f64,
195}
196
197#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
199pub struct FileSearchResult {
200 pub id: i64,
201 pub path: String,
202 pub score: f64,
205 pub chunks: Vec<ChunkHit>,
207}
208
209pub trait RetrieveStore: Send + Sync {
218 fn file_mtimes(&self) -> Result<HashMap<String, i64>>;
221 fn upsert_file(&self, path: &str, mtime: i64) -> Result<()>;
222 fn remove_file(&self, path: &str) -> Result<()>;
223 fn file_count(&self) -> Result<u64>;
224
225 fn upsert_document(&self, doc: &Document) -> Result<()>;
228 fn remove_document(&self, id: i64) -> Result<()>;
229
230 fn rebuild_fts(&self) -> Result<()>;
232
233 fn document_ids(&self) -> Result<Vec<i64>>;
234 fn document_count(&self) -> Result<u64>;
235
236 fn embed_pending(
240 &self,
241 embedder: &dyn Embedder,
242 on_progress: &dyn Fn(usize, usize),
243 ) -> Result<usize>;
244
245 fn vec_info(&self) -> Result<VecInfo>;
246
247 fn search_fts(&self, q: &FtsQuery<'_>) -> Result<Vec<FileSearchResult>>;
251
252 fn search_similar(&self, q: &VectorQuery<'_>) -> Result<Vec<FileSearchResult>>;
256
257 fn search_hybrid(&self, q: &HybridQuery<'_>) -> Result<Vec<FileSearchResult>> {
261 crate::db::default_hybrid(self, q)
262 }
263}