Files
thrillwiki_django_no_react/memory-bank/documentation/search_integration_design.md
2025-08-15 12:24:20 -04:00

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

  1. Spatial Indexing:

    • Use PostGIS GiST index on location_point
    • Add Geohash index for fast proximity searches
  2. Text Integration:

    SearchIndex.objects.update(
        content=SearchVector('content') + 
        SearchVector('location_metadata__city', weight='B') +
        SearchVector('location_metadata__state', weight='C')
    )
    
  3. 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

  1. Hybrid Indexing:

    • GiST index for spatial queries
    • Geohash for quick distance approximations
  2. Query Optimization:

    EXPLAIN ANALYZE SELECT * FROM search_index 
    WHERE ST_DWithin(location_point, ST_MakePoint(-75.456,40.123), 0.1);
    
  3. 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
    
  4. Rate Limiting:

    • 10 location searches/minute per user
    • Tiered limits for authenticated users

5. Frontend Integration

UI Components

  1. Location Autocomplete:

    <LocationSearch 
      onSelect={(result) => setFilters({...filters, location: result})}
    />
    
  2. Proximity Toggle:

    <Toggle 
      label="Near Me"
      onChange={(enabled) => {
        if (enabled) navigator.geolocation.getCurrentPosition(...)
      }}
    />
    
  3. 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

  1. Phase 1: Index integration (2 weeks)
  2. Phase 2: Backend implementation (3 weeks)
  3. Phase 3: Frontend components (2 weeks)
  4. Phase 4: Beta testing (1 week)
  5. Phase 5: Full rollout

Metrics & Monitoring

  • Query latency percentiles
  • Cache hit rate
  • Accuracy of location results
  • Adoption rate of location filters