Блог веб-разработчика v 1.0.0
Symfony2, AngularJS, React, Gulp, PhpStorm и много других страшных слов

Поиск через Sphinx в Symfony2

10 лет назад
9483 просмотра
PHP PHP Frameworks Sphinx Symfony2

Использование Sphinx на Symfony2 с выдачей результатов сущностями DoctrineПри реализации поиска на сайте есть несколько вариантов: использовать голый поиск по БД через LIKE (или MATCH в лучшем случае), использовать сторонний поисковый движек (например Google) или использовать поисковый движек Sphinx. Sphinx устанавливается на сервер в виде отдельного демона, который индексирует БД по вашим же запросам и производит релевантный поиск по этому индексу. Причем поиск идет в разы быстрее, чем прямые запросы в БД. Конечно, установка такого поискового движка требует некоего опыта в администрировании серверов, но на самом деле все не так сложно. В этой статье, допустим, что у вас уже есть сервер с настроенным и установленным Sphinx,а так же сайт на Symfony2 на котором хочется реализовать быстрый релевантный поиск.

Для Symfony2 есть несколько готовых бандлов для подключения поискового движка Sphinx. Все они основываются на библиотеках вроде PHP SphinxAPI. Но у каждого из этих бандлов есть "фатальный недостаток": результаты поиска - это голый PHP массив, который приходится вручную парсить и собирать в сущности Doctrine2. Это не очень удобно, поэтому давайте избавимся от этой проблемы.

Настройка индексации Sphinx

Для использования SphinxsearchBundle необходимо добавить атрибут index_name в каждый source индекса Sphinx, который мы собираемся использовать. Данный атрибут никак не повлияет на работу вашего поискового индекса, поэтому можете не опасаться дополнительных проблем. Конкретно в нем не нужно указывать имя сущности, а только наименование индекса. Для примера добавим атрибут к нашему воображаемому индексу IndexName:

source Example {
     sql_query = SELECT id, title, 'IndexName' as 'index_name' FROM my_table
     sql_attr_string = index_name
}

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

SphinxsearchBundle устанавливается через composer (желательно). Он не требует зависимостей, но все же это рекомендуемый метод установки. Добавляем секуцию в composer.json и обновляем наши зависимости. Конечно, можно просто скачать исходники из GitHub репозитория.

{
     "require": {
         "iakumai/sphinxsearch-bundle": "dev-master"
     }
}

Далее нужно добавить бандл в AppKernel.php в вашем Symfony2 проекте:

public function registerBundles() {
     $bundles = array(
         // ...
         new IAkumaI\SphinxsearchBundle\SphinxsearchBundle()
     );
}

А так же добавить секцию sphinxsearch в config.yml:

sphinxsearch:
     searchd:
         # Хост для подключения к демону Sphinx
         host: localhost
         # Порт для подключения к демону Sphinx
         port: 9312
         # Файл сокета, если нужно подключаться к демону через сокет
         socket: /path/to/socket.file
     indexes:
         # Список индексов Sphinx (ключ) и имен Entity (значение)
         # которые будут использоваться при поиске
         IndexName: "Bundle:Entity"

Мы указали параметры подключения к демону Sphinx, а так же обозначили, что результаты поиска по идексу IndexName будут преобразовываться в сущности Bundle:Entity. Это очень важный момент в настройке бандла, т.к. без данной секции после поиска мы будем получать "голый" PHP массив.

Поиск через SphinxsearchBundle

Бандл предоставляет сервис iakumai.sphinxsearch.search для использования Sphinx в вашем проекте. Данный сервис содержит два публичных метода для поиска. Самый простой из них это search(). Пример использования:

// Метод вашего контроллера
public function searchAction(Request $request) {
     $searchd = $this->get('iakumai.sphinxsearch.search'); // Получаем объект сервиса
     // Производим поиск по указанному индексу
     return $sphinxSearch->search($request->query->get('q', ''), array('IndexName')); 
}

// Пример результатов поиска
array(10) {
  .....
  ["matches"]=>
  array(20) {
    [22]=>
    array(3) {
      ["weight"]=>
      string(1) "2"
      ["attrs"]=>
      array(0) {
      }
      ["entity"]=> ... // Здесь наш объект Bundle:Entity
    }
    .........

При использовании метода search() поиск может идти только по одному индексу. Поэтому дополнительно настраивать Sphinx не требуется вообще. Но в действительности мы редко будем искать всего по одному индексу, поэтому используем другой публичный метод searchEx() для которого мы и настраивали Sphinx в начале статьи. Пример использования:

// Метод вашего контроллера
public function searchAction(Request $request) {
     $searchd = $this->get('iakumai.sphinxsearch.search'); // Получаем объект сервиса
     return $sphinxSearch->searchEx($request->query->get('q', ''), array('IndexName', 'AnotherIndex')); // Производим поиск по указанному индексу
}

// Пример результатов поиска
array(10) {
  .....
  ["matches"]=>
  array(20) {
    [22]=>
    array(3) {
      ["weight"]=>
      string(1) "2"
      ["attrs"]=>
      array(0) {
        ["index_name"]=>
        string(9) "IndexName"
      }
      ["entity"]=> ... // Здесь наш объект Bundle:Entity
    }
    .........

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

Так же бандл поддерживает фильтры, сортировку, пагинацию и многое другое. Обо всех возможностях можно прочитать на странице проекта на GitHub: https://github.com/IAkumaI/SphinxsearchBundle

Что еще почитать