Установка и интеграция solr с django под Ubuntu 12.04

  • Tutorial
image image

Введение


Как известно, на многих сайтах/веб-приложениях необходимо тем или иным образом реализовать поиск. Все хотят быстрый и качественный поиск. Разработчики помимо всего прочего хотят, чтобы поисковик был прост в установке и использовании. Так как речь идет о django, то перед нами встает ряд ограничений в реализации поиска (при условии, что в сутках 24 часа и дедлайны никто не отменял). Предлагаю вашему вниманию небольшой туториал о том, как поставить и максимально безболезненно интегрировать в django проект такой мощный поисковик, как apache solr. Всех заинтересованных прошу под кат.



Наши грабли


В прошлом у нас был не очень приятный опыт работы с django-sphinx. Сам sphinx отличный поисковик, django-sphinx тоже замечательная библиотека, но после django 1.3 приходится немного танцевать с бубном, чтобы завести весь этот стек. Но даже после того как все это настроено и работает остается ощущение, что все это лежит находится где-то там, отдельно от проекта, со своими индексами по крону и конфигами, не связанными с моделями на проекте. Да, там есть автогенератор конфигов, но завести его под 1.4+ не удалось, да и после каждой генерации пришлось бы править конфиги самого поисковика (searchd). В целом поиск django-sphinx работает, причем быстро и качественно, но поддерживать это не очень удобно, как и интегрировать в проект.

Как мы нашли для себя иголку в haystack


Вот настал черед нового проекта. И снова встал вопрос выбора поисковика. От django-sphinx мы сразу же отказались и стали искать альтернативное решение. Первая библиотека, на которую мы обратили внимание, это django-haystack, что неудивительно, т.к. это самая популярная библиотека для интеграции поисковиков в django. Посмотрев api, на ней и остановились. Нам обещали полную интеграцию с любым из предложенных поисковиков: elasticsearch, solr, whoosh, xapian. После беглого осмотра были приняты следующие решения:

  1. Whoosh исключили из-за отсутствия кучи мелких фич, некоторые из которых были необходимы для проекта. Да и поговаривают, что он по скорости проигрывает остальным вариантам.
  2. Xapian исключили после неудачной попытки интеграции стороннего бекенда.
  3. Осталось 2 варианта — elasticsearch и solr. Оба на java, оба на lucene, фичи практически идентичны. “Подбросив монетку” было решено использовать solr.



Больше граблей


Сразу скажу, что в данной статье описана в основном работа именно с solr, а не с haystack. Информацию по haystack можно найти в официальных доках, она хорошо изложена и дублировать ее здесь я не вижу смысла.

Библиотека выбрана, поисковик выбран. Пора приступать к работе. Первой же ошибкой была установка из официального репа ubuntu 12.04. В официальном репе версия solr 1.4, хотя документация haystack рекомендует версию 3.5+ во избежание оказий. Решили ставить сами. После серии проб и ошибок пришли к решению, которое отлично сработало как на локалках разработчиков, так и на тестовом и боевом серверах. Вот примерная последовательность действий:

  1. Ставим системные пакеты jre и jetty:
    apt-get install openjdk-7-jre-headless jetty
    
  2. Python либы:
    pip install pysolr django-haystack
    
  3. Немного “темной магии” для глобальной установки самого solr’а:
    #!/bin/sh
    #variables
    LIB_DIRECTORY="/opt/solr"
    CONF_DIRECTORY="/etc/solr"
    OLD_CONF_DIRECTORY="$LIB_DIRECTORY/example/solr/conf"
    
    #check if already installed
    if ( [ -d $LIB_DIRECTORY ] && [ -d $CONF_DIRECTORY ] ); then
        echo "solr is already installed"
        exit
    fi
    
    #install if not
    #download and unpack
    wget http://archive.apache.org/dist/lucene/solr/3.6.2/apache-solr-3.6.2.tgz
    tar -xvzf apache-solr-3.6.2.tgz
    
    #install
    mv apache-solr-3.6.2 $LIB_DIRECTORY
    mv $OLD_CONF_DIRECTORY $CONF_DIRECTORY
    ln -s $CONF_DIRECTORY $OLD_CONF_DIRECTORY
    
    #cleanup
    rm apache-solr-3.6.2.tgz
    
    echo "solr installed"
    



