В этой статье мы подробно расскажем, как внедрить эффективный и высокоскоростной полнотекстовый поиск в 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
Что делает этот код?
- Проверка запроса: Если запрос пустой, возвращается пустой QuerySet.
- Создание вектора поиска: Используется
SearchVector
для анализа текстового поляtext
. - Ранжирование результатов:
SearchRank
оценивает релевантность каждого стиха. - Формирование ссылки: Поле
full_reference
объединяет название книги, номер главы и номер стиха в удобном формате. - Фильтрация и сортировка: Результаты фильтруются по запросу и сортируются по релевантности.
Шаг 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. Этот подход позволяет:
- Выполнять быстрый и релевантный поиск.
- Разбивать результаты на страницы для удобства пользователя.
- Формировать читаемые ссылки на найденные элементы.
Вы можете адаптировать этот код для любых текстовых данных в вашем проекте. Например, его можно использовать для поиска статей, продуктов или комментариев. Удачи в разработке!
Написать комментарий