1mod aggregation;
14mod bm25;
15pub mod explain;
16pub mod feedback;
17mod fuzzy;
18mod hybrid;
19mod metadata;
20pub mod mmr;
21mod rerank;
22pub mod result_cache;
23pub mod semantic_cache;
24pub mod utility;
25
26#[cfg(feature = "neural-rerank")]
27pub mod neural_rerank;
28
29pub use aggregation::*;
30pub use bm25::*;
31pub use explain::*;
32pub use fuzzy::*;
33pub use hybrid::*;
34pub use metadata::*;
35pub use mmr::*;
36pub use rerank::*;
37pub use result_cache::*;
38
39use crate::types::SearchStrategy;
40
41pub fn select_search_strategy(query: &str) -> SearchStrategy {
43 let query = query.trim();
44 let word_count = query.split_whitespace().count();
45 let has_quotes = query.contains('"');
46 let has_operators = query.contains(':')
47 || query.contains(" AND ")
48 || query.contains(" OR ")
49 || query.contains(" NOT ");
50 let has_special = query.contains('*') || query.contains('?');
51
52 if has_quotes || has_operators || has_special {
54 return SearchStrategy::KeywordOnly;
55 }
56
57 if word_count <= 2 {
59 return SearchStrategy::KeywordOnly;
60 }
61
62 if word_count >= 8 {
64 return SearchStrategy::SemanticOnly;
65 }
66
67 SearchStrategy::Hybrid
69}
70
71#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
73pub enum DedupeStrategy {
74 #[default]
76 ById,
77 ByContentHash,
79}
80
81#[derive(Debug, Clone)]
83pub struct SearchConfig {
84 pub short_threshold: usize,
86 pub long_threshold: usize,
88 pub min_score: f32,
90 pub keyword_weight: f32,
92 pub semantic_weight: f32,
94 pub rrf_k: f32,
97 pub project_context_boost: f32,
99 pub project_context_path: Option<String>,
101 pub dedupe_strategy: DedupeStrategy,
103}
104
105impl Default for SearchConfig {
106 fn default() -> Self {
107 Self {
108 short_threshold: 2,
109 long_threshold: 8,
110 min_score: 0.1,
111 keyword_weight: 0.4,
112 semantic_weight: 0.6,
113 rrf_k: 60.0,
114 project_context_boost: 0.2,
115 project_context_path: None,
116 dedupe_strategy: DedupeStrategy::default(),
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_strategy_selection() {
127 assert_eq!(select_search_strategy("auth"), SearchStrategy::KeywordOnly);
129 assert_eq!(
130 select_search_strategy("jwt token"),
131 SearchStrategy::KeywordOnly
132 );
133
134 assert_eq!(
136 select_search_strategy("\"exact phrase\""),
137 SearchStrategy::KeywordOnly
138 );
139
140 assert_eq!(
142 select_search_strategy("auth AND jwt"),
143 SearchStrategy::KeywordOnly
144 );
145
146 assert_eq!(
148 select_search_strategy("how does authentication work"),
149 SearchStrategy::Hybrid
150 );
151
152 assert_eq!(
154 select_search_strategy(
155 "explain the authentication flow with jwt tokens and refresh mechanism"
156 ),
157 SearchStrategy::SemanticOnly
158 );
159 }
160}