Поиск через sphinx в django 1.6 admin

    Задача: реализовать полнотекстовый поиск в админке django.
    Пример модели, по которой будем делать поиск:
    class Movie(models.Model):
        title_en = models.CharField(max_length=255, null=True)
        title_ru = models.CharField(max_length=255, null=True)
        year = models.PositiveSmallIntegerField(null=True)
    


    Установка и настройка sphinx


    Ставим sphinx через любой пакетный менеджер, у меня под рукой только FreeBSD, поэтому я ставлю из портов.
    cd /usr/ports/textproc/sphinxsearch
    make install clean
    

    В статье используется версия 0.9.9, с ней точно будет работать пакет django-sphinx, на других версиях не проверял.
    Создаем конфиг /usr/local/etc/sphinx.conf
    Скрытый текст
    # Источник
    source movies_src
    {
        type = pgsql # или mysql
        sql_host = 127.0.0.1
        sql_user = sphinx
        sql_pass = sphinxpass
        sql_db = dbname
        sql_port = 5432
        sql_query = select id, title_en, title_ru, year from movies_movie # запрос, который выбирает данные для индекса
        sql_attr_uint = year
    }
    
    # Настройки индекса
    index movies
    {
        source = movies_src
        path = /var/data/search/movies
        docinfo = extern
        charset_type = utf-8
        morphology = stem_enru # подключаем русскую и английскую морфологию
    }
    
    # Настройки демона
    searchd
    {
        listen = 9312
        log = /var/log/searchd.log
        query_log = /var/log/searchd.query.log
        read_timeout = 3
        client_timeout = 10
        max_children = 15
        pid_file = /var/run/sphinxsearch/searchd.pid
        max_matches = 1000
        seamless_rotate = 1
        preopen_indexes = 1
        unlink_old = 1
        mva_updates_pool = 1M
        max_packet_size = 8M
    }
    


    Создаем лог-файлы и директории из конфига, делаем им chown _sphinx. Если вы тоже используете postgres не забудьте добавить соответствующую строчку в pg_hba.conf, чтобы сфинкс смог подключиться.
    Выдаем юзеру права на чтение из нужной нам таблицы. Привожу пример для постгреса, чтобы не гуглить лишний раз:
    GRANT CONNECT ON DATABASE mydb TO sphinx;
    GRANT USAGE ON SCHEMA public TO sphinx;
    GRANT SELECT ON movies_movie TO sphinx;
    

    Запускаем индексатор и ставим его в крон
    /usr/local/bin/indexer --config /usr/local/etc/sphinx.conf  --rotate --all >/dev/null 2>&1
    

    Запускаем sphinx
    /usr/local/etc/rc.d/sphinxsearch start
    

    Интеграция с django


    Ставим пакет django-sphinx
    pip install django-sphinx
    

    Добавляем 'djangosphinx' в INSTALLED_APPS
    Прописываем настройки в settings.py
    SPHINX_API_VERSION = 0x116 # для sphinx 0.9.9+
    SPHINX_PORT = 9312
    SPHINX_SERVER = '127.0.0.1'
    

    Добавляем в модель Sphinx-менеджер
    class Movie(models.Model):
        search = SphinxSearch(
            index='movies',
            weights={
                'title_en': 100,
                'title_ru': 100,
            }
        )
    

    Проверяем что полнотекстовый поиск работает.
    >>> Movie.search.query(u'зеленый').order_by('year')[0]
    <Movie: The Green Mile (1999) - Зеленая миля>
    

    Поиск через sphinx в админке


    У django-sphinx как бы есть свой класс SphinxModelAdmin, который позволяет использовать сфинкс в штатном поиске админки.
    Но вот что пишет сам автор про эту фичу:
    • Only shows your max sphinx results (defaults to 1000)
    • Filters currently don't work.
    • This is a huge hack, so it may or may not continue working when Django updates.

    Такое нам не подходит, так что придется допиливать.
    В django 1.6 у класса ModelAdmin появился метод get_search_results, позволяющий переопределить встроенный механизм поиска.
    Документация: docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_search_results
    Его и будем использовать.
    У меня получился такой класс:
    class SphinxModelAdmin(admin.ModelAdmin):
        def get_search_results(self, request, queryset, search_term):
            if search_term:
                sphinx_queryset = self.model.search.query(search_term)
                doc_ids = [doc.pk for doc in sphinx_queryset]
                queryset = queryset.filter(pk__in=doc_ids)
                return queryset, True
            else:
                return super(SphinxModelAdmin, self).get_search_results(
                    request, queryset, search_term
                )
    

    Этот способ не теряет фильтры и сортировки админки, мы просто сужаем область поиска до документов, найденных в сфинксе.

    Использовать так:
    class MovieAdmin(SphinxModelAdmin):
        pass
    

    Проверяем что поиск правильно работает.

    Внешний вид — django-grappelli

    Минусы данного решения:
    Django 1.6 на момент написания топика находится в бете.

    Ссылочки:
    sphinxsearch.com
    github.com/dcramer/django-sphinx
    vostryakov.ru/blog/28-sphinx-11-django-postgresql
    docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_search_results
    Share post

    Comments 12

      +3
      Я вас умоляю, не надо использовать django-sphinx. Он сильно-сильно legacy, untested и buggy. Я уже писал об этом как-то. А, поскольку, нормального решения под Python-приложения не было, я разработал Sphinxit. Попробуйте.

      GitHub: github.com/semirook/sphinxit
      Слайды с доклада по теме: speakerdeck.com/semirook/poisk-sphinx
        0
        Нужен django-sphinxit :)
          0
          Будет. Хотя пока непонятно в каком виде. Вы хотите, чтобы конфиг автоматом создавался? И чтобы ротация индексов на post_save сигналах отрабатывала? Sphinxit хоть сейчас можно использовать в Django, как и в любом другом фреймворке (в том-то и прелесть). В общем, пишите хотелки мне на GitHub.
            0
            Ну например, чтобы поиск в сфинксе возвращал инстансы джанговых моделей, как это сделано в django-sphinx.
              0
              Чтобы работало с QuerySet на основе моделей, видимо.
            0
            Здорово, если появится пакет для встраивания сфинкса в джангу.
            Но похоже, с того времени sphinxit не обновлялся?
            Интеграции с джанговскими моделями нет?
              0
              Есть github.com/Yuego/django-sphinx
              Но у него хватает недостатков. Всё никак не соберусь его переписать под sphinxit.
            0
            А что за проект, если не секрет?
              +1
              Очередной проект на выходные, у которого никогда не будет релиза.
            0
            а что за скин у админки?
              +1
              Это django-grappelli. В статье, кстати, об этом написано ;)

            Only users with full accounts can post comments. Log in, please.