Создание ознакомительного поискового движка на Sphinx + php

  • Tutorial

Предыстория


Раньше для поиска по сайту мы использовали обычный fulltext поиск. Но в определенный момент он перестал нас устраивать и мы решили опробовать альтернативную технологию поиска: Sphinx. К сожалению, у сфинкса совсем нет русской документации, поэтому эта статья — аналог статьи Build a custom search engine with PHP, только на русском языке и для моего локального окружения (windows 7, mysql/php)
Статья состоит из 4 частей:
  1. Краткий рассказ про подготовку базы для поиска.
  2. Рассказ про первоначальную установку и настройку сфинкса
  3. Индексирование базы и тестовый поиск из командной строки
  4. Тестовый поиск из php


1. Подготовим базу данных для поиска


Перед началом использования сфинкса лучше сразу подготовить базу данных для более удобной индексации и поиска.
У меня есть три таблицы: products, companies, categories, соответственно товары, компании и категории товаров. Создадим view, в котором объединим те данные из всех трех таблиц, по которым будет идти поиск:
CREATE OR REPLACE
VIEW `catalog` AS
SELECT p.`id` , p.`id_company` , p.`id_category` , p.`name` , p.`keyword` , p.`short_desc` , com.`name` AS company_name, cat.`name` AS category_name
FROM `products` p
JOIN `categories` cat ON p.id_category = cat.id
JOIN `companies` com ON p.id_company = com.id
WHERE 1 

Поиск в моем случае ведется по полям: название, ключевое слово, краткое описание товара, название компании-производителя и название категории товара. В WHERE можно дописать дополнительные условия, например чтобы поиск шел только по активным компаниям и товарам. На этом подготовительные работы с базой данных закончены.

2. Установка Sphinx


Необходимо с оффсайта скачать свежий дистрибутив: www.sphinxsearch.com/downloads.html
Я скачал себе версию Win32 binaries w/MySQL support 1.10 beta. Далее распаковываем содержимое архива в какую-нибудь папку (я распаковал в C:\Sphinx)

Конфигурирование Sphinx

После скачивания и распаковки архива следующий шаг — настройка. Я в качестве основы взял файл sphinx.conf.in
В этом разделе просто буду приводить конфиги с небольшими комментариями, там вроде все понятно. Весь текст из этого раздела лежит в одном файле sphinx.conf.in

Источник данных

# Источник данных для поиска
source catalog
{
	# Тип данных
	# Доступные типы: mysql, pgsql, mssql, xmlpipe, xmlpipe2, odbc
	type			= mysql

	# Необходимые параметры для подключения к базе данных
	sql_host		= localhost
	sql_user		= root
	sql_pass		=
	sql_db		= products
	sql_port		= 3306	# опциональный, по умолчанию 3306

	# пред-запрос, выполняется перед выполнением основного запроса на получение данных из базы
	# В нашей базе данные хранятся в UTF-8, чтобы поиск по русским символам работал успешно выполним соответсвующий запрос
	sql_query_pre		= SET NAMES utf8

	# запрос, который получает данные документов для поиска
	# первым полем обязательно должен идти уникальный положительный ID документа
	sql_query		= \
		SELECT * \
		FROM catalog         

        # поля, по которым может идти группировка, фильтрация и сортировка
        sql_attr_uint    = id_company
        sql_attr_uint    = id_category

	# document info query, ONLY for CLI search (ie. testing and debugging)
	# optional, default is empty
	# must contain $id macro and must fetch the document by that id
	sql_query_info		= SELECT * FROM products WHERE id=$id
}


Параметры индексации товаров

# индекс каталога товаров для поиска
index catalog
{
     # Источник данных для индексирования
    source            = catalog

    # Адрес, где будут хранится данные индекса
    path            = C:\Sphinx/data/catalog

    # Индекс с учетом морфологии
    morphology        = stem_ru

    # Минимальная длина слова для индексации
    min_word_len        = 1

    # Кодировка
    charset_type        = utf-8
}


Настройки поискового демона (службы)

# поисковый демон (служба)
searchd
{
    # какой порт и какой протокол "слушает" служба
    listen            = 9312

    # файл с логами
    log            = C:\Sphinx/log/searchd.log

    # файл с логами поисковых запросов
    query_log        = C:\Sphinx/log/query.log

    # PID file, searchd process ID file name
    # mandatory
    pid_file        = C:\Sphinx/log/searchd.pid
}


На этом конфигурирование сфинкса закончено.
Теперь установим нашу службу. Для этого в адресной строке пишем C:\Sphinx\bin\searchd --install --config C:\Sphinx\sphinx.conf.in --servicename SphinxSearch
Служба установлена и должна появится в администрирование — службы. Пока не запускаем — наши данные еще не проиндексированы.

3. Индексирование и тестовый поиск


