furry/search/views.py

309 lines
9.0 KiB
Python

"""
Advanced Search Views mit Elasticsearch
"""
from django.shortcuts import render
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.conf import settings
# from haystack.query import SearchQuerySet
# from haystack.forms import SearchForm
# from haystack.inputs import AutoQuery
# from haystack.backends import SQ
import json
def advanced_search(request):
"""Advanced Search mit Elasticsearch"""
# Search parameters
query = request.GET.get('q', '')
category = request.GET.get('category', '')
fursuit_type = request.GET.get('fursuit_type', '')
price_min = request.GET.get('price_min', '')
price_max = request.GET.get('price_max', '')
featured = request.GET.get('featured', '')
sort_by = request.GET.get('sort', '-created_at')
# Initialize search
sqs = SearchQuerySet()
if query:
# Fuzzy search with synonyms
search_query = query
synonyms = settings.SEARCH_SETTINGS.get('SEARCH_SYNONYMS', {})
for synonym_key, synonym_list in synonyms.items():
if query.lower() in synonym_list:
search_query = f"{query} {synonym_key}"
break
sqs = sqs.filter(text=AutoQuery(search_query))
# Apply filters
if category:
sqs = sqs.filter(category=category)
if fursuit_type:
sqs = sqs.filter(fursuit_type=fursuit_type)
if price_min:
sqs = sqs.filter(price__gte=float(price_min))
if price_max:
sqs = sqs.filter(price__lte=float(price_max))
if featured == 'true':
sqs = sqs.filter(featured=True)
# Apply sorting
if sort_by == 'price':
sqs = sqs.order_by('price')
elif sort_by == '-price':
sqs = sqs.order_by('-price')
elif sort_by == 'title':
sqs = sqs.order_by('title')
elif sort_by == '-title':
sqs = sqs.order_by('-title')
else:
sqs = sqs.order_by('-created_at')
# Pagination
page = request.GET.get('page', 1)
paginator = Paginator(sqs, settings.SEARCH_SETTINGS.get('SEARCH_RESULTS_PER_PAGE', 20))
page_obj = paginator.get_page(page)
# Facets
facets = {
'categories': sqs.facet('category').facet_counts()['fields']['category'],
'fursuit_types': sqs.facet('fursuit_type').facet_counts()['fields']['fursuit_type'],
'price_ranges': [
{'range': '0-100', 'count': sqs.filter(price__range=(0, 100)).count()},
{'range': '100-500', 'count': sqs.filter(price__range=(100, 500)).count()},
{'range': '500-1000', 'count': sqs.filter(price__range=(500, 1000)).count()},
{'range': '1000+', 'count': sqs.filter(price__gte=1000).count()},
]
}
# Search analytics
if settings.SEARCH_SETTINGS.get('ENABLE_SEARCH_ANALYTICS', True):
track_search_analytics(request, query, len(sqs))
context = {
'query': query,
'page_obj': page_obj,
'facets': facets,
'filters': {
'category': category,
'fursuit_type': fursuit_type,
'price_min': price_min,
'price_max': price_max,
'featured': featured,
'sort_by': sort_by,
},
'total_results': len(sqs),
'search_highlight': settings.SEARCH_SETTINGS.get('SEARCH_HIGHLIGHT', True),
}
return render(request, 'search/advanced_search.html', context)
def search_suggestions(request):
"""AJAX Search Suggestions"""
query = request.GET.get('q', '')
if len(query) < settings.SEARCH_SETTINGS.get('MIN_SUGGESTION_LENGTH', 2):
return JsonResponse({'suggestions': []})
# Get suggestions from Elasticsearch
sqs = SearchQuerySet()
suggestions = sqs.autocomplete(title_auto=query)[:settings.SEARCH_SETTINGS.get('MAX_SUGGESTIONS', 10)]
suggestion_list = []
for suggestion in suggestions:
suggestion_list.append({
'title': suggestion.title,
'url': suggestion.url,
'type': suggestion.model_name,
})
return JsonResponse({'suggestions': suggestion_list})
def search_analytics(request):
"""Search Analytics Dashboard (Admin only)"""
if not request.user.is_staff:
return JsonResponse({'error': 'Zugriff verweigert'}, status=403)
# Get search statistics
from search.models import SearchAnalytics
analytics = SearchAnalytics.objects.all().order_by('-created_at')[:100]
# Popular searches
popular_searches = SearchAnalytics.objects.values('query').annotate(
count=Count('id')
).order_by('-count')[:10]
# Search trends
search_trends = SearchAnalytics.objects.extra(
select={'date': 'DATE(created_at)'}
).values('date').annotate(
count=Count('id')
).order_by('date')
context = {
'analytics': analytics,
'popular_searches': popular_searches,
'search_trends': search_trends,
}
return render(request, 'search/analytics.html', context)
def search_api(request):
"""REST API für Search"""
query = request.GET.get('q', '')
filters = request.GET.get('filters', '{}')
try:
filters = json.loads(filters)
except json.JSONDecodeError:
filters = {}
# Build search query
sqs = SearchQuerySet()
if query:
sqs = sqs.filter(text=AutoQuery(query))
# Apply filters
for key, value in filters.items():
if value:
sqs = sqs.filter(**{key: value})
# Pagination
page = request.GET.get('page', 1)
per_page = request.GET.get('per_page', 20)
paginator = Paginator(sqs, per_page)
page_obj = paginator.get_page(page)
# Format results
results = []
for result in page_obj:
results.append({
'id': result.pk,
'title': result.title,
'description': result.description,
'url': result.url,
'type': result.model_name,
'score': result.score,
})
return JsonResponse({
'results': results,
'total': len(sqs),
'page': page,
'per_page': per_page,
'pages': paginator.num_pages,
})
def track_search_analytics(request, query, result_count):
"""Track search analytics"""
if not query or not settings.SEARCH_SETTINGS.get('ENABLE_SEARCH_ANALYTICS', True):
return
from search.models import SearchAnalytics
SearchAnalytics.objects.create(
query=query,
user=request.user if request.user.is_authenticated else None,
result_count=result_count,
ip_address=request.META.get('REMOTE_ADDR'),
user_agent=request.META.get('HTTP_USER_AGENT', ''),
)
def search_autocomplete(request):
"""Enhanced Autocomplete mit Fuzzy Search"""
query = request.GET.get('q', '')
if len(query) < 2:
return JsonResponse({'suggestions': []})
# Product suggestions
product_sqs = SearchQuerySet().models(Product).autocomplete(
title_auto=query
)[:5]
# Auction suggestions
auction_sqs = SearchQuerySet().models(Auction).autocomplete(
title_auto=query
)[:5]
suggestions = []
# Add product suggestions
for product in product_sqs:
suggestions.append({
'title': product.title,
'type': 'product',
'category': product.category,
'price': float(product.price),
'url': f'/products/{product.pk}/',
})
# Add auction suggestions
for auction in auction_sqs:
suggestions.append({
'title': auction.title,
'type': 'auction',
'status': auction.status,
'current_bid': float(auction.current_bid) if auction.current_bid else 0,
'url': f'/auction/{auction.pk}/',
})
return JsonResponse({'suggestions': suggestions})
def search_faceted(request):
"""Faceted Search API"""
query = request.GET.get('q', '')
facets = request.GET.getlist('facets')
sqs = SearchQuerySet()
if query:
sqs = sqs.filter(text=AutoQuery(query))
# Apply facets
for facet in facets:
if ':' in facet:
field, value = facet.split(':', 1)
sqs = sqs.filter(**{field: value})
# Get facet counts
facet_counts = {}
for field in ['category', 'fursuit_type', 'status']:
facet_counts[field] = sqs.facet(field).facet_counts()['fields'].get(field, [])
# Price ranges
price_ranges = [
{'range': '0-100', 'count': sqs.filter(price__range=(0, 100)).count()},
{'range': '100-500', 'count': sqs.filter(price__range=(100, 500)).count()},
{'range': '500-1000', 'count': sqs.filter(price__range=(500, 1000)).count()},
{'range': '1000+', 'count': sqs.filter(price__gte=1000).count()},
]
return JsonResponse({
'facets': facet_counts,
'price_ranges': price_ranges,
'total': len(sqs),
})