Програмиране на Zend Lucene търсачка в Симфони проекти

Symfony и Lucene - уеб сайт вътрешна търсачка
В тази статия ще ви запозная с лесен начин за разработване на търсачката Zend lucene в уеб сайтове с symfony 1.4 Doctrine ORM.
Ще представя нещата малко по-различно от както са направени в оригиналната документация на Symfony. Ще се опитам, по скоро да обясня принципа на имплементиране на търсачката, а не да предоставя код, който чрез просто копиране и после поставяне да бъде напълно работещ. Просто има неща, който са строго специфични и индивидуални във всеки проект и е необходимо да се схване принципа според дадената ситуация.
След като се научите да вкарвате тази функционалност в системата, тогава идва ред на елементи като autocompleter, advanced search и тн. Те няма да бъдат обект на тази статия.
Първоначално Трябва да изтеглите Zend Framework. Можете да го направите от тук. След това поставете Zend Framework в следната директория:
Изработка на уеб сайтове с Zend Lucene in Symfony Projects
Когато тествате на локален хост, не е проблем да оставите всички файлове на Zend Framework, но при качване на сървър, можете да оставите само следните файлове и папки от Zend:


• Exception.php
• Loader/
• Autoloader.php
• Search/

//exams/config/ProjectConfiguration.class.php
Сега трябва да добавим следните редове в ProjectConfiguration.class.php
Програмиране на Zend Lucene в Symfony Projects

static protected $zendLoaded = false;
static public function registerZend() {
   if (self::$zendLoaded) {
      return;
   }
   set_include_path(sfConfig::get('sf_lib_dir').'/vendor'.PATH_SEPARATOR.get_include_  path());
   require_once sfConfig::get('sf_lib_dir').'/vendor/Zend/Loader/Autoloader.php';

   Zend_Loader_Autoloader::getInstance();
   self::$zendLoaded = true;
   Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());

   ini_set("iconv.internal_encoding", 'UTF-8');
   Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('utf-8');
}

Ако сте чели урока за интегриране на Zend lucene в официалната документация на Symfony Framework, ще забележите, че този код има известни разлики с дадения там пример. Разликите се състоят в това, че чрез допълнителните редове ние добавяме възможността търсачката да работи и с кирилица.
Следващата стъпка е да си изберем таблица от нашата база данни, която ще обхождаме. В примера таблицата е “test“. В системата тя е представена от два класа. Необходимо е сами да се ориентирате за някои неща, които показвам, защото във вашите проекти имената на класовете например ще са различни. Но веднъж схванете ли техниката, няма да е проблем за вас.
Zend Lucene в Symfony уеб проекти
В testTable.class.php въвеждаме следната финкция:

static public function getLuceneIndex() {
   ProjectConfiguration::registerZend();
   if (file_exists($index = self::getLuceneIndexFile())) {
      return Zend_Search_Lucene::open($index);
   } else {
      return Zend_Search_Lucene::create($index);
   }
}
static public function getLuceneIndexFile() {
   return sfConfig::get('sf_data_dir').'/exams.'.sfConfig::get('sf_environment').'.index';
}

С тази функция се създава папка, която изглежда по следния начин:
Symfony уеб проекти и Zend Lucene търсачка
В нея се съхраняват индексите на въведените от вас данни, които се извикват за проверка при търсене. Естествено в последният ред:

return sfConfig::get('sf_data_dir').'/exams.'.sfConfig::get('sf_environment').'.index';

Можете да смените името на папката с каквото пожелаете. Сега е необходимо да направим още две неща:
Първото е да променим функцията save(), в test.class.php

public function save(Doctrine_Connection $conn = null) {

   // ...

   $ret = parent::save($conn);
   $this->updateLuceneIndex();
   return $ret;
}

И след това да създадем следните две функции в същата таблица:

public function delete(Doctrine_Connection $conn = null) {
   $index = testsTable::getLuceneIndex();

   foreach ($index->find('pk:'.$this->getId()) as $hit) {
      $index->delete($hit->id);
   }
   return parent::delete($conn);
}

public function updateLuceneIndex() {
   $index = testsTable::getLuceneIndex();

   // Премахване на съществуващият запис
   foreach ($index->find('pk:'.$this->getId()) as $hit) {
      $index->delete($hit->id);
   }
   $doc = new Zend_Search_Lucene_Document();

   // Съхраняваме първичният ключ на елемента за проверка при търсене
   $doc->addField(Zend_Search_Lucene_Field::Keyword('pk', $this->getId()));

   // Индексираме поле
   $doc->addField(Zend_Search_Lucene_Field::UnStored('name', $this->getName(), 'utf-8'));
   //$doc->addField(Zend_Search_Lucene_Field::UnStored('description', $this->getDescription(), 'utf-8'));

   $index->addDocument($doc);
   $index->commit();
}

