Cat

Django 29.1 Добавляем поиск на сайт

В этом посте мы начнём реализовывать поиск на сайте. Я расскажу о двух способах реализации поиска.

Все статьи

Icon Link

Реклама

Icon Link
Сайт на Django proDream 14 Сентябрь 2023 Просмотров: 1548

Ещё одной немаловажной частью сайта является поиск. У пользователя должна быть возможность найти материал по запрошенному слову или фразе.

В Django есть простой механизм поиска с помощью фильтра contains или icontains. Первый чувствителен к регистру, а второй нет.
Например: PostModel.objects.filter(full_body__icontains='поисковый запрос').
Но такой поиск ищет "в лоб" точно подходящие слова или фразы.

Помимо этого способа, используя СУБД PostgreSQL, открываются расширенные возможности поиска. Такие, как выдача результатов в зависимости от частоты встречаемого запроса в тексте или поиск по триграммному сходству.

Файлов задействовано много, и в процессе будут возникать моменты, когда будем прописывать то, чего ещё нет, дабы не прерываться.

 

Представление и функционал поиска.

Начнём с представления для поиска и его непосредственного функционала.

Откроем файл views.py и создадим новый класс SearchPageView унаследованный от TemplateView.

 

Код класса:

from django.shortcuts import render  
from django.views.generic import TemplateView
from django.core.paginator import Paginator

from blog import models, forms

class SearchPageView(TemplateView):  
    template_name = 'blog/search.html'  

    def get(self, request, *args, **kwargs):  
        form = forms.SearchForm(request.GET)  
        if form.is_valid():  
            query = form.cleaned_data['query']  

            # ... тут код поиска

            paginator = Paginator(results, 10)  
            page_number = request.GET.get('page', 1)  
            results = paginator.get_page(page_number)  

            context = {"query": query,  
                       "results": results}  

            return render(request,  
                          self.template_name,  
                          context)  

        return render(request,  
                      self.template_name,  
                      {"query": request.GET['query']})

 

В теле класса пропишем поле template_name в котором укажем на шаблон страницы поиска.

Далее создадим метод get, принимающий self, request, *args, **kwargs.

В нём создадим переменную form и присвоим ей нашу будущую форму поиска, передав в неё GET-запрос - forms.SearchForm(request.GET)

Затем проверка на валидность формы. Если форма валидна, в переменную query получаем поисковый запрос очищенный от лишних пробелов.

Далее идёт код поиска, его рассмотрим чуть позже.

Затем, поскольку результатов может быть много, мы разбиваем их на страницы используя пагинацию.

Создаём переменную context, передавая в неё поисковый запрос и результаты поиска.

Ниже возвращаем рендер страницы с результатами поиска. А вне блока if прописываем ещё один рендер, возвращающий страницу с поисковым запросом, но без результатов.

Теперь рассмотрим два варианта реализации поиска.

 

Поиск по частоте упоминания в тексте или полнотекстовый поиск

Код:

from django.contrib.postgres.search import SearchVector, SearchRank, SearchQuery

search_vector = SearchVector('title', 'full_body')  
search_query = SearchQuery(query)  
results = (models.PostModel.post_manager.annotate(search=search_vector,  
                                                  rank=SearchRank(search_vector, search_query,  
                                                                  cover_density=True)  
                                                  ).filter(search=search_query)).order_by('-rank')

 

Сначала определяем поля для поиска с помощью SearchVector. В нашем случае, поиск будет производиться по полям 'title' и 'full_body'.

Затем создаем объект SearchQuery, который представляет собой запрос для поиска, передавая в него поисковый запрос.

Затем осуществляется фильтрация и сортировка результатов поиска:

Обращаясь к нашему собственному менеджеру постов, выдающему только опубликованные посты, методом annotate к списку постов мы добавляем новое поле search. Которое содержит поисковые вектора.

SearchRank используется для присвоения ранга каждому результату поиска, с учетом search_vector и search_query. Здесь установлен параметр cover_density=True, чтобы учесть плотность ключевых слов на странице.

В итоге, результаты фильтруются по search_query и сортируются в порядке убывания ранга (-rank).

 

Поиск по сходству или триграммный поиск.

Код:

from django.contrib.postgres.search import TrigramSimilarity

similarity = (TrigramSimilarity('full_body', query) +  
              TrigramSimilarity('title', query))  
results = models.PostModel.post_manager.annotate(  
    similarity=similarity  
).filter(similarity__gt=0.1).order_by('-similarity')

В переменной similarity находится результат применения TrigramSimilarity - Это функция, которая на основе алгоритма трехграмм вычисляет сходство между строками. В нашем случае функция применяется к полям 'full_body' и 'title'.

Далее, как и в предыдущем коде используется собственный менеджер и метод annotate.

Затем происходит фильтрация результатов. Используется фильтрация по значению similarity, где сходство больше 0.1. То есть, в результаты попадут только те посты, у которых сходство с запросом больше 0.1.

В итоге сортируем результаты в порядке схожести с запросом.

Для своего сайта я выбрал второй вариант.

Продолжение в следующем посте.

Автор

    Нет комментариев

    Реклама