Sphinx Search плагин поиска для Wordpress – поиск с Sphinx Search для начинающих

Posted in PHP, Sphinx Search, WPSphinx plugin on December 23rd, 2009 by Yaroslav Vorozhko – 6 Comments

Два года назад Петр Зайцев из Percona попросил меня написать ему плагин поиска для WordPress используя Spinx Search. Сейчас этот модуль работает на mysqlperformanceblog.com.

Данная статья будет полезна тем кто только начинает знакомится с Sphinx Search. Ее можно использовать  как начальное руководство для написания простого поисковика на Sphinx Search.

Возможности плагина:

  • Быстрый поиск, ну это и понятно, мы ведь используем Sphinx Search
  • Возможность использовать расширенный синтаксис поиска (http://www.sphinxsearch.com/doc.html#extended-syntax)
  • Сортировка результатов поиска по дате или по релевантности
  • Поиск по постам, комментариям или страницам. Это отличает этот плагин от стандартного поиска на WP, который не производит поиск по комментариям и страницам. А также многие другие поисковые плагины не имеют такой возможности.
  • Есть возможность исключить из результатов поска комментарии, страницы или посты
  • И многие другие вкусности, про которые вы можете узнать на странице плагина

Все это позволяет нам делать Sphinx Search, и сейчас мы разеберем как это реализовано.

Конфигурационный файл

В первую очередь нам надо знать как устроен индекс. (sphinx.conf можно найти в каталоге rep/sphinx.conf)

Мы использовали самое простое решение это один монолитный индекс для всех данных: постов, страниц и комментариев. Формируется такой индекс единым SQL запросом, который приводить я тут не буду, он очень длинный и нас сейчас он не интересует (это все таки статья про Spihnx Search, а не про MySQL :) ), но посмотреть его можно в том же sphinx.conf.

Единственное, что нам стоит знать это какие атрибуты у нас есть:

  • comment_ID
  • post_ID
  • isPost
  • isComment
  • isPage
  • post_type
  • date_added

Атрибуты isPost, isComment и isPage отвечают за тип источника. date_added содержит дату добавления данных.

Поиск

Теперь рассмотрим как делать поиск, фильтрацию и сортировку используя атрибуты.
Пример:

CODE:
  1. if ( empty($this->params['search_comments']) ){
  2.     $this->config->sphinx->SetFilter('isComment', array(0));
  3. }
  4.                
  5. if ( empty($this->params['search_pages']) ){
  6.     $this->config->sphinx->SetFilter('isPage', array(0));
  7. }
  8.            
  9. if ( empty($this->params['search_posts']) ){
  10.     $this->config->sphinx->SetFilter('isPost', array(0));
  11. }
  12.        
  13.        
  14. if ( $this->params['search_sortby'] == 'date' ){ {
  15.     $this->config->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'date_added');}
  16. } else {
  17.     $this->config->sphinx->SetSortMode(SPH_SORT_RELEVANCE);
  18. }
  19.  
  20. $res = $this->config->sphinx->Query ( $this->search_string, $this->config->admin_options['sphinx_index'] ););

Первое, если один из аттрибутов не установлен, то с помощью SetFilter('isPost', array(0)) мы исключаем его из поиска.
Второе, если пользователь захотел отсортировать результаты по дате добавления, то мы испольязем режим сортировки по атрибуту SetSortMode(SPH_SORT_ATTR_DESC, 'date_added'). По умолчанию данные сортируются по релевантности.
И последнее мы выполняем собственно запрос с помощью метода Query(), первый параметр это запрос введенный пользователем, второй это индекс по которому выполнять поиск.

Результат поиска

Результат поиска мы должны обработать следующим образом:

  • Получить найденный идентификационные номера и по ним получить данные
  • Используя атрибуты isPost, isPage и isComment мы узнаем из какой таблицы получать данные
  • Потом объединяем полученный результат
  • И последнее мы выделяем ключевые слова в результата, путем добавления html тэга STRONG вокруг слова.

Выделение ключевых слов делает метод BuildExcerpts

CODE:
  1. $opts = array(
  2.     'limit'  => $this->config->admin_options['excerpt_limit'],
  3.     'around' => $this->config->admin_options['excerpt_around'],
  4.     'chunk_separator' => $this->config->admin_options['excerpt_chunk_separator'],
  5.     'after_match' => $this->config->admin_options['excerpt_after_match'.$isTitle],
  6.     'before_match' => $this->config->admin_options['excerpt_before_match'.$isTitle]
  7. );
  8.                    
  9. $excerpts = $this->config->sphinx->BuildExcerpts(
  10.     $post_content,
  11.         'main_'.$this->config->admin_options['sphinx_index'],
  12.     $this->search_string,
  13.     $opts
  14. );

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

В итоге как мы видим, написать свой поиск используя Sphinx Search достаточно просто. Если у вас большой блог и вы также хотите получить быстрый и много-функциональный поиск, тогда скачивайте WPSphinx плагин - это бесплатно. :)

Как вы используете Sphinx Search API?