Конечно если вы не хотите ставить solr так “глобально”, то можно немного подкрутить скрипт и ставить в любую другую папку — solr работает сразу после разархивации без дополнительных танцев, просто мы хотели видеть конфиги в привычных местах и чтобы очередное “стороннее” приложение было там же, где и остальные.

В общем уже можно запускать и все будет работать:
cd /opt/solr/example/ && java -jar start.jar


Но не можем же мы так все оставить. Как же конфиги, переиндексация и нормальный запуск? Давайте по порядку.

Конфиги


В данном контексте нас интересует 2 конфига:

  1. /etc/solr/schema.xml — сама схема данных и прочая информация для индексации и поиска, связанная непосредственно с нашими данными. Этот конфиг необходимо обновлять каждый раз, когда вы меняете индексы у себя в проекте.
  2. /etc/solr/solrconfig.xml — настройки самого solr’а, т.е. модули, handler’ы и прочие настройки, которые напрямую не касаются данных. Этот конфиг вам может пригодиться, если вам нужны дополнительные фичи solr’а.


После обновления любого из конфигов необходимо ребутать solr, чтобы изменения вступили в силу. (На самом деле необязательно, можно ограничиться RELOAD, что будет явно правильней в боевых условиях. см wiki.apache.org/solr/CoreAdmin)
Для генерации schema.xml haystack поставляет замечательную команду build_solr_schema. Но у нее есть небольшой минус — при ее использовании нужно помнить куда надо класть конфиг, т.к. она либо принтит конфиг в консоль, либо в файл, если он указан. Можно немного поправить дело, если сделать собственную команду, полную копию build_solr_schema, просто указать дефолтное значение имени файла (в нашем случае это /etc/solr/schema.xml). Таким образом мы немного упростили жизнь разработчикам, ведь теперь достаточно только запустить эту команду и конфиг будет обновлен.

Переиндексация


В нашем случае есть 2 варианта как поддерживать актуальность индексов:

  1. “Классический вариант” — полная переиндексация раз в N минут. Единственный плюс такого подхода, который приходит в голову, это его простота. Минусов немного больше: при большой базе это долго, между переиндексациями данные могут стать неактуальными.
  2. “Атомарная переиндексация” — переиндексация каждого объекта по сигналам post_save, post_delete и т.д. Главным плюсом очевидно будет скорость независимо от размера бд. Немаловажно, что при таком подходе весь контроль переиндексации находится “на глазах” в коде проекта, а не в каком-то там кроне или в лучшем случае таске celery. Но и тут не без минусов: неактуальность данных при неправильной реализации и оверхед при вызове сигналов с переиндексацией. Последний устраняется вынесением в асинхронный таск (в нашем случае celery).


Мы выбрали второй вариант (никто не запрещает выбрать оба сразу) и для реализации воспользовались простым миксингом для моделей и парой тасков:

Миксин

class RefreshIndexMixin(object):
    #index - соответствующий индекс модели, например PostIndex или CarIndex
    def update_index(self, index):
        current_app.send_task('project.tasks.update_index', args=[self, index])

    def remove_index(self, index):
        current_app.send_task('project.tasks.remove_index', args=[self, index])


В модели соответственно достаточно вызвать update_index или remove_index в соответствующем сигнале.

Таски

from celery import shared_task


@shared_task
def update_index(obj, index):
    index().update_object(obj)


@shared_task
def remove_index(obj, index):
    index().remove_object(obj)


Нормальный запуск.


Мы повсеместно используем supervisor, поэтому в нашем случае долго думать не пришлось. Вот пример конфига для локалки/тестового сервера:

[program:solr]
command=java -jar start.jar
directory=/opt/solr/example/
stderr_logfile=/var/log/solr.error.log
stdout_logfile=/var/log/solr.log
autorestart=true

Почему именно для локалки/тестового? Потому что solr “из коробки” идет с собственной админкой, где можно посмотреть индексы, настройки и т.д., а так же производить всякие манипуляции с этими данными. На боевом сервере вы скорее всего захотите отказаться от такой фичи, поэтому вот команда запуска только на localhost’е на дефолтном порте:

command=java -Djetty.host=127.0.0.1 -Djetty.port=8983 -jar start.jar


Итог


