Быстрый полнотекстовый поиск в Rails приложениях

    Рано или поздно перед каждым сайтом (будь то социальная сеть, форум, блог или еще какое-то хранилище информации) возникает проблема поиска. Каждый разработчик решает эту проблему по-своему: одни пишут свои решения, другие используют готовые встраиваемые поисковики, третьи используют внешние решения, например сервисы от Google.

    В этой статье мне хотелось бы рассказать о таком средстве поиска, как Ferret, и о том, как просто использовать данное решение в своем Rails-приложении.


    Итак, ferret — это высокопроизводительный поисковой движок для Ruby, основанный на Apache Lucene. Установка ferret сводится к набору одной единственной команды:

    gem install ferret

    Ferret — движок, предназначенный для Ruby в целом, а не только для Rails приложений. Использование его в Rails проще чем можно себе представить. Для интеграции Rails и Ferret нужно установить плагин Acts As Ferret в свой проект. Зайдем в папку проекта и в командной строке наберем следующее:

    ruby script/plugin install svn://projects.jkraemer.net/acts_as_ferret/tags/stable/acts_as_ferret


    Для того чтобы использовать Ferret необходимо сделать следующее: в моделе (предположим, что мы хотим проиндексировать пользователей, а вернее их имя и фамилию), по которой мы хотим производить полнотекстовый поиск добавляем такие строки:

    class Member < ActiveRecord::Base
    acts_as_ferret :fields => [:first_name, :last_name]
    end


    После этого Ferret будет индексировать таблицу модели Member. Теперь рассмотрим, как производится поиск.

    Самый простой способ — использование метода find_id_by_contents. Итак, если мы напишем следующую иструкцию:

    total_results, members = Member.find_id_by_contents("Egor")

    То произойдет следующее:
    • создается папка index/development/member, в которую будут помещаться индексы
    • при каждом добавлении, удалении или обновлении участников индексы будут обновляться (без каких либо дополнительных иструкций со стороны разработчика). Если вам по какой-то причине нужно принудительно обновить все индексы, просто удалите папку index/development/member и индексы будут принудительно обновлены
    • ActAsFerret вызовет функцию Search_Each
    • в результате работы функции возвращается общее число найденных записей, а также первые 10 из них в следующем формате

    members = [
    {:model => "Member", :id => "4", :score => "1.0"},
    {:model => "Member", :id => "21", :score => "0.93211"},
    {:model => "Member", :id => "27", :score => "0.32212"}
    ]

    Вы можете спросить а что же делать, если хочется получить более 10 результатов? Ответ прост: функция find_id_by_contents имеет несколько параметров, которые позволяют управлять ее поведением:
    • offset — указывает позицию, с которой будет возвращены результаты
    • limit — количество возвращаемых записей


    Конечно функцию find_id_by_contents можно использовать с блоком, например так:

    results = []
    total_results = Member.find_id_by_contents("Egor") {|result|
    results.push result
    }


    Или же, если мы хотим получить записи из базы, а не только идентификаторы, то можно сделать так:

    results = []
    total_results = Member.find_id_by_contents("Egor") {|result|
    results.push Member.find(result[:id])
    }


    Для решения последней задачи есть более простой способ: использование функции find_by_contents.

    @results = Member.find_by_contents("Egor")

    Итак, что же делает эта инструкция?
    • вызывает функцию find_id_by_contents для получения идентификаторов
    • достает результаты из базы по найденным идентификаторам
    • возвращает объект класса ActsAsFerret::SearchResults который по сути является небольшой надстройкой над массивом элементов ActiveRecord


    Как вы могли заметить использование Ferret достаточно просто и удобно, в то же время, четко вписывается в идеалогию Ruby и Ruby On Rails, поэтому данный инструмент может стать незаменимым помощником для организации поиска в вашем проекте.

    P.s. для того, чтобы узнать про такие задачи, связанные с Ferret, как:
    • разбивка результатов поиска на страницы (pagination)
    • продвинутый поиск
    • индексация нестандартных полей
    • сотрировка результатов
    • подстветка результатов поиска
    • установка различных весов для различных полей модели


    я рекомендую посмотреть подробнее это руководство (я активно использовал его для написания данной статьи), а также официальный сайт проекта
    Поделиться публикацией

    Комментарии 15

      +2
      спасибо, весьма интересно будет почитать про поиск на рельсах
        +1
        Спасибо, то что нужно!
          +2
          Выглядит отлично, но я не понимаю: учитывает ли он русскую морфологию? Если я ищу 'кисть', он вернет «кисти, кистей»?
            +1
            Русскую морфологию из коробки — нет. Можно прикрутить самому, но это отдельная задача. С помощью словаря Зализняка (Старостина) и Trie или какой ещё подходящей структуры данных делается за день.
            +2
            много раз имел дела с ferret — впечатления исключительно отрицательные
            не все версии нормально работают с русским индексом
            индекс может покрешится на больших нагрузках в многопоточном приложении

            сейчас использую sphinx
            • НЛО прилетело и опубликовало эту надпись здесь
                +3
                В многопоточных приложения ferret действительно добавляет потенциальные баги. Недавно с этим столкнулся:

                На продакшн серверах (разныех) феррет периодически начал отваливаться с такой ошибкой в логе: [FATAL] Segfault без какой-либо конкретики, что наводит на мысль что это где-то в дебрях руби кода происходит.

                Со сфинксом работать гораздо менее приятно, особенно если нужно индексировать не БД, а поля сущностей, но то, что феррет нестабилен — это уже общее, устоявшееся мнение

                  +2
                  Опоздали вы с постом — уже некоторое время ferret в продакшене не используют. Пользуйтесь сфинксом.
                    –1
                    Хм, надо посмотреть что у нас там с этим делом в проекте…
                    +1
                    Кроме Lucene & Sphinx — выбор в зависимости от ситуации.
                      +2
                      Отвлёкся. В общем, кроме Lucene & Sphinx смотреть нечего, из них выбирать по ситуации.
                      +2
                      Сфинк и только сфинкс.
                        +2
                        Круче сфинкса пока ничего нет в свободном доступе
                          0
                          Для rails полно плагинов на sphinx. Какой лучше — сказать не берусь, но thinking_sphinx — помню точно, использовал. Все вполне прилично, по нему есть много мануалов.
                          • НЛО прилетело и опубликовало эту надпись здесь

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое