2012-07-28 67 views
2

我有一個Symfony項目,我使用Zend Lucene搜索框架在網站上集成搜索。它工作得很好,但它僅限於搜索1個表格。Symfony Zend Lucene搜索多個表

我需要我的用戶能夠搜索整個站點(8個選擇表)並將結果一起返回。每個表都具有相同的索引字段。這是指定表並調用查詢的代碼。

有沒有一種方法可以讓所有8個表格查找結果?

public function getForLuceneQuery($query) 
{ 
    $hits = self::getLuceneIndex()->find($query); 

    $pks = array(); 
    foreach ($hits as $hit) 
    { 
    $pks[] = $hit->pk; 
    } 

    if (empty($pks)) 
    { 
    return array(); 
    } 
    $alltables = Doctrine_Core::getTable('Car'); 
    $q = $alltables->createQuery('j') 
    ->whereIn('j.token', $pks) 
    ->orderBy('j.endtime ASC') 
    ->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time())) 
    ->andWhere('j.activated = ?', '1') 
    ->limit(21); 

    return $q->execute(); 
} 

舉個位上的8臺的背景,他們都基本相似。他們都有標題,製作,模型等,所以我需要對它們全部運行一個查詢,並按升序返回所有結果(不管它在哪個表中)。 Doctrine_core :: getTable命令似乎不喜歡多個表或甚至數組(除非我沒有做對)。謝謝!

更新(工作):

這裏是更新的代碼。這是我在SearchTable.class.php文件:

public function getForLuceneQuery($query) 
{ 
    // sort search result by end time 
    $hits = self::getLuceneIndex()->find(
    $query, 'endtime', SORT_NUMERIC, SORT_ASC 
); 

    $result = array(
    'index' => $hits, 
    'database' => array(), 
); 

    // group search result by class 
    foreach ($hits as $hit) 
    { 
    if (!isset($result['database'][$hit->class])) 
    { 
     $result['database'][$hit->class] = array(); 
    } 

    $result['database'][$hit->class][] = $hit->pk; 
    } 

    // replace primary keys with real results 
    foreach ($result['database'] as $class => $pks) 
    { 
    $result['database'][$class] = Doctrine_Query::create() 
     // important to INDEXBY the same field as $hit->pk 
     ->from($class . ' j INDEXBY j.token') 
     ->whereIn('j.token', $pks) 
     ->orderBy('j.endtime ASC') 
     ->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time())) 
     ->andWhere('j.activated = ?', '1') 
     ->limit(21) 
     ->execute(); 

    } 

    return $result; 
} 

以下是我在actions.class.php文件的搜索模塊:

public function executeIndex(sfWebRequest $request) 
    { 
    $this->forwardUnless($query = $request->getParameter('query'), 'home', 'index'); 

    $this->results = Doctrine_Core::getTable('Search') 
     ->getForLuceneQuery($query); 

    } 

最後,這是我的模板文件indexSuccess.php我已經簡化它所以它更容易理解。我的indexSuccess.php比較複雜,但現在我可以調用這些值,我可以進一步對其進行自定義。

<div class="product_list" 
    <ul> 
    <?php foreach ($results['index'] as $hit): ?> 
     <li class="item"> 
     <?php if (isset($results['database'][$hit->class][$hit->pk])) ?> 
     <span class="title"> 
      <?php echo $results['database'][$hit->class][$hit->pk]->getTitle() ?> 
     </span> 
     </li> 
    <?php endforeach ?> 
    </ul> 
    </div> 

這工作很好。我可以通過調用搜索結果中的每個字段來定製它,並且它非常完美。我爲每個具有相同標題的表添加了一個項目,並且搜索結果將它們全部拉出。非常感謝!

+0

我認爲這不能用1個查詢來完成。您應該將模型類名稱存儲在索引中,然後在搜索之後,可以將匹配的主鍵組合到像array('model class name'=> array(pk))這樣的數組中,並對每個匹配的表執行查詢。在查詢索引和顯示結果時可以進行排序。您可以迭代原始結果集並從查詢結果中選擇適當的記錄。但我不知道如何分頁。或者,您可以將所有需要顯示的數據存儲在索引中,並僅使用該數據而不查詢數據庫。 – 1ed 2012-07-30 21:38:48

+0

我可以執行多個查詢(每個表上有一個查詢),然後加入結果,按升序對它們進行排序,然後顯示它們?我不確定這個代碼會是什麼樣子。對不起,我對Symfony有點新鮮。謝謝。 – djcloud23 2012-07-31 05:09:30

