mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:11:07 -05:00
5.0 KiB
5.0 KiB
Search Integration Design: Location Features
1. Search Index Integration
Schema Modifications
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField
class SearchIndex(models.Model):
# Existing fields
content = SearchVectorField()
# New location fields
location_point = gis_models.PointField(srid=4326, null=True)
location_geohash = models.CharField(max_length=12, null=True, db_index=True)
location_metadata = models.JSONField(
default=dict,
help_text="Address, city, state for text search"
)
class Meta:
indexes = [
GinIndex(fields=['content']),
models.Index(fields=['location_geohash']),
]
Indexing Strategy
-
Spatial Indexing:
- Use PostGIS GiST index on
location_point - Add Geohash index for fast proximity searches
- Use PostGIS GiST index on
-
Text Integration:
SearchIndex.objects.update( content=SearchVector('content') + SearchVector('location_metadata__city', weight='B') + SearchVector('location_metadata__state', weight='C') ) -
Update Triggers:
- Signal handlers on ParkLocation/RideLocation changes
- Daily reindexing task for data consistency
2. "Near Me" Functionality
Query Architecture
sequenceDiagram
participant User
participant Frontend
participant Geocoder
participant SearchService
User->>Frontend: Clicks "Near Me"
Frontend->>Browser: Get geolocation
Browser->>Frontend: Coordinates (lat, lng)
Frontend->>Geocoder: Reverse geocode
Geocoder->>Frontend: Location context
Frontend->>SearchService: { query, location, radius }
SearchService->>Database: Spatial search
Database->>SearchService: Ranked results
SearchService->>Frontend: Results with distances
Ranking Algorithm
def proximity_score(point, user_point, max_distance=100000):
"""Calculate proximity score (0-1)"""
distance = point.distance(user_point)
return max(0, 1 - (distance / max_distance))
def combined_relevance(text_score, proximity_score, weights=[0.7, 0.3]):
return (text_score * weights[0]) + (proximity_score * weights[1])
Geocoding Integration
- Use Nominatim for address → coordinate conversion
- Cache results for 30 days
- Fallback to IP-based location estimation
3. Search Filters
Filter Types
| Filter | Parameters | Example |
|---|---|---|
radius |
lat, lng, km |
?radius=40.123,-75.456,50 |
bounds |
sw_lat,sw_lng,ne_lat,ne_lng |
?bounds=39.8,-77.0,40.2,-75.0 |
region |
state/country |
?region=Ohio |
highway |
exit_number |
?highway=Exit 42 |
Implementation
class LocationFilter(SearchFilter):
def apply(self, queryset, request):
if 'radius' in request.GET:
point, radius = parse_radius(request.GET['radius'])
queryset = queryset.filter(
location_point__dwithin=(point, Distance(km=radius))
if 'bounds' in request.GET:
polygon = parse_bounding_box(request.GET['bounds'])
queryset = queryset.filter(location_point__within=polygon)
return queryset
4. Performance Optimization
Strategies
-
Hybrid Indexing:
- GiST index for spatial queries
- Geohash for quick distance approximations
-
Query Optimization:
EXPLAIN ANALYZE SELECT * FROM search_index WHERE ST_DWithin(location_point, ST_MakePoint(-75.456,40.123), 0.1); -
Caching Layers:
graph LR A[Request] --> B{Geohash Tile?} B -->|Yes| C[Redis Cache] B -->|No| D[Database Query] D --> E[Cache Results] E --> F[Response] C --> F -
Rate Limiting:
- 10 location searches/minute per user
- Tiered limits for authenticated users
5. Frontend Integration
UI Components
-
Location Autocomplete:
<LocationSearch onSelect={(result) => setFilters({...filters, location: result})} /> -
Proximity Toggle:
<Toggle label="Near Me" onChange={(enabled) => { if (enabled) navigator.geolocation.getCurrentPosition(...) }} /> -
Result Distance Indicators:
<SearchResult> <h3>{item.name}</h3> <DistanceBadge km={item.distance} /> </SearchResult>
Map Integration
function updateMapResults(results) {
results.forEach(item => {
if (item.type === 'park') {
createParkMarker(item);
} else if (item.type === 'cluster') {
createClusterMarker(item);
}
});
}
Rollout Plan
- Phase 1: Index integration (2 weeks)
- Phase 2: Backend implementation (3 weeks)
- Phase 3: Frontend components (2 weeks)
- Phase 4: Beta testing (1 week)
- Phase 5: Full rollout
Metrics & Monitoring
- Query latency percentiles
- Cache hit rate
- Accuracy of location results
- Adoption rate of location filters