diskann_disk/search/
search_mode.rs1use diskann::graph::ext::labeled::QueryLabelProvider;
13use diskann::graph::search::AdaptiveL;
14use diskann_providers::model::graph::provider::DeterminantDiversityParams;
15
16pub type SearchPredicate<'a> = Box<dyn Fn(&u32) -> bool + Send + Sync + 'a>;
21
22pub enum SearchMode<'a> {
37 FlatScan {
38 filter: Option<SearchPredicate<'a>>,
39 },
40
41 Graph {
42 filter: Option<SearchPredicate<'a>>,
43 },
44
45 InlineFilter {
46 filter: Box<dyn QueryLabelProvider<u32> + 'a>,
47 adaptive_l: Option<AdaptiveL>,
48 },
49
50 DiverseGraph {
51 filter: Option<SearchPredicate<'a>>,
52 params: DeterminantDiversityParams,
53 },
54}
55
56impl<'a> SearchMode<'a> {
57 pub fn flat() -> Self {
59 Self::FlatScan { filter: None }
60 }
61
62 pub fn flat_filtered<F>(predicate: F) -> Self
64 where
65 F: Fn(&u32) -> bool + Send + Sync + 'a,
66 {
67 Self::FlatScan {
68 filter: Some(Box::new(predicate)),
69 }
70 }
71
72 pub fn graph() -> Self {
74 Self::Graph { filter: None }
75 }
76
77 pub fn graph_filtered<F>(predicate: F) -> Self
80 where
81 F: Fn(&u32) -> bool + Send + Sync + 'a,
82 {
83 Self::Graph {
84 filter: Some(Box::new(predicate)),
85 }
86 }
87
88 pub fn inline_filter<F>(predicate: F, adaptive_l: Option<AdaptiveL>) -> Self
95 where
96 F: Fn(&u32) -> bool + Send + Sync + 'a,
97 {
98 struct FnLabelProvider<F>(F);
99
100 impl<F> std::fmt::Debug for FnLabelProvider<F> {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("FnLabelProvider").finish_non_exhaustive()
103 }
104 }
105
106 impl<F> QueryLabelProvider<u32> for FnLabelProvider<F>
107 where
108 F: Fn(&u32) -> bool + Send + Sync,
109 {
110 fn is_match(&self, vec_id: u32) -> bool {
111 (self.0)(&vec_id)
112 }
113 }
114
115 Self::InlineFilter {
116 filter: Box::new(FnLabelProvider(predicate)),
117 adaptive_l,
118 }
119 }
120
121 pub fn diverse_graph(params: DeterminantDiversityParams) -> Self {
124 Self::DiverseGraph {
125 filter: None,
126 params,
127 }
128 }
129
130 pub fn diverse_graph_filtered<F>(predicate: F, params: DeterminantDiversityParams) -> Self
134 where
135 F: Fn(&u32) -> bool + Send + Sync + 'a,
136 {
137 Self::DiverseGraph {
138 filter: Some(Box::new(predicate)),
139 params,
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn flat_no_filter_constructor() {
150 let mode = SearchMode::flat();
151 assert!(matches!(mode, SearchMode::FlatScan { filter: None }));
152 }
153
154 #[test]
155 fn flat_filtered_constructor() {
156 let mode = SearchMode::flat_filtered(|id| *id == 5);
157 match &mode {
158 SearchMode::FlatScan { filter: Some(p) } => {
159 assert!(p(&5));
160 assert!(!p(&4));
161 }
162 _ => panic!("expected FlatScan with filter"),
163 }
164 }
165
166 #[test]
167 fn graph_no_filter_constructor() {
168 let mode = SearchMode::graph();
169 assert!(matches!(mode, SearchMode::Graph { filter: None }));
170 }
171
172 #[test]
173 fn graph_filtered_constructor() {
174 let mode = SearchMode::graph_filtered(|id| *id == 7);
175 match &mode {
176 SearchMode::Graph { filter: Some(p) } => {
177 assert!(p(&7));
178 assert!(!p(&6));
179 }
180 _ => panic!("expected Graph with filter"),
181 }
182 }
183
184 #[test]
185 fn inline_filter_constructor_without_adaptive_l() {
186 let mode = SearchMode::inline_filter(|id| *id == 3, None);
187 match &mode {
188 SearchMode::InlineFilter {
189 filter,
190 adaptive_l: None,
191 } => {
192 assert!(filter.is_match(3));
193 assert!(!filter.is_match(2));
194 }
195 _ => panic!("expected InlineFilter with adaptive_l = None"),
196 }
197 }
198
199 #[test]
200 fn inline_filter_constructor_with_adaptive_l() {
201 let adaptive = AdaptiveL::new(5, 16.0).expect("valid AdaptiveL");
202 let mode = SearchMode::inline_filter(|id| *id == 11, Some(adaptive));
203 match &mode {
204 SearchMode::InlineFilter {
205 adaptive_l: Some(_),
206 ..
207 } => {}
208 _ => panic!("expected InlineFilter with adaptive_l = Some"),
209 }
210 }
211}