+0

當然,您可以將結果縮短爲一個「Doctrine_Collection」,但我認爲這會造成太大的開銷。我剛剛發佈了我以前的想法作爲答案,並添加了一些代碼。 – 1ed 2012-07-31 21:46:50

回答

2

好的。我會盡力給你一些提示,用代碼:)

首先,你應該將這些字段添加到索引:

$doc->addField(Zend_Search_Lucene_Field::Keyword('class', get_class($record))); 
$doc->addField(Zend_Search_Lucene_Field::UnIndexed('endtime', strtotime($record->get('endtime')))); 

應該比你使用這些新的領域:

public function getForLuceneQuery($query) 
{ 
    // sort search result by end time 
    $hits = self::getLuceneIndex()->find(
    $query, 'endtime', SORT_NUMERIC, SORT_ASC 
); 

    $result = array(
    'index' => $hits, 
    'database' => array(), 
); 

    // group search result by class 
    foreach ($hits as $hit) 
    { 
    if (!isset($result['database'][$hit->class])) 
    { 
     $result['database'][$hit->class] = array(); 
    } 

    $result['database'][$hit->class][] = $hit->pk; 
    } 

    // replace primary keys with real results 
    foreach ($result['database'] as $class => $pks) 
    { 
    $result['database'][$class] = Doctrine_Query::create() 
     // important to INDEXBY the same field as $hit->pk 
     ->from($class . ' j INDEXBY j.token') 
     ->whereIn('j.token', $pks) 
     ->orderBy('j.endtime ASC') 
     ->andwhere('j.endtime > ?', date('Y-m-d H:i:s', time())) 
     ->andWhere('j.activated = ?', '1') 
     ->limit(21) 
     ->execute(); 

    // if you want different query per table 
    // you should call a function which executes the query 
    // 
    // if (!method_exists($table = Doctrine_Core::getTable($class), 'getLuceneSearchResult')) 
    // { 
    // throw new RuntimeException(sprintf('"%s::%s" have to be exists to get the search results.', get_class($table), 'getLuceneSearchResult')); 
    // } 
    // 
    // $results[$class] = call_user_func(array($table, 'getLuceneSearchResult'), $pks); 
    } 

    return $result; 
} 

後,在模板中,你應該遍歷$result['index']並顯示結果從$result['database']

foreach ($result['index'] as $hit) 
{ 
    if (isset($result['database'][$hit->class][$hit->pk])) 
    { 
    echo $result['database'][$hit->class][$hit->pk]; 
    } 
} 

而且有相同的備用(也許更好),我能想到的解決方案:

替代的解決方案#1:

可以存儲在索引數據,並將這些數據將在搜索訪問結果。如果您在顯示結果時不需要太多數據,並且可以頻繁更新索引,我認爲這是一個不錯的選擇。這樣你就可以使用分頁和根本不需要SQL查詢。

$doc->addField(Zend_Search_Lucene_Field::Text('title', $content->get('title'))); 
... 
$hit->title; 

替代的解決方案#2:

正如你寫,你的表是非常相似的,所以你也許可以使用column aggregation inheritance。通過這種方式,所有數據都存儲在一個表中,因此您可以將它們全部查詢並按需要進行排序和分頁。

+0

我認爲我更喜歡Alternate解決方案#1。開銷越少越好。但是,如何從模板中調用值。我是否將每個變量分配給一個變量?我試圖調用$ hit-> title;從模板,它說變量「命中」沒有定義。再次感謝你的幫助。 – djcloud23 2012-08-01 21:29:46

+0

你應該在'$ this-> hits = YourCustomClass :: getLuceneIndex($ query,...);'這樣的情況下'$ this-> hits'在一個動作中查詢索引。 hits'。它是一個'Zend_Search_Lucene_Search_QueryHit'對象的數組,你應該在模板foreach($ hits as $ hit)中遍歷這個數組{$ echo $ hit-> title; }' – 1ed 2012-08-01 22:15:38

+0

對不起,我意識到替代解決方案#1將我不想搜索的內容編入索引。我想盡可能保持索引儘可能乾淨,所以我嘗試了你的原始答案。在這樣做的時候,似乎查詢運行正常,但是當我在模板中調用'$ result-> getPicture1()'時,它說'致命錯誤:調用未定義的方法sfOutputEscaperArrayDecorator :: getpicture1()'哪一個是未在我的表中的索引值。如果您需要查看,我可以用代碼更新我的原始問題。 – djcloud23 2012-08-02 02:57:46