Что мы имеем в сухом остатке:

  • мощный поисковый движок, который отлично интегрирован с django проектом
  • атомарное индексирование
  • легкое изменение любых настроек в рамках проекта
  • целый ворох дополнительных фич (faceting, more like this и т.д.), которые действительно работают


За то время, что мы используем эту связку, она показала себя отличнейшим образом со всех сторон. Пользователи и заказчик довольны быстрым и адекватным поиском на сайте. Для разработчиков наличие такого java-монстра практически незаметно, т.к. им нужны только индексы, пара команд manage.py и перезагрузка solr’а через supervisor.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 11

    0
    У вас не возникло проблемы с индексом связанных сущностей? Пример: есть Клиент, у него есть Менеджер, необходимо искать полнотекстовым поиском клиентов по имени менеджера, при изменении имени менеджера в индексе клиентов данные уже неактуальны.
      0
      Нет, у нас таких проблем пока не возникало. Тут все зависит от структуры самих индексов и переиндексации. Можно при обновлении объекта обновлять все индексы, где он участвует. Но это уже все специфика каждого конкретного проекта.
      0
      На правах оффтопа:

      Может вы знаете, почему могут возникать такие проблемы?
      github.com/rtfd/readthedocs.org/issues/827

      До разработчиков не достучаться, а мне нужно настроить поиск для локального сервера документации. В Django не силён, куда копать уже не знаю. Подразумеваю, что проблема с моделью для построения индекса, но не уверен, так как по коду вроде всё правильно.

        0
        Если честно, то я RTD не пользовался никогда, поэтому сложно сказать.

        Судя по виду индекса и сообщению в ошибке проблемы возникают из-за того, что в индексе прописано поле slug related объекта version, а самого привязанного объекта не существует:

        version = CharField(model_attr='version__slug', faceted=True)
        

        Проверьте, что у всех объектов ImportedFile есть связанные объекты version. Через shell может выглядеть так:

        files = ImportFiles.objects.filter(version__isnull=True)
        
          0
          Спасибо за совет! Обязательно проверю.
            0
            Прошу прощения, не успел поправить опечатку: не ImportFiles, а ImportFile.

            P.S.: предварительно импортировать эту модель надо, конечно же
        0
        После обновления любого из конфигов необходимо ребутать solr, чтобы изменения вступили в силу.

        Это в общем случае неверно.
        В solr естъ такие сущности как Cores — wiki.apache.org/solr/CoreAdmin — если изменения в конфигах обратно совместимы ядро можно перегрузитъ командой RELOAD.
        Если же изменения несовместимы можно создать новое ядро через CREATE и «свапнутъ» его с текущим через SWAP.

          0
          Действительно, был не прав.
          Спасибо за исправление, сейчас добавлю в пост.
          0
          У django-sphinx есть множество форков, которые работают с dj1.4+. Например этот.
          Меня больше смущает другой вопрос: Почему вы не используете к примеру Real-time indexes?
          В IT не принято подкидывать монетку при выборе стека. Нужно как минимум аргументировать и взвесить все варианты разумно.
          1. В данной статье нет замеров производительности, нет реальных доводов, кроме тех, что официальная батарейка не работает.
          2. Темная магия — это плохо. Зачем когда есть CheckInstall ну и dpkg build?
            0
            После django-sphinx захотелось чего-нибудь с бОльшим community.
            Чем наш вариант отличается от Real-time indexes? В haystack это реализовано ровно так, как у нас, только мы еще в асинхрон это вынесли.
            Это был не выбор стека, а выбор одного компонента. Когда у вас ограниченное количество времени на проект, то кроме монетки ничего не остается.
            1. Замеров нет, но я не понимаю как они связаны с темой статьи. Не понял о какой вы батарейке. Генератор конфигов sphinx'а в django-sphinx или backend для xapian?
            2. Согласен, что плохо. Есть масса вариантов лучше. Но в данном контексте они нам не нужны. Чтобы собирать всякие deb пакеты надо потратить больше времени, чем на этот простой скрипт, который целиком и полностью выполняет задачу. Плюсы «нормальной установки» в данной ситуации не ясны.
            0
            О! Может вы мне ответите на этот вопрос о готовке ядер Solr-а? toster.ru/q/101767

            С момента публикации вопроса ни одного ответа. Но за это время мне посоветовали перейти на ElasticSearch у которого конфиги на yaml/json, бинарник не требующий jre и другие плюшки.

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