309 lines
9.0 KiB
Python
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),
|
|
}) |