# Search Functionality Improvement Plan

## Technical Implementation Details

### 1. Database Optimization
```python
# parks/models.py
from django.contrib.postgres.indexes import GinIndex

class Park(models.Model):
    class Meta:
        indexes = [
            GinIndex(fields=['name', 'description'], 
                   name='search_gin_idx',
                   opclasses=['gin_trgm_ops', 'gin_trgm_ops']),
            Index(fields=['location__address_text'], name='location_addr_idx')
        ]

# search/services.py
from django.db.models import F, Func
from analytics.models import SearchMetric

class SearchEngine:
    @classmethod
    def execute_search(cls, request, filterset_class):
        with timeit() as timer:
            filterset = filterset_class(request.GET, queryset=cls.base_queryset())
            qs = filterset.qs
            results = qs.annotate(
                search_rank=Func(F('name'), F('description'),
                               function='ts_rank')
            ).order_by('-search_rank')
            
        SearchMetric.record(
            query_params=dict(request.GET),
            result_count=qs.count(),
            duration=timer.elapsed
        )
        return results
```

### 2. Architectural Changes
```python
# search/filters.py (simplified explicit filter)
class ParkFilter(SearchableFilterMixin, django_filters.FilterSet):
    search_fields = ['name', 'description', 'location__address_text']
    
    class Meta:
        model = Park
        fields = {
            'ride_count': ['gte', 'lte'],
            'coaster_count': ['gte', 'lte'],
            'average_rating': ['gte', 'lte']
        }

# search/views.py (updated)
class AdaptiveSearchView(TemplateView):
    def get_queryset(self):
        return SearchEngine.base_queryset()
    
    def get_filterset(self):
        return ParkFilter(self.request.GET, queryset=self.get_queryset())
```

### 3. Frontend Enhancements
```javascript
// static/js/search.js
const searchInput = document.getElementById('search-input');
let timeoutId;

searchInput.addEventListener('input', () => {
  clearTimeout(timeoutId);
  timeoutId = setTimeout(() => {
    fetchResults(searchInput.value);
  }, 300);
});

async function fetchResults(query) {
  try {
    const response = await fetch(`/search/?search=${encodeURIComponent(query)}`);
    if (!response.ok) throw new Error(response.statusText);
    const html = await response.text();
    updateResults(html);
  } catch (error) {
    showError(`Search failed: ${error.message}`);
  }
}
```

## Implementation Roadmap

1. Database Migrations
```bash
uv run manage.py makemigrations parks --name add_search_indexes
uv run manage.py migrate
```

2. Service Layer Integration
- Create search/services.py with query instrumentation
- Update all views to use SearchEngine class

3. Frontend Updates
- Add debouncing to search inputs
- Implement error handling UI components
- Add loading spinner component

4. Monitoring Setup
```python
# analytics/models.py
class SearchMetric(models.Model):
    query_params = models.JSONField()
    result_count = models.IntegerField()
    duration = models.FloatField()
    created_at = models.DateTimeField(auto_now_add=True)
```

5. Performance Testing
- Use django-debug-toolbar for query analysis
- Generate load tests with locust.io