Теперь в командной строке пишем C:\Sphinx\bin\indexer --all --config C:\Sphinx\sphinx.conf.in. Получаем результат вроде этого:
C:\Users\Iskander> C:\Sphinx\bin\indexer --all --config C:\Sphinx\sphinx.conf.in

Sphinx 1.10-beta (r2420)
Copyright (c) 2001-2010, Andrew Aksyonoff
Copyright (c) 2008-2010, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file 'C:\Sphinx\sphinx.conf.in'...
indexing index 'catalog'...
collected 572 docs, 0.1 MB
sorted 0.0 Mhits, 100.0% done
total 572 docs, 115192 bytes
total 0.380 sec, 303124 bytes/sec, 1505.19 docs/sec
total 2 reads, 0.000 sec, 31.6 kb/call avg, 0.0 msec/call avg
total 9 writes, 0.001 sec, 14.6 kb/call avg, 0.1 msec/call avg


Все, индекс создан. Теперь запускаем нашу службу SphinxSearch и в поисковой строке пишем что нибудь вроде
C:\Sphinx\bin\search --config C:\Sphinx\sphinx.conf.in computer и получаем результат
C:\Users\Iskander> C:\Sphinx\bin\search --config C:\Sphinx\sphinx.conf.in computer
Sphinx 1.10-beta (r2420)
Copyright (c) 2001-2010, Andrew Aksyonoff
Copyright (c) 2008-2010, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file 'C:\Sphinx\sphinx.conf.in'...
index 'catalog': query 'computer': returned 1 matches of 1 total in 0.000 sec

displaying matches:
1. document=1, weight=2812, id_company=2, id_category=1263
        id=1
        id_company=2
        id_category=1263

words:
1. 'computer': 1 documents, 2 hits


Поиск работает!

4. Поиск из php


Ну тут все просто. Проведем, например, поиск по запросу «Computer»
<?php
    // Подключим файл с api
    include('C:\Sphinx\api\sphinxapi.php');

    // Создадим объект - клиент сфинкса и подключимся к нашей службе
    $cl = new SphinxClient();
    $cl->SetServer( "localhost", 9312 );

    // Собственно поиск
    $cl->SetMatchMode( SPH_MATCH_ANY  ); // ищем хотя бы 1 слово из поисковой фразы
    $result = $cl->Query("computer"); // поисковый запрос

    // обработка результатов запроса
    if ( $result === false ) { 
          echo "Query failed: " . $cl->GetLastError() . ".\n"; // выводим ошибку если произошла
      }
      else {
          if ( $cl->GetLastWarning() ) {
              echo "WARNING: " . $cl->GetLastWarning() . " // выводим предупреждение если оно было
    ";
          }

          if ( ! empty($result["matches"]) ) { // если есть результаты поиска - обрабатываем их
              foreach ( $result["matches"] as $product => $info ) {
                    echo $product . "<br />"; // просто выводим id найденных товаров
              }
          }
      }

  exit;


Вот собственно и все, мы установили sphinx, проиндексировали нашу базу данных и написали код на php который ищет по нашей базе и выводит id найденных товаров.

Пришло время дальше изучать документацию, настраивать ранжирование как этого требует проект, и вообще продолжать перенос функционала с полнотекстового поиска на сфинкс.