В първата функция няма какво да променяме. При втората, полетата:

$doc->addField(Zend_Search_Lucene_Field::UnStored('name', $this->getName(), 'utf-8'));
//$doc->addField(Zend_Search_Lucene_Field::UnStored('description', $this->getDescription(), 'utf-8'));

чиито стйности се индексират и при търсене се показват.
Това беше първата част от имплементирането в нашета система. Сега в CMD въвеждате:
Symfony уеб сайтове и Zend Lucene търсачка
Сега идва ред на създаването на маршрут.
Разработване на Symfony уеб сайтове с мощна вътрешна търсачка
В routing.yml създаваме сления път.

search:
url: /search
param: { module: tests, action: search }

В този случай, модула в който ще е нашата търсачка е “tests“. Темплейта е search.
Контролера би трябвало да изглежда по следния начин:

public function executeSearch(sfWebRequest $request) {
   //проверяваме дали имаме post и дали този post ни е изпратил параметър query
   if ($request->isMethod('post') || $request->getParameter('query')) {
      $query = $request->getParameter('query');
      //проверяваме дали заявката за търсене е по голяма или равна на 3 символа

      if (strlen($query)>=3) {
         //ако е >=3 символа изпращаме запитване и извличаме резултатите в променливата query
         $this->query = Doctrine_Core::getTable('tests')->getForLuceneQuery($query);
      }
   } else {
      //ако условието за post не е изпълнено тогава извеждаме текст : няма резултат
      $this->renderText('No results');
   }
}

Във searchSuccess.php трябва да развием примерно следния код:

if (isset($query) && count($query) != 0) {
   foreach($query as $tests):
      echo $tests--->getCategories()->getName();
   endforeach;
} еlse {
   еcho “no results”;
}

В if израза просто проверяваме дали има подадена променлива $query и дали тя съдържа резултат. След това с foreach извеждаме резултата. В примера полето е само едно, но в нормална ситуация $test съдържа повече от една стойност (име , категория и тн…). Естествено с css & html оформяме резултата по начина по който искаме.
//your_project/lib/model/doctrine/testTable.class.php
Сега отиваме отново в класа testTable.class.php и добавяме следната функция:

public function getForLuceneQuery($query) {
   $hits = self::getLuceneIndex()->find($query);
   $pks = array();
   foreach ($hits as $hit) {
      $pks[] = $hit->pk;
   }
   if (empty($pks)) {
      return array();
   }
   $q = $this->createQuery('t')
             ->where('t.active = ?', 1)
             ->whereIn('t.id', $pks)
             ->limit(3);

   return $q->execute();
}

Може да погледнете във вашия контролер и да видите, че тази функция се извиква след като сме въвели дума за търсене.
В частта:

$q = $this->createQuery('t')
          ->where('t.active = ?', 1)
          ->whereIn('t.id', $pks)
          ->limit(3);

return $q->execute();

примерната заявка която правя, проверява дали дадения елемент от таблицата е активен и слагам лимит 3. Така при съвпадение, търсенето ще изведе само 3 резултата. Разбира се можете да създадете собствени заявки, които отговарят на изискванията и нуждите на вашата система.
Сега е ред на последната част, а именно формата. За да е постоянно видима формата във вашия уеб сайт, ще въведем кода необходим за нея във файла layout.php
Zend Lucene в уебсайтове
В контролера на формата посочваме, че след submit запитването се изпраща на контролера за търсене, който създадохме. В него те минават през функцията getForLuceneQuery($query) и ако има резултат от запитването той се извежда чрез темплейта searchSuccess.php.

Надявам се тази статия да е полезна за разработването на вашите уеб проекти. Дори да не направите нещата от първия път, не се отчайвайте. Нищо не е прекалено лесно, и за всяко нещо човек трябва да се потруди. Моят съвет е да разгледате и начина за програмиране в официалния сайт на Symfony Framework в проекта “jobeet” и най-важното нещо е да не спирате да тествате.

One thought on “Програмиране на Zend Lucene търсачка в Симфони проекти

Вашият коментар

Вашият email адрес няма да бъде публикуван Задължителните полета са отбелязани с *