L o a d i n g
Поиск с контекстом: Реализация релевантного поиска в Django через SearchRank и динамическую конкатенацию данных Python

В этой статье мы подробно расскажем, как внедрить эффективный и высокоскоростной полнотекстовый поиск в Django-проектах, используя возможности PostgreSQL. На конкретном примере поиска стихов из Библии мы покажем, как настроить поисковую систему, которая позволяет быстро и точно находить текстовые данные. Метод легко адаптируется для других типов контента, таких как статьи, блоги или любой другой текстовый материал.

Почему PostgreSQL?

PostgreSQL предоставляет встроенные инструменты для полнотекстового поиска, такие как SearchVector, SearchQuery и SearchRank. Эти инструменты позволяют:

  • Выполнять быстрый поиск по текстовым полям.
  • Ранжировать результаты по релевантности.
  • Использовать сложные запросы с учетом морфологии языка (например, поиск слов в разных формах).

Шаг 1: Настройка моделей

Для начала создадим модели, которые будут хранить данные. В нашем примере это будет модель Verse (стих), связанная с главами (Chapter) и книгами (Book).

from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

class Chapter(models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='chapters')
    number = models.PositiveIntegerField()

    def __str__(self):
        return f"{self.book.name} {self.number}"

class Verse(models.Model):
    chapter = models.ForeignKey(Chapter, on_delete=models.CASCADE, related_name='verses')
    number = models.PositiveIntegerField()
    text = models.TextField()

    def __str__(self):
        return f"{self.chapter.book.name} {self.chapter.number}:{self.number}"

Шаг 2: Реализация поиска

Используем инструменты PostgreSQL для создания функции поиска. Мы будем использовать следующие классы:

  • SearchVector : Создает вектор для поиска по текстовым полям.
  • SearchQuery : Представляет запрос пользователя.
  • SearchRank : Ранжирует результаты по релевантности.

Метод search_verse

Создадим метод, который будет выполнять поиск стихов по тексту:

from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
from django.db.models import F, Value
from django.db.models.functions import Concat

def search_verse(query: str):
    if not query:
        return Verse.objects.none()  # Если запрос пустой — возвращаем пустой QuerySet

    search_query = SearchQuery(query)
    verses = (
        Verse.objects.annotate(
            search=SearchVector('text'),  # Добавляем SearchVector
            rank=SearchRank(SearchVector('text'), search_query),
            full_reference=Concat(
                'chapter__book__name', Value(' '), 
                'chapter__number', Value(':'), 
                'number',
                output_field=models.CharField()
            )
        )
        .filter(search=search_query)  # Поиск через SearchQuery
        .order_by('-rank')  # Сортируем по релевантности
    )
    return verses

Что делает этот код?

  1. Проверка запроса: Если запрос пустой, возвращается пустой QuerySet.
  2. Создание вектора поиска: Используется SearchVector для анализа текстового поля text.
  3. Ранжирование результатов: SearchRank оценивает релевантность каждого стиха.
  4. Формирование ссылки: Поле full_reference объединяет название книги, номер главы и номер стиха в удобном формате.
  5. Фильтрация и сортировка: Результаты фильтруются по запросу и сортируются по релевантности.

Шаг 3: Пагинация результатов

Чтобы улучшить пользовательский опыт, добавим пагинацию. Для этого используем класс Paginator из Django.

Изменение представления

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views import View
from django.shortcuts import render

class SimfonySearchView(View):
    def get(self, request, *args, **kwargs):
        query = request.GET.get('query', '').strip()
        page_number = request.GET.get('page', 1)

        # SEO настройки
        request.page_title = 'Поиск по Библии'
        request.page_description = 'Найдите нужное слово в Библии'

        # Поиск стихов
        verses_result = search_verse(query) if query else []

        # Пагинация
        paginator = Paginator(verses_result, 30)  # 30 результатов на страницу
        try:
            verses_page = paginator.page(page_number)
        except PageNotAnInteger:
            verses_page = paginator.page(1)
        except EmptyPage:
            verses_page = paginator.page(paginator.num_pages)

        return render(request, "bible/search_results.html", {
            "verses": verses_page,
            "query": query,
            "paginator": paginator
        })

Шаг 4: Отображение результатов в шаблоне

Создадим HTML-шаблон для отображения результатов поиска и навигации по страницам.

{% if verses %}
    <h2>Результаты поиска:</h2>
    <ul>
        {% for verse in verses %}
            <li>
                <div class="verse">
                    <p><strong>{{ verse.full_reference }}</strong></p>
                    <p>{{ verse.text }}</p>
                </div>
            </li>
        {% endfor %}
    </ul>

    <!-- Навигация по страницам -->
    <div class="pagination">
        <span class="step-links">
            {% if verses.has_previous %}
                <a href="?query={{ query }}&page=1">Первая</a>
                <a href="?query={{ query }}&page={{ verses.previous_page_number }}">Назад</a>
            {% endif %}

            <span class="current">
                Страница {{ verses.number }} из {{ verses.paginator.num_pages }}
            </span>

            {% if verses.has_next %}
                <a href="?query={{ query }}&page={{ verses.next_page_number }}">Вперед</a>
                <a href="?query={{ query }}&page={{ verses.paginator.num_pages }}">Последняя</a>
            {% endif %}
        </span>
    </div>
{% else %}
    <p>Ничего не найдено.</p>
{% endif %}

Шаг 5: Настройка базы данных

Убедитесь, что ваш проект использует PostgreSQL в качестве базы данных. В файле settings.py добавьте:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'your_database_name',
        'USER': 'your_database_user',
        'PASSWORD': 'your_database_password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

Также установите необходимые зависимости:

pip install psycopg2-binary

Заключение

Мы рассмотрели, как реализовать полнотекстовый поиск в Django с использованием PostgreSQL. Этот подход позволяет:

  • Выполнять быстрый и релевантный поиск.
  • Разбивать результаты на страницы для удобства пользователя.
  • Формировать читаемые ссылки на найденные элементы.

Вы можете адаптировать этот код для любых текстовых данных в вашем проекте. Например, его можно использовать для поиска статей, продуктов или комментариев. Удачи в разработке!

Написать комментарий

Вы можете оставить комментарий автору статьи Обязательные поля помечены *