diff --git a/.jules/bolt.md b/.jules/bolt.md index ba84fca0..bf784525 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -21,3 +21,7 @@ ## 2025-05-30 - O(1) Blockchain Hash Lookup **Learning:** Calculating a blockchain-style hash chain requires finding the previous record's hash. A naive DB scan is O(log N) with indexes, but high-concurrency follow operations can be optimized. **Action:** Implement an in-memory thread-safe cache for the last hash of each grievance to achieve O(1) lookup during follower creation, falling back to DB on cache miss. + +## 2025-07-15 - Fast Bounding Box Pre-filter +**Learning:** Calculating great circle distance (Haversine) for every issue against a target location is computationally expensive (O(N) with heavy math ops like sin, cos, atan2). In high-traffic aggregations, this can become a bottleneck. +**Action:** Use a fast bounding box pre-filter (`get_bounding_box` with a 5% epsilon) to quickly discard issues that are definitely outside the search radius before running the expensive exact haversine distance calculation. diff --git a/backend/spatial_utils.py b/backend/spatial_utils.py index 8af329a3..213bfeaa 100644 --- a/backend/spatial_utils.py +++ b/backend/spatial_utils.py @@ -76,10 +76,24 @@ def find_nearby_issues( """ nearby_issues = [] + # Fast bounding box pre-filter with a 5% epsilon to optimize expensive haversine calculations + epsilon_radius = radius_meters * 1.05 + min_lat, max_lat, min_lon, max_lon = get_bounding_box( + target_lat, target_lon, epsilon_radius + ) + for issue in issues: if issue.latitude is None or issue.longitude is None: continue + # Skip issues outside the bounding box + lon_delta = (issue.longitude - target_lon + 180) % 360 - 180 + lon_half_width = max(target_lon - min_lon, max_lon - target_lon) + if not (min_lat <= issue.latitude <= max_lat and ( + abs(target_lat) >= 89.9 or abs(lon_delta) <= lon_half_width + )): + continue + distance = haversine_distance( target_lat, target_lon, issue.latitude, issue.longitude