Допиливаем djapian. Встречайте django-xapian!
Обновление от 24 февраля 2009 года: проект я прекратил развивать, т.к. получил полный доступ к проекту Djapian. Все нижеперчисленные идеи уже туда портированы, успели проэволюционировать и дополниться другими. Так что смело пользуйтись самим Djapian.
Проблема выбора реализации для индексирования/поиска на сайте, поднятая мною недавно, пришла к своему разрешению. Как я уже говорил, остановился я на djapian. Но в том виде, в котором было данное приложение, его использовать было практически невозможно.
Решил немножко подкрутить там гайки и обтесать под свои нужды. Но поскольку изменений накопилось много, то я завел отдельный проект для моей ветки. Назвал я его просто и без затей django-xapian. И конечно же выкладываю в открытый доступ свои наработки. Вот домашняя страничка проекта http://webnewage.org/projects/p/django-xapian/
Основные изменения по сравнению с оригиналом:
- Код подтянут до транка джанги
- Исправлен NameError, который ставил крест на использовании оригинала:)
- Объекты обрабатываются только при срабатывании пользовательского триггера, а не просто по событию
- Перенесен сервис индексировани из отдельно скрипта в команду manage.py
- Оптимизирован процесс индексирования - убраны лишние действия, которые выполнял исходный скрипт, не отфильтровывая неактуальные уже изменения в объектах.
- Убрана махинация с неймспейсами
- Добавлена возможность индексирования не только непосредственно полей данных модели и но результатов выполнения методов, либо связанных объектов
- мелкие косметические изменения, добавляющий более прозрачности в работу приложения
Теперь короткий рассказ, как данным приложением пользоваться.
Для начала необходимо установить django-xapian
, взяв из репозитория исходники и сделать так, чтобы dxapian директория была доступна в PYTHON_PATH
( я обычно делаю символическую ссылку в /lib/python/site-packages/
). Конечно должен быть установлен и сам xapian, с биндиногом для питона.
Теперь непосредственно будем интегрировать. Возьмем абстрактную модель статьи:
class Article( models.Model ):
title = models.CharField( max_length = 255 )
text = models.TextField()
published = models.BooleanField( defautl = True )
Совсем простая, но с флагом публикации, потом покажу зачем он нам в примере понадобиться.
Теперь важный этап создание индексера для этой модели:
from dxapian import XapianIndexer
indexer = XapianIndexer( model = Article,
fields = ( "text", ),
attributes = { "title" : "title", },
trigger = ( lambda article: article.published )
)
Первым параметром передаем модель, которую хотим индексировать, в нашем случае Article. Второй параметр это последовательность полей модели, которые должны быть индексированы. Параметр attributes
- это тоже поля для индексирования, но те которые будут доступны через префиксы в запросе поиска, в формате префикс : поле
. В качестве поля для индексирования могут выступать и методы(конечно их результат, но указывает в перечислении именно имя метода), и “вложенные” объекты, например если бы у наши статьи были бы разбиты на категории то можно было бы добавить в attributes
"category" : "catagory.title"
. Последний параметр это триггер, который сообщает должен ли быть проиндексирован объект, в нашем случае он проверяет флаг “опубликованности” статьи.
Для того чтобы индексер точно знал куда ему сохранять индекс, необходимо в settings.py
указать значение DXAPIAN_BASE
, в котором должен быть абсолютный путь до директории хранения. Далее индексер сам разбирается куда сохранять данные (можно кастомизировать, передав параметр path
при создании индексера).
Поскольку процесс индексрования может быть достаточно ресурсоемким, то его проводить не при добавлении/изменении объекта( и прохождении триггера), а по расписанию. Отвечает за сам процесс индексирования команда xapianindex
. Чтобы узнать о ней поподробней напишите в командной строке:
> ./manage.py help xapianindex
Запуск это скрипта нужно прописать в планировщике (например cron), ну а интервал подобрать исходя из особенностей вашего проекта.
Всё. С самим индексированием покончено. Теперь приступим к реализации конечной цели - поиска. Тут всё тоже просто. Напишем вьюху для поиска:
class SearchForm( forms.Form ):
query = forms.CharField()
def search( request )
form = SearchForm( "query" in request.GET and request.GET or None )
context = { "form" : form }
if form.is_valid():
query = form.cleaned_data[ "query" ]
resultset = Article.indexer.search( query = query )
context.update( { "resultset" : resultset } )
return render_to_response( "search.html", context )
Итак, когда мы создавали индексер, он любезно добавил свой объект к модели как атрибут indexer
. Через него и осуществляется поиск. Результатом поиска является объект типа ResultSet
, в подробности вдаваться не буду, просто скажу, что он инкапсулирует в себе итерацию по результату поиска (что и из названия понятно:)).
Теперь выведем результат в шаблоне, где будет понятно в каком виде поиск возвращает “строки” результата:
{% for result in resultset %}
<li>{{result.score}}% - {{result.instance.title}}</li>
{% endfor %}
Видно, что score
- это показатель релевантности, а result.instance
это сам объект модели, который соответствует результату. Он кешируется при первом доступе к атрибуту, поэтому его можно “дергать” часто:)
Всё. Поиск готов. Теперь его можно испытать. Конечно в индексе что-то уже должно быть. А поисковый запрос может выглядеть например так:
title:блог OR джанго
За более подробной информацией по синтаксису запросов отправляю в документацию по xapian, там всё очень подробно расписано.
Я конечно не все особенности использования django-xapian осветил, но основные моменты постарался.
Работа по доработке продолжается. Есть несколько идей которые хотелось бы ещё реализовать.
Пожелания, замечания и всё что связано с django-xapian прошу писать сюда, в комментарии. Помогу всем кто решиться попробовать его в действии:)
P.S. Поиск, который недавно появился на блоге, сделан именно с использованием данного приложения.