from django.shortcuts import render from elasticsearch_dsl import Q from datetime import datetime from .documents import DiarioOficialDocument from elasticsearch.exceptions import RequestError def search_diarios(request): q = request.GET.get('q', '') page = int(request.GET.get('page', 1)) size = int(request.GET.get('size', 10)) # Parâmetros de filtro de data date_start = request.GET.get('date_start', '') date_end = request.GET.get('date_end', '') # Tipo de correspondência (exata ou parcial) match_type = request.GET.get('match_type', 'partial') # 'exact' ou 'partial' start = (page - 1) * size end = start + size results = [] total = 0 did_you_mean = None search_suggestions = [] try: if q: # Construir a consulta base search = DiarioOficialDocument.search() # Determinar o tipo de consulta com base no match_type if match_type == 'exact': # Correspondência exata (frase exata) query = Q( 'multi_match', query=q, fields=['content^3', 'tipo.nome^2', 'numero', 'pages.content'], type='phrase' ) else: # Correspondência parcial (qualquer termo) query = Q( 'multi_match', query=q, fields=['content^3', 'tipo.nome^2', 'numero', 'pages.content'], fuzziness='AUTO', operator='or' # Pelo menos um termo deve corresponder ) # Aplicar a consulta principal search = search.query(query) # Aplicar filtros de data se fornecidos date_filters = [] if date_start: try: date_start_obj = datetime.strptime(date_start, '%Y-%m-%d') date_filters.append(Q('range', data={'gte': date_start_obj})) except ValueError: pass # Ignorar datas inválidas if date_end: try: date_end_obj = datetime.strptime(date_end, '%Y-%m-%d') date_filters.append(Q('range', data={'lte': date_end_obj})) except ValueError: pass # Ignorar datas inválidas if date_filters: for date_filter in date_filters: search = search.filter(date_filter) # Configuração do highlighting search = search.highlight('content', fragment_size=150, number_of_fragments=3) search = search.highlight('pages.content', fragment_size=150, number_of_fragments=3) # Paginação total_search = search.count() search = search[start:end] # Executar a pesquisa response = search.execute() total = response.hits.total.value # "Você quis dizer" - sugestão para termos com erros de digitação if total < 3 and q: # Se poucos resultados, sugira correções suggestion_search = DiarioOficialDocument.search() suggestion_search = suggestion_search.suggest( 'phrase_suggestion', q, phrase={ 'field': 'content', 'size': 5, 'highlight': { 'pre_tag': '', 'post_tag': '' } } ) suggestion_result = suggestion_search.execute() # Processe as sugestões if hasattr(suggestion_result, 'suggest') and 'phrase_suggestion' in suggestion_result.suggest: suggestions = suggestion_result.suggest['phrase_suggestion'][0]['options'] if suggestions: for suggestion in suggestions: if suggestion['text'].lower() != q.lower(): did_you_mean = suggestion['text'] break # Gerar sugestões de pesquisa relacionadas if q: # Use a expansão de termos para sugerir pesquisas relacionadas related_search = DiarioOficialDocument.search() related_search = related_search.query( 'more_like_this', fields=['content'], like=q, min_term_freq=1, max_query_terms=12 ) related_search = related_search[:5] # Limite para 5 sugestões try: related_results = related_search.execute() # Extraia termos relevantes dos resultados relacionados for hit in related_results: if hasattr(hit, 'content') and hit.content: # Extraia alguns termos significativos do conteúdo content_terms = hit.content.split()[:10] # Primeiros 10 termos suggestion = ' '.join(content_terms) if suggestion not in search_suggestions and suggestion != q: search_suggestions.append(suggestion) if len(search_suggestions) >= 5: # Limite para 5 sugestões break except: # Ignore erros de sugestões relacionadas pass # Processar resultados for hit in response: # Adicionar destaque highlight = "" if hasattr(hit.meta, 'highlight'): if 'content' in hit.meta.highlight: highlight = "...".join(hit.meta.highlight.content) # Processar páginas com destaque highlighted_pages = [] total_occurrences = 0 if hasattr(hit.meta, 'highlight') and 'pages.content' in hit.meta.highlight: # Calcular o número total de ocorrências for content in hit.meta.highlight['pages.content']: # Contar o número de tags, que representam termos destacados total_occurrences += content.count('') # Processar os destaques por página for i, content in enumerate(hit.meta.highlight['pages.content']): # Encontre a página correspondente page_number = i + 1 # Lógica simplificada, pode precisar de ajuste highlighted_pages.append({ 'number': page_number, 'content': content }) # Combine dados do documento com os destaques result = { 'id': hit.id, 'tipo': hit.tipo.nome if hasattr(hit, 'tipo') and hit.tipo else '', 'numero': hit.numero, 'data': hit.data, 'link': hit.link, 'highlight': highlight, 'highlighted_pages': highlighted_pages, 'occurrences': total_occurrences } results.append(result) except RequestError as e: # Tratar erros de consulta do Elasticsearch error_message = str(e) return render(request, 'diarios/diarios_search.html', { 'error': error_message, 'query': q }) context = { 'query': q, 'date_start': date_start, 'date_end': date_end, 'match_type': match_type, 'results': results, 'total': total, 'page': page, 'size': size, 'total_pages': (total + size - 1) // size if total > 0 else 0, 'did_you_mean': did_you_mean, 'search_suggestions': search_suggestions[:5] # Limite para 5 sugestões } return render(request, 'diarios/diarios_search.html', context) def diario_detail(request, pk): diario = get_object_or_404(Diario, pk=pk) return render(request, 'diarios/diario_detail.html', {'diario': diario})