1use nodedb_mem::ScopedMemory;
23#[cfg(test)]
24use nodedb_types::BoundingBox;
25use nodedb_types::geometry::Geometry;
26use nodedb_types::geometry_bbox;
27
28use crate::predicates;
29use crate::rtree::RTree;
30
31pub struct SpatialPreFilterResult {
36 pub candidate_ids: Vec<u64>,
38 pub rtree_candidates: usize,
40 pub exact_matches: usize,
42}
43
44pub fn spatial_prefilter(
58 rtree: &RTree,
59 query_geometry: &Geometry,
60 distance_meters: Option<f64>,
61 exact_geometries: &dyn Fn(u64) -> Option<Geometry>,
62 memory: &ScopedMemory,
63) -> SpatialPreFilterResult {
64 let search_bbox = if let Some(dist) = distance_meters {
66 geometry_bbox(query_geometry).expand_meters(dist)
67 } else {
68 geometry_bbox(query_geometry)
69 };
70
71 let rtree_results = rtree.search(&search_bbox);
73 let rtree_candidates = rtree_results.len();
74
75 let bytes = rtree_candidates * std::mem::size_of::<u64>();
77 let _guard = memory.reserve(bytes).ok();
78 let mut candidate_ids = Vec::with_capacity(rtree_candidates);
79 for entry in &rtree_results {
80 if let Some(doc_geom) = exact_geometries(entry.id) {
81 let passes = if let Some(dist) = distance_meters {
82 predicates::st_dwithin(&doc_geom, query_geometry, dist)
83 } else {
84 predicates::st_intersects(&doc_geom, query_geometry)
85 };
86 if passes {
87 candidate_ids.push(entry.id);
88 }
89 }
90 }
91
92 let exact_matches = candidate_ids.len();
93 SpatialPreFilterResult {
94 candidate_ids,
95 rtree_candidates,
96 exact_matches,
97 }
98}
99
100pub fn ids_to_bitmap(candidate_ids: &[u64], max_id: u64) -> Vec<u8> {
108 let num_bytes = ((max_id + 8) / 8) as usize;
109 let mut bitmap = vec![0u8; num_bytes];
110 for &id in candidate_ids {
111 let byte_idx = (id / 8) as usize;
112 let bit_idx = (id % 8) as u32;
113 if byte_idx < bitmap.len() {
114 bitmap[byte_idx] |= 1 << bit_idx;
115 }
116 }
117 bitmap
118}
119
120pub fn bitmap_contains(bitmap: &[u8], id: u64) -> bool {
122 let byte_idx = (id / 8) as usize;
123 let bit_idx = (id % 8) as u32;
124 if byte_idx < bitmap.len() {
125 bitmap[byte_idx] & (1 << bit_idx) != 0
126 } else {
127 false
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::rtree::RTreeEntry;
135 use crate::test_support::test_memory;
136
137 fn make_tree() -> RTree {
138 let mut tree = RTree::new(test_memory());
139 for i in 0..10 {
141 tree.insert(RTreeEntry {
142 id: i,
143 bbox: BoundingBox::from_point(i as f64, i as f64),
144 });
145 }
146 tree
147 }
148
149 #[test]
150 fn prefilter_with_distance() {
151 let tree = make_tree();
152 let query = Geometry::point(5.0, 5.0);
153
154 let get_geom = |id: u64| -> Option<Geometry> {
156 if id < 10 {
157 Some(Geometry::point(id as f64, id as f64))
158 } else {
159 None
160 }
161 };
162
163 let result = spatial_prefilter(&tree, &query, Some(200_000.0), &get_geom, &test_memory());
165 assert!(!result.candidate_ids.is_empty());
166 assert!(result.candidate_ids.contains(&5));
168 }
169
170 #[test]
171 fn prefilter_intersects_no_distance() {
172 let tree = make_tree();
173 let query = Geometry::polygon(vec![vec![
175 [3.0, 3.0],
176 [7.0, 3.0],
177 [7.0, 7.0],
178 [3.0, 7.0],
179 [3.0, 3.0],
180 ]]);
181
182 let get_geom = |id: u64| -> Option<Geometry> {
183 if id < 10 {
184 Some(Geometry::point(id as f64, id as f64))
185 } else {
186 None
187 }
188 };
189
190 let result = spatial_prefilter(&tree, &query, None, &get_geom, &test_memory());
191 assert!(result.candidate_ids.contains(&4));
193 assert!(result.candidate_ids.contains(&5));
194 assert!(result.candidate_ids.contains(&6));
195 assert!(!result.candidate_ids.contains(&0));
197 assert!(!result.candidate_ids.contains(&9));
198 }
199
200 #[test]
201 fn bitmap_roundtrip() {
202 let ids = vec![0, 5, 7, 63, 100];
203 let bitmap = ids_to_bitmap(&ids, 128);
204
205 for &id in &ids {
206 assert!(bitmap_contains(&bitmap, id), "id {id} should be set");
207 }
208 assert!(!bitmap_contains(&bitmap, 1));
209 assert!(!bitmap_contains(&bitmap, 64));
210 }
211
212 #[test]
213 fn empty_bitmap() {
214 let bitmap = ids_to_bitmap(&[], 0);
215 assert!(!bitmap_contains(&bitmap, 0));
216 }
217}