Надеюсь эта статья сможет кому нибудь помочь сделать «быстрый старт» со сфинксом, потому что по-моему очень не хватает нормальных русских статей и документации по этому, довольно сложному, на мой взгляд, продукту.
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 59
  • 0
    Прикольно получается, когда переводят всё, включая опции:)

    morphology = stem_ru

    А английский вам не нужен?
    • +2
      Хочу отдельно заметить что это статья — нечто вроде руководства по быстрому старту, т.е. куда что прописать чтобы сфинкс начал работать и что то искать. Тонкости настройки — тема для отдельной статьи.

      Но вообще лично мне не нужна английская морфология, у меня база полностью на русском.
      • +3
        Вы уж извините :)

        Цитата из статьи:
        ________________________
        4. Поиск из php
        Ну тут все просто. Проведем, например, поиск по запросу «Computer»
        ________________________
        • +2
          Да это пример, из командной строки я тоже на английском искал потому то на русском в командной строке кракозябры выходят

          по секрету скажу что везде где в статье есть пример поиска по слову «computer» я на самом деле искал по запросу «газонная решетка»
    • +1
      Спасибо! Как-то безуспешно искал вменяемый материал на русском по сфинксу…
      • +6
        И как у вас с буквой ё дела обстоят?
        хинт www.sphinxsearch.com/forum/view.html?id=5401
        • +1
          Еще не столкнулся с этой проблемой, а уже знаю решение ;)

          Спасибо!
        • 0
          А у меня следующий вопрос — как Sphinx берет данные для индексирования? Он с каким то интервалом проходит весь view catalog? Или может тригеры какие нибудь вешает в базу на изменения данных? view catalog в конечном счете может оказаться очень большим. И в каком режиме Sphinx читает view catalog — dirty read? Или он лочит все таблицы?
          • 0
            Это тема для отдельной статьи. Обычно держат два индекса: то что изменилось сегодня И всё остальное. Первый перестраивается раз в 5 минут и мёрджится со вторым раз в сутки.
            • 0
              добавлю, что смотерть в сторону дельта-индекса
              • 0
                RT, если быть точнее.
                • 0
                  это не одно и то же
              • 0
                уже есть Real-time index
                • 0
                  RT это все таки изврат для избранных с небольшой базой. Ибо хранится в памяти.
                  • 0
                    Вы не правы, он частично в памяти:
                    ==
                    RT индексы внутри состоят из фрагментов. Один фрагмент хранится в оперативной памяти, который хранит последние обновления. Когда размер фрагмента в RAM превышает лимит, он сбрасывается на диск, а оперативная память очищается
                    ==
                    • 0
                      Да, моя ошибка.
                      Но все таки это еще новая вещь с кучей ограничений. Если нет проблем с оперативкой — вполне можно взять обычный индекс, благо там многое кешируется. Хотя интересно было бы сравнить результаты на большой базе.

                      А если есть нехватка памяти, то брать RT имхо не стоит, учитывая заявленные ограничения. Всё таки бетка.
                        • 0
                          1) Зачем смешивать RT & обычные индексы? :) Распределенные вроде как нужны немножко для других целей — как правило для объединения нескольких индексов с разных машины.
                          2) Не указаны параметры индексов при тестировании. Обычный индекс можно загнать в ситуацию, когда он в памяти будет хранить минимум информации, а можно заставить хранить почти все. Разница в скорости и использовании винта будет на порядок.

                          Хотя результаты теста как раз и подтверждают мои слова чуть выше :)
                          RT — изврат для небольших баз. Хотя надеюсь в релизе оптимизируют.
              • 0
                Sphinx индексирует данные, когда вы запускаете indexer.
                У нас он, например, запускается по крону в ночное время (основные индексы), и днём с часовым интервалов (дельта-индексы).

                В любой момент через indexer --rotate-all можно обновить все данные.
                • 0
                  Для снижения нагрузки есть возможность настроить выборку интервалом:
                  WHERE ( id>=$start AND id записанного. дельта обновляется чаще.

                  два полученных индекса можно как мержить, так и оставить как есть (рекомендую второе. Сколько помню мерж — пару раз натыкался на глюки, на одной из версий помнится даже умирала база).

                  Отдельный геморой раньше был с удаленными записями. Но в последнем релизе придумали клевую фишку, и теперь как правило это делается просто и красиво
                • 0
                  Большую нагрузку создает Sphinx? Думаю его поставить на VDS
                  • 0
                    Да никакую нагрузку, только что в то время, как сканирует базу, но данную проблему легко решить, коннектясь к slave базе.
                    • 0
                      Да и индекс строится очень быстро… у меня пять миллионов записей меньше чем за 10 минут проиндексировал.
                      • 0
                        Именно поэтому и нужно на slave стучаться, у меня база мускульная гигабайта четыре, поиск идет очень быстро, но тоже не одну и не две минуты, зачем-же насиловать основную базу. Я отдельный slave держу для сфинкса и бэкапа, очень удобно :)
                        • 0
                          А это смотря сколько данных :)

                          В нашем случае полное индексирование проходит минут за 40, при этом на Sphinx выделено что-то около гигабайта памяти.
                          • 0
                            а если не секрет, сколько данных у вас? :)
                    • 0
                      А есть API для других ЯП? (желательно C#)
                      Может ссылку знаете?..
                    • 0
                      глупый вопрос, может кто ответ тут подскажет: есть папка с 10 Гб пдф документов (вперемешку с док и эксель файлами, но это не так важно) и есть относительно свободный сервер с линуксом.
                      как мне поставить\настроить поиск по этим документам? главная проблема: не могу сформулировать нужные запросы для гугла. куда копать, с чего начать?
                    • 0
                      Я для поиска по сайту использую DataparkSearch Engine
                      • 0
                        Ух ты… Похоже мощная штука. Я правильно понял что у него на выходе сразу HTML страничка и нету API?
                        • 0
                          На выходе можно получить любой текстовый формат, в директории doc/samples дистрибутива лежит шаблон rss8.htm.en используя который можно выдавать результаты в RSS.

                          API нет.
                        • 0
                          А… так он чтоли сам сайт по http сканирует? Не базу?
                          • 0
                            И по http может, и напрямую базу может.
                        • 0
                          Вы мне объясните soundex на сфинксе по русски работает?
                          • 0
                            Если вам для спеллчекера, то советую проверку по триграммам habrahabr.ru/blogs/sphinx/61807/ — работает прелестно плюс словарь строится на основе вашего же индекса
                            • 0
                              Мне нужно решить такую задачу.
                              Есть текстовый запрос с декстопной программы с самыми грубыми ошибками к БД.
                              Есть БД наиболее употребляемых слов русского языка (например, 100 тыс. слов).
                              Как получить из БД список слов максимально релевантных запросу?
                              Например, ввели «поардох» — получили «пароход, паровоз, порох»
                              • 0
                                Да, метод с триграммами с этим довольно хорошо справляется. Выдает список вариантов. В архиве с исходниками сфинкса есть пример php скрипта к той статье. Дополнительный плюс, что этот метод можно собственноручно «подтюнить» (см myrank из статьи).

                                По крайней мере я реализовывал спеллчекер для поискового индекса крупного интернет-магазина автозапчастей. В продакшн еще не выпустили но на тестах работает отлично.
                                А если у вас уже база слов а не текстов, то процесс еще упрощается т.к. триграммы можно генерировать сразу из базы слов а не indexer-ом.
                                • 0
                                  Огромное спасибо. Буду изучать Сфинкс.
                            • 0
                              Soundex, как алгоритм, применим только к английскому и родственным ему языкам.

                              На русском же выдает такое… ужасть просто.
                            • 0
                              После большой и страстной любви со сфинксом и его ранкерами в своем проекте решил избавиться от него как можно быстрее.

                              В итоге успешно перебрался на Solr, попутно получив фасетный поиск, подсветку результатов и real-time обновления для изменившихся данных в индексе.

                              Да, и еще он умеет кушать .doc, .pdf и т.п. вкусные файлы :)
                              • 0
                                Интрига целая, даже интересно.

                                А откуда взялась «большая любовь с ранкерами»? Там вроде ровно 1 вызов «выбрать ранкер» и несколько тех ранкеров.
                              • +1
                                Ну между прочим вот отличная статья того же толка: www.ibm.com/developerworks/ru/library/os-php-sphinxsearch/

                                А кто как решает проблему с окончаниями? Пока что в голову пришла идея отрезать пару символов с конца слова и отдавать sphinx`у запрос вида «автошко*» для поиска по вхождениям. За любые хинты буду очень благодарен.
                                • 0
                                  Если я не ошибаюсь, достаточно подключить словарь словоформ
                                  • 0
                                    уже погрузился в статью про триграммы — в ней нашел все ответы. ссылка выше.
                                  • 0
                                    О, так она есть на русском! :)

                                    А я ее читал на английском www.ibm.com/developerworks/library/os-php-sphinxsearch/

                                    У нее есть минус что она написана для старой версии сфинкса и я когда пробовал брать из нее конфиги получал ошибки что, например, sql_group_column уже deprecated и нужно использовать другие параметры в конфиге
                                    • 0
                                      это единственное кстати исправление :)
                                    • +1
                                      Проблема с окончаниями решается как я понимаю подключением морфологии (morphology = stem_ru)

                                      По крайней мере у меня по запросу «газонные решетки» находит товар «газонная решетка», по запросу «куплю газонную решетку» — тоже.
                                      • 0
                                        Дада, именно стеммер en.wikipedia.org/wiki/Stemming для этого предназначен. В сфинксе есть встроенные для английского русского чешского и еще кучу можно подключить через стороннюю библиотеку: sphinxsearch.com/docs/current.html#conf-morphology
                                        • 0
                                          А что лучше справляется с окончаниями стеммер или триграммы?
                                          • 0
                                            Вы, судя по всему пользовались стеммингом, можете сказать своё мнение о том на сколько он эффективен? Спасибо.
                                            • 0
                                              Ну я просто стеммер не отключал а без него не пробовал. Причем толькко на английском языке.
                                              Вообще вполне эффективен.

                                              А n-граммы вроде как для отбрасывания окончаний и не предназначены. Используйте стеммер конечно же
                                            • 0
                                              Стемминг предназначен не того, чтобы по запросу «собакой» в результат попали документы с другими словоформами: «собака», «собаки», «собаками» и т.д.

                                              Для поиска по маске создана директива min_infix_len. И то и то вещи похожие но все же разные
                                            • 0
                                              Смотрите в сторону min_infix_len — этот параметр как раз для ваших целей и предназначен
                                            • 0
                                              Спасибо большое за статью, пишите по сфинксу ещё :)
                                              • +1
                                                не за что:)
                                                Пока особо нечего писать, добавил исправление ошибок в запросах с использованием триграмм и все работает :) т.к. поисковых запрсов на сайте немного (порядка 100 в день) то на доработку поиска пока не заморачиваемся, сконцентрировав внимание на более актуальных частях проекта.

                                                Как только что — сразу напишу :)

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