Posted in Development, LinkAider, Projects, Sphinx Search, ZendFramework on December 21st, 2009 by Yaroslav Vorozhko – Be the first to comment

Sphinx Search API для PHP пердставляет собой единый класс, который позволяет использовать все возможности Sphinx Search через его интерфейс. Но, такой класс является удобным только для небольших скриптов и задач.
Для более сложных задач и больших веб приложений необходимо другое решение. И это решения является проектированием и реализацией собственной обертки для Sphinx Search API.

В нашем проекте LinkAider.com мы используем следующие понятия и классы при работе с Sphinx Search:

  1. Сфинкс клиент, отвечающий за подключение, выполнение запросов и обработку ошибок. Для разработчика сфинкс клиент невидим, мы только сообщаем ему параметры подключения к searchd.
  2. Сфинкс индекс - это один из основных классов, с которым работает разработчик, этот класс отвечает за формирование запросов и выполнение запросов через Сфинкс клиент, а также за обработку результатов запроса.
  3. Сфинкс запрос - это еще один класс к которому обращается разработчик для составления запросов. Каждый запрос отвечает за свои индекс к которому обращается, а также содержит свои фильтры, группировки, сортировки и собственно сам запрос.
  4. Сфинкс результат - это класс, который разбирает ответ сфинкса и предоставляет удобный интерфейс к информации по каждому запросу, также он содержит информацию об ошибках, которую испльзуетя Сфинкс клиент для логирования. Сфинкс результат используется разработчиками для создания запросов к базе данных и получения искомых данных.

Пример работы выглядит следующим образом:

  1. Мы создаем объект Сфинкс индекс, который инициализирует Сфинкс клиент, устанавливая для него параметры подключения.
  2. Потом мы создаем объекты запросы для каждого указываем фильтры, группировки и т.п., и указываем к какому индексу делать запрос. Запросов может быть один или несколько, несколько запросов обрабатываються паралельно, что улучшает общую производительность системы.
  3. Каждый созданный запрос мы добавляем в Сфинкс индекс, при добавлении мы можем также указатьк какому индексу делать запрос.
  4. Специальный метод Run класса Сфинкс индекс запускает все запросы и как результат возвращает нам объект Сфинкс результат.
  5. Данные из Сфинкс результата мы используем чтоб создать запросы к базе данных и получить искомые данные.

Преимущеста, которые мы получем от работы с такой библотекой - это простота. Разработчик используя объект Сфинкс запрос может выполнять любые запросы.
Сфинкс результат предоставляет удобную обертку над массивом результата.
Нет необходимости помнить множество констант SphinxClient и парсить массив результата. В общем это выглдит так, как будто вы работаете с обычной таблицой, но не до конца. :)

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

Решение я вижу, в привязке каждого запроса к определенной таблице или группе таблиц.

По описнной выше логике я разрабатываю модуль Sphinx Search для Zend Framework, который я планирую выложить в откртый доступ очень скоро, возможно к новому году.

Поэтому хотелось бы узнать как вы используете Sphinx Search и какие преимущества и недостатки вывидите у вашего подхода.

Буду рад любым советам и рекомендациям.

Простой загрузчик файлов для Zend Framework

Posted in Development, Tips And Tricks, ZendFramework on December 17th, 2009 by Yaroslav Vorozhko – Be the first to comment

Сегодня увидел хороший code snippet простого лоадера файлов в проект на Zend Framework.

CODE:
  1. class App_Application_Resource_Fileloader extends Zend_Application_Resource_ResourceAbstract {
  2.   /**
  3.    * @return null
  4.    */
  5.  
  6. public function init() {
  7.         $options = $this->getOptions ();
  8.         foreach ($options as $filePath)
  9.             include $filePath;
  10. return null;
  11.        
  12.     }
  13.    
  14. }
  15.  
  16.  
  17. resources.fileLoader[] = LIBRARY_PATH "/App/Regex_Functions.php"
  18. resources.fileLoader[] = LIBRARY_PATH "/App/Api_Functions.php"

Autoload своих библотек в Zend Framework 1.8+

Posted in Development, PHP, Tips And Tricks, ZendFramework on December 11th, 2009 by Yaroslav Vorozhko – Be the first to comment

Чтоб загружать в Zend Framework свои библиотеки, нам надо сделать следующее:
Первое - это добавить физический путь к библиотеке в include_path.
Второе - это указать префикс классов библиотеки, если такой вообще используется.

Добавить путь в include_path можно двумя способами:
1. Жетско прописать путь в public/index.php файле:

CODE:
  1. set_include_path(implode(PATH_SEPARATOR, array(
  2.     realpath(APPLICATION_PATH . '/../library'),
  3.     realpath(APPLICATION_PATH . '/../mylib'),
  4.     get_include_path(),
  5. )));

2. Прописать путь в конфигруационном файле application/configs/application.ini

CODE:
  1. includePaths.mylib = APPLICATION_PATH "/../mylib"

