代码之家  ›  专栏  ›  技术社区  ›  GoRivera

Symfony Zend-Lucene搜索多个表

  •  2
  • GoRivera  · 技术社区  · 12 年前

    我有一个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张表的背景,它们基本上都是相似的。它们都有title、make、model等,所以我需要对它们运行一个查询,并按升序返回所有结果(无论它在哪个表中)。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 我把它简化了,这样更容易理解。我的 索引成功.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>
    

    这个效果很好。我可以通过调用搜索结果中的每个字段来定制它,它非常完美。我在每个表中添加了一个具有相同标题的项目,搜索结果将它们全部提取出来。非常感谢!

    1 回复  |  直到 12 年前
        1
  •  2
  •   1ed    12 年前

    好的。我试着给你一些提示,代码是:)

    首先,您应该将这些字段添加到索引中:

    $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 。通过这种方式,所有数据都存储在一个表中,这样您就可以一起查询它们,并可以根据需要排序和分页。