1#[cfg(feature = "elasticsearch")]
47pub mod elasticsearch;
48pub mod algolia;
49pub mod traits;
50pub mod types;
51
52pub use traits::*;
53pub use types::*;
54#[cfg(feature = "elasticsearch")]
55pub use elasticsearch::*;
56pub use algolia::*;
57
58pub const VERSION: &str = env!("CARGO_PKG_VERSION");
60
61pub const DEFAULT_INDEX_NAME: &str = "default";
63
64pub const MAX_DOCUMENT_SIZE: usize = 100 * 1024 * 1024;
66
67pub const DEFAULT_SEARCH_LIMIT: usize = 100;
69
70pub const MAX_SEARCH_LIMIT: usize = 10000;
72
73#[derive(thiserror::Error, Debug)]
75pub enum SearchError {
76 #[error("Elasticsearch connection error: {0}")]
77 ElasticsearchConnection(String),
78
79 #[error("Elasticsearch operation error: {0}")]
80 ElasticsearchOperation(String),
81
82 #[error("Algolia API error: {0}")]
83 AlgoliaError(String),
84
85 #[error("Index not found: {0}")]
86 IndexNotFound(String),
87
88 #[error("Document not found: {0}")]
89 DocumentNotFound(String),
90
91 #[error("Invalid query: {0}")]
92 InvalidQuery(String),
93
94 #[error("Document serialization error: {0}")]
95 Serialization(String),
96
97 #[error("Document deserialization error: {0}")]
98 Deserialization(String),
99
100 #[error("Document too large: {size} bytes (max: {max} bytes)")]
101 DocumentTooLarge { size: usize, max: usize },
102
103 #[error("Invalid index name: {0}")]
104 InvalidIndexName(String),
105
106 #[error("Configuration error: {0}")]
107 ConfigError(String),
108
109 #[error("Network error: {0}")]
110 NetworkError(String),
111
112 #[error("Search timeout error")]
113 TimeoutError,
114
115 #[error("Search rate limit exceeded")]
116 RateLimitExceeded,
117
118 #[error("Search error: {0}")]
119 Other(String),
120}
121
122pub type SearchResult<T> = Result<T, SearchError>;
124
125#[derive(Debug, Clone)]
127pub struct SearchConfig {
128 pub default_limit: usize,
130
131 pub max_limit: usize,
133
134 pub enable_analytics: bool,
136
137 pub timeout_ms: u64,
139
140 pub enable_fuzzy_search: bool,
142
143 pub fuzziness: f32,
145
146 pub minimum_should_match: String,
148
149 pub enable_highlighting: bool,
151
152 pub highlight_pre_tag: String,
154
155 pub highlight_post_tag: String,
157
158 pub enable_snippets: bool,
160
161 pub snippet_length: usize,
163
164 pub enable_suggestions: bool,
166
167 pub suggestion_limit: usize,
169
170 pub enable_geo_search: bool,
172
173 pub default_crs: String,
175}
176
177impl Default for SearchConfig {
178 fn default() -> Self {
179 Self {
180 default_limit: DEFAULT_SEARCH_LIMIT,
181 max_limit: MAX_SEARCH_LIMIT,
182 enable_analytics: true,
183 timeout_ms: 5000,
184 enable_fuzzy_search: true,
185 fuzziness: 0.7,
186 minimum_should_match: "75%".to_string(),
187 enable_highlighting: true,
188 highlight_pre_tag: "<mark>".to_string(),
189 highlight_post_tag: "</mark>".to_string(),
190 enable_snippets: true,
191 snippet_length: 200,
192 enable_suggestions: true,
193 suggestion_limit: 10,
194 enable_geo_search: false,
195 default_crs: "EPSG4326".to_string(),
196 }
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub enum SearchBackend {
203 Elasticsearch,
204 Algolia,
205}
206
207impl SearchBackend {
208 pub fn name(&self) -> &'static str {
210 match self {
211 Self::Elasticsearch => "elasticsearch",
212 Self::Algolia => "algolia",
213 }
214 }
215}
216
217#[derive(Debug, Clone)]
219pub struct SearchStats {
220 pub total_queries: u64,
222
223 pub total_documents: u64,
225
226 pub avg_query_time_ms: f64,
228
229 pub queries_per_second: f64,
231
232 pub index_size_bytes: Option<u64>,
234
235 pub total_indices: u64,
237
238 pub last_query_at: Option<chrono::DateTime<chrono::Utc>>,
240
241 pub backend_stats: std::collections::HashMap<String, serde_json::Value>,
243}
244
245impl Default for SearchStats {
246 fn default() -> Self {
247 Self {
248 total_queries: 0,
249 total_documents: 0,
250 avg_query_time_ms: 0.0,
251 queries_per_second: 0.0,
252 index_size_bytes: None,
253 total_indices: 0,
254 last_query_at: None,
255 backend_stats: std::collections::HashMap::new(),
256 }
257 }
258}
259
260impl SearchStats {
261 pub fn update_with_query(&mut self, query_time_ms: u64) {
263 self.total_queries += 1;
264
265 let total_time = self.avg_query_time_ms * (self.total_queries - 1) as f64 + query_time_ms as f64;
267 self.avg_query_time_ms = total_time / self.total_queries as f64;
268
269 use chrono::Utc;
271 self.last_query_at = Some(Utc::now());
272 }
273
274 pub fn update_queries_per_second(&mut self, time_window_seconds: u64) {
276 if time_window_seconds > 0 {
277 self.queries_per_second = self.total_queries as f64 / time_window_seconds as f64;
278 }
279 }
280}