В обоих случаях мы добавляем путь к mylib в include_path. Второй способ более предпочтительный, так как на основе выбранной конфигурации (production, develeopment, test) можно устанавливать путь к различным версиям mylib.
Кстати используя этот же прием, можно устанавливать путь и к каталогу library/Zend и на основе конфигураций подгружать различный версии Zend Framework.

И так, второе что нам необходимо сделать - это подключить автозагрузчик и установить префикс используемый классами библиотеки.
Я прописал автозагрузчик в файле public/index.php

CODE:
  1. require_once 'Zend/Loader/Autoloader.php';
  2. $loader = Zend_Loader_Autoloader::getInstance();
  3. $loader->registerNamespace('My_');

В данном случае префикс классов в моей библиотеки "My_".

Также, этот же код можно прописать и в классе Bootstrap.php

CODE:
  1. protected function _initAutoload()
  2.     {
  3.         require_once 'Zend/Loader/Autoloader.php';
  4.         $loader = Zend_Loader_Autoloader::getInstance();
  5.         $loader->registerNamespace('My');
  6.     }

Все, теперь ZF знает как загружать классы из вашей бибилиотеки.

Zend Studio: Workspace in use choose different workspace

Posted in Clients Configuration, Tips And Tricks, Zend Studio on December 7th, 2009 by Yaroslav Vorozhko – Be the first to comment

Вот такую ошибку я увидел недавно, после того как экстренно завершила работу Zend студия.
Причем заставить выбрать рабочее простраство по умолчанию не представляется возможным.
Вот как решается это под Linux (Ubuntu):
rm -rf /tmp/eclipse
С данной проблемой под Windows не сталкивался, поэтому точного решения не подскажу.
Но, как ориентир, я порекомендовал бы почистить "tmp" каталог в Application Data.

Lighttpd создаем виртуальные хосты автоматически

Posted in Lighttpd, Server Configuration on December 6th, 2009 by Yaroslav Vorozhko – Be the first to comment

Для создания автоматических вартуальных хостов нам потребуется модуль evhost. Этот модуль идет в стандартной поставке Lighttpd.
В секции modules расскомментируйте соответсвующую строку или добавте новую, если модуля нет в списке.
Первое, что нам надо знать - это то как lighttpd читает и парсит URI с помощью этого модуля.

CODE:
  1. %% => % sign
  2.   %0 => domain name + tld
  3.   %1 => tld
  4.   %2 => domain name without tld
  5.   %3 => subdomain 1 name
  6.   %4 => subdomain 2 name
  7.   %_ => full domain name

Нас интересуют сабдомены первого уровня, которые хранятся в переменной %3.
Создадим для них специальный конфиг:

CODE:
  1. $HTTP["host"] =~ ".*\.example\.org" {
  2.       evhost.path-pattern = "/home/%3/public_html/"
  3.   }

Этим конфигом мы будем перехватытвать все запросы обращенные к example.org и всем его сабдоменам.
Директива evhost.path-pattern устанавливает document root для каждого сабдомена.
Note: не указывайте server.document-root как путь по умолчанию к вашим сабдоменам. Это может быть опасно.

Теперь каждый каталог в /home будет считатся сабдоменом для example.org.
Незабудте также прописать каждый сабдомен в DNS или /etc/hosts.
Перезапускаем Lighttpd и проверяем.
Спасибо!

HG Mercurial email уведомления

Posted in Mercurial HG, Server Configuration, Tips And Tricks on November 24th, 2009 by Yaroslav Vorozhko – Be the first to comment

В каждом серьезном проекте используется контроль версий и разработку зачастую ведет не один программист. Как минимум, кроме программиста есть еще и главный программист, который следит за качеством разработки.
С помощью hg mercural мы можем предоставить главному программисту мониторинг изменений в репозитории, особенно когда это касается обновления главной ветки.
В mercurial для этого есть замечательный встроенный плагин notify, который поможет нам настроить уведомления по email.

Схема репозиториев будет следующая:

  1. Главный /www/mybigsite.com/
  2. Транзитный /home/repos/mybigsite
  3. Репозиторий разработчика находится в его домашнем каталоге.

Когда разработчику надо отправить новую порцию изменений в главный репозиторий, он:

  1. Отправляет данные в транзитный репозиторий (команда hg pus)
  2. А потом главный разработчик проверяет изменения и обновляет главный репозиторий (команда hg pus && hg up)

Настроим два уведомления:

  1. По событию hg pus в транзитный репозиторий - будем отправлять diff изменений главному программисту. Таких обновлений (hg pus) разработчик или разработчики могут сделать несколько, до того как будет обновлен главный репозиторий.
  2. Главный программист получив уведомления об обновлении транзитного репозитория проверяет изменения и обновляет главный репозиторий. По этому событию мы отправляем главному разработчику новую порцию diff изменений, который содержит именно все порции обновлений, что попали на главный репозиторий.

Теперь перейдем к настройке уведомлений используя hghook notify.
Открываем файл команд hgrc транзитного репозитория, если файла нет, то создаем его /home/repos/mybigsite/.hg/hgrc и добавляем следующие строки

read more »