Yii: Делаем поиск по сайту, используя Zend Lucene
Ожнажды мы делали поиск по сайту, используя только средства самого Yii (делая выборку данных из базы данных запросом со сравнением like). Этот способ прекрасно работает на сайте loco.ru, можно тут и опробовать его. Почитайте нашу статью о поиске по сайту на Yii, чтобы сейчас было проще. Идея та же, только теперь мы задействуем мощности библиотека Zend_Search_Lucene из фреймворка Zend.
(Для версии Yii2 смотрите Поиск по сайту с Zend Lucene для Yii2)
1. Скачиваем на официальном сайте архив фреймворка Zend.
2. Берём оттуда library/Zend/Search и копируем в vendors/Zend, также копируем library/Zend/Exception.php туда же. Если у вас ещё нет папки vendors, создайте. Мы завели её, чтобы отличать сторонние решения от самого Yii. Zend - сторонний "поставщик" библиотеки Zend_Search_Lucene в нашем случае. Посмотрите что получилось:
Структура папок в Yii с Zend Lucene.
3. Создайте в папке protected/runtime директорию search с правами на запись. Zend будет туда сохранять индекс страниц для результатов поиска. Ему можно.
4. В components создаём SearchBlock.php, который дальше поместим где захотим.
<?php Yii::import('zii.widgets.CPortlet'); class SearchBlock extends CPortlet { public $title='Поиск'; protected function renderContent() { echo CHtml::beginForm(array('search/search'), 'get', array('style'=> 'inline')) . CHtml::textField('q', '', array('placeholder'=> 'search...','style'=>'width:140px;')) . CHtml::submitButton('Go!',array('style'=>'width:30px;')) . CHtml::endForm(''); } }
5. Вызовем этот виджет в шаблоне (например в правой колонке на главной в самом верху), вставляя код:
<?php $this->widget('SearchBlock', array( )); ?>
Вот что получилось на вид:
6. Теперь самое главное, мозг "поиска...", создаём контроллер protected/controllers/SearchController.php:
<?php class SearchController extends Controller { /** * @var string index dir as alias path from <b>application.</b> , default to <b>runtime.search</b> */ private $_indexFiles = 'runtime.search'; /** * (non-PHPdoc) * @see CController::init() */ public function init(){ Yii::import('application.vendors.*'); require_once('Zend/Search/Lucene.php'); parent::init(); } /** * Search index creation */ public function actionCreate() { setlocale(LC_CTYPE, 'ru_RU.UTF-8'); Zend_Search_Lucene_Analysis_Analyzer::setDefault( new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8()); $index = new Zend_Search_Lucene(Yii::getPathOfAlias('application.' . $this->_indexFiles), true); $posts = Place::model()->findAll(); foreach($posts as $post){ $doc = new Zend_Search_Lucene_Document(); $doc->addField(Zend_Search_Lucene_Field::Text('title', CHtml::encode($post->name), 'UTF-8') ); $doc->addField(Zend_Search_Lucene_Field::Text('link', "/place/".$post->id."-".CHtml::encode($post->alias) , 'UTF-8') ); $content = $this->clean_content($post->description); $doc->addField(Zend_Search_Lucene_Field::Text('content', $content , 'UTF-8') ); $index->addDocument($doc); } $index->optimize(); $index->commit(); echo 'Lucene index создан успешно'; } public function actionSearch() { setlocale(LC_CTYPE, 'ru_RU.UTF-8'); Zend_Search_Lucene_Analysis_Analyzer::setDefault( new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8()); $this->layout='column2'; if (($term = Yii::app()->getRequest()->getParam('q', null)) !== null) { $index = new Zend_Search_Lucene(Yii::getPathOfAlias('application.' . $this->_indexFiles)); $results = $index->find($term); $query = Zend_Search_Lucene_Search_QueryParser::parse($term); $this->render('search', compact('results', 'term', 'query')); } } // Function for returning a preview of the content: // The preview is the first XXX characters. private function preview_content($data, $limit = 400) { return substr($data, 0, $limit) . '...'; } // End of preview_content() function. // Function for stripping junk out of content: private function clean_content($data) { return strip_tags($data); } }
7. Создаём представление views/search/search.php
<?php $this->pageTitle='Результаты поиска -' .Yii::app()->name; $this->breadcrumbs=array( 'Результаты поиска по запросу: '. CHtml::encode($term), ); ?> <h3>Результаты поиска по запросу: "<?php echo CHtml::encode($term); ?>"</h3> <?php if (!empty($results)): ?> <?php foreach($results as $result): ?> <h2><?php echo CHtml::link($result->title, CHtml::encode($result->link)); ?></h2> <p><?php echo $query->highlightMatches($result->content); ?></p> <hr/> <?php endforeach; ?> <?php else: ?> <p class="error">Поиск не дал результатов.</p> <?php endif; ?>
8. Дальше по идее всё. Надо сначала набрать в строке браузера: http://домен/search/create, чтобы Lucene создал индекс для поиска в папку runtime. Дальше это можно иногда снова выполнять, когда новые материалы добаляются, то есть обновляйте индекс время от времени, либо в КРОН поместите по расписанию. Может возникать ошибка с iconv() из за русской кодировки, но когда мы добавили в контроллерах строки
setlocale(LC_CTYPE, 'ru_RU.UTF-8');
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8());
проблема ушла. Почитайте о проблеме с русским языком и iconv в Zend Lucene.
В работе поиск можно посмотреть на whytravel.ru:
Также полезно почитать:
- Adding search to Yii blog example (using Zend Lucene) - на эту статью мы ориентировались, там подробности, которые мы опускали;
- Integrating Zend_Lucene with Yii - статья Ларри Уллмана;
- Creating a Search Index with Zend_Search_Lucene - другая стаья от Ларри Уллмана;
- Документация Zend Lucene;
А для версии Yii2 смотрите Поиск по сайту с Zend Lucene для Yii2
almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.
Вы можете почитать все статьи от almix'а.
- 14 Разработка приложения на Yii. Урок 14: Выводим список категорий как новый виджет на сайте (29.04.2015)
- 13 Разработка приложения на Yii. Урок 13: Внедряем категории для статей. (29.04.2015)
- 12 Разработка приложения на Yii. Урок 12: Профилирование приложения, включаем кеширование. (20.01.2015)
- 11 Yiico. Видеокурс по разработке сайта на Yii. Урок 11: Отладка приложения, включаем журналирование. (20.01.2015)
- 10 Yiico. Видеокурс по разработке сайта на Yii. Урок 10: Если ваше приложение находится не в корневой папке, а во вложенной. (19.10.2014)
- 9 Разработка сайта на Yii с нуля. Урок 9. Выборка статей определённого автора. (13.08.2014)
- 8 Разработка сайта на Yii с нуля. Урок 8. Вызов в моделях функции, общей для них. Как избегать дублирования кода? (25.07.2014)
- 7 Разработка сайта на Yii с нуля. Урок 7. Изменение количества выводимых записей на странице в CGridView. Включаем сессии Yii. (04.09.2013)
- 6 Разработка сайта на Yii с нуля. Урок 6. Автоматич. отправка оповещений об одобренных комментариях на email автора комментария (11.08.2013)
- 5 Разработка сайта на Yii с нуля. Урок 5. Переименовываем blog в yiico. Изменяем "Home" в breadcrumbs. Включаем Gzip-сжатие. (09.08.2013)
- 4 Курс по Yii с нуля. Урок 4. Дорабатываем простую работу с пользователями: хранение пароля при редактировании пользователя. (13.04.2013)
- 3 Курс по Yii с нуля. Урок 3. Создаём новых пользователей. Организуем простую систему авторизации. Аутентификация, пароли, соли. (20.07.2014)
- 2 Yii + Git (github) на Mac. (24.03.2013)
- 2 Курс по Yii с нуля. Урок 2. Переносим и настраиваем Yii и проект нашего сайта на рабочем сервере. Избавляемся от index.php в url (09.07.2014)
- 1 Курс по Yii с нуля. Урок 1. Устанавливаем Yii на локальном компьютере. Заводим проект будущего сайта. (10.03.2013)
- 0 Composer – пакетный менеджер PHP. Что и как? (22.07.2014)
- 0 MySQL: проверить содержится ли значение в поле столбца (в столбце хранится строка значений через запятую) (22.05.2014)
- 0 Yii: Статичные страницы (создание, редактирование, удаление) (28.04.2014)
- 0 Yii: Расширение ECKEditor = Связка ckeditor + kcfinder (визуальный редактор с бесплатным файловым менеджером) (28.04.2014)
- 0 Yii: Bootstrap tabs, делаем активной вкладку на которую выполняется переход по ссылке (20.03.2014)
- 0 Yii: Доступ к атрибуту модели из файла шаблона (Вызов в шаблоне какого-либо атрибута модели). (17.02.2014)
- 0 Yii: Множественный автокомплит с помощью CJuiAutoComplete (автокомплит нескольких значений в одно поле) (07.02.2014)
- 0 Yii: Фотогалерея через поведение (доработка расширения imagesgallerymanager) (08.03.2016)
- 0 Yii: Установка и настройка Yii-app заготовки приложения от Crisu83 (09.03.2014)
- 0 Twitter Bootstrap Carousel Crossfade (09.09.2013)
20 комментариев
У меня вот такая ошибка вылетает при http://домен/search/create! Help!
Warning: include(Place.php) [function.include]: failed to open stream: No such file or directory in W:\home\domen\www\yii-v1.1.9\framework\YiiBase.php on line 418
Warning: include() [function.include]: Failed opening 'Place.php' for inclusion (include_path='.;W:\home\domen\www\protected\vendors;W:\home\domen\www\protected\components;W:\home\domen\www\protected\models;/usr/local/php5/PEAR') in W:\home\domen\www\yii-v1.1.9\framework\YiiBase.phpon line 418
Fatal error: Class 'Place' not found in W:\home\domen\www\protected\controllers\SearchController.php on line 30
З.Ы. На денвере (на локальном сервере) лежи сайтик.
Аффтар, ты текст писал по памяти чтоли?
Написано:
Должно быть:
Здравствуйте. очень надеюсь на вашу помощь ибо сделал все что мог. проблема при создании индекса. Дело в том что локально у меня все работало, а как залил на сервер появилась такая пролема. При том что файлы индекса прекрасно создаются (права на папку 777). но во время их создания не рендерится надпись что индекс создан успешно, вместо этого 504 ошибка через некоторое время. Вот что мне выдается если после строки $index->addDocument($doc); вставить print_r($doc); exit;
Zend_Search_Lucene_Document Object ( [_fields:protected] => Array ( [short] => Zend_Search_Lucene_Field Object ( [name] => short [value] => Требуемый контент тут [isStored] => 1 [isIndexed] => 1 [isTokenized] => 1 [isBinary] => [storeTermVector] => [boost] => 1 [encoding] => UTF-8 ) [full] => Zend_Search_Lucene_Field Object ( [name] => full [value] => totally [isStored] => 1 [isIndexed] => 1 [isTokenized] => 1 [isBinary] => [storeTermVector] => [boost] => 1 [encoding] => UTF-8 ) ) [boost] => 1 )
Fatal error: Exception thrown without a stack frame in Unknown on line 0
заранее благодарю за помощь.
Прошел по ссылке на сайт http://whytravel.ru/ дабы проверить поиск. и на все мои запросы мне выдается ответ что:
Вводил слова: Германия, Дубай, ОАЭ и ....Поиск не дал результатов.
Так этот поиск работает или нет?
Беда!
Делаю все так же на yupe yii.
www\protected\vendors\Zend\Search\Lucene.php(538)Выдает ошибку:
В папке runtime/search создается 1 пустой файл read.lock.file
Что делать?
скачал с сайта Zend
но в указанном месте нет папки Search
2. Берём оттуда library/Zend/Search
её убрали из фреймворка?
Тут вопрос насчет пагинаци, можно решить с помоще CArrayDataProvider:
/* actionSearch*/
.........................................
if (($term = Yii::app()->getRequest()->getQuery('q')) !== null) {
$index = new Zend_Search_Lucene(Yii::getPathOfAlias('application.' . $this->_indexFiles));
$results = new CArrayDataProvider($index->find($term), array(
'id' => 'search',
'pagination' => array(
'pageVar' => 'page',
// 'pageSize'=>1,
),
'sort' => false,
));
$query = Zend_Search_Lucene_Search_QueryParser::parse($term);
$this->render('search', compact('results', 'term', 'query'));
и
/*views/search/search*/
......................................
<?php if ($results->itemCount): ?>
<?php foreach ($results->data as $result):
?>
<h2><?php echo CHtml::link(CHtml::encode($result->title), CHtml::encode($result->link)); ?></h2>
<p><?php echo $query->highlightMatches($result->content); ?></p>
<hr/>
<?php endforeach; ?>
<?php else: ?>
<div class="alert alert-error">Пошук не дав результатів.</div>
<?php endif; ?>
<div class="pagination pagination-centered">
<?php $this->widget('CLinkPager', array('pages' => $results->pagination)); ?>
</div>
Делаю все как написано. Уперся в проблему "include(Post.php): failed to open stream: No such file or directory"
Что я делаю не так?