Zend Lucene和PDF文档第4部分:搜索

上一次,我们为PDF文档建立了索引,并准备向我们的应用程序添加搜索表单。添加搜索需要两件事,一个是输入搜索词的表单,另一个是控制提交表单时发生的操作。

最简单的任务是设置搜索视图,创建视图文件search.phtml(在views / scripts / search /中)并添加以下代码。这只是具有单个文本输入的基本形式。请注意,添加了queryString参数,这将使我们能够在搜索框中打印出最后一次搜索到的值,这是一种良好的可用性做法。

<form action="" method="get" name="searchForm">
    <input type="text" name="q" id="q" value="<?php echo $this->queryString; ?>" />
    <input type="submit" name="search" value="Search" />
</form>

设置要执行的搜索操作的方式有很多,这取决于您打算如何执行。最简单的方法是将查询字符串传递给Lucene索引find()方法。我在这里选择的方法使用Zend_Search_Lucene_Search_Query_Boolean类创建一个多词查询,其中每个词都必须存在,不存在或都不存在。尽管起初我们只想传递一个参数,但是此方法使我们将来可以轻松添加搜索字词,而不必大惊小怪。这是完整的搜索操作。

public function searchAction()
{
    $filters = array('q' => array('StringTrim', 'StripTags'));
    $validators = array('q' => array('presence' => 'required'));
    $input = new Zend_Filter_Input($filters, $validators, $_GET);
 
    if (is_string($this->_request->getParam('q'))) {
        $queryString = $input->getEscaped('q');
        $this->view->queryString = $queryString;
 
        if ($input->isValid()) {
            $config = Zend_Registry::get('config');
            $index = App_Search_Lucene::open($config->luceneIndex);
 
            $query = new Zend_Search_Lucene_Search_Query_Boolean();
 
            $pathTerm = new Zend_Search_Lucene_Index_Term($queryString);
            $pathQuery = new Zend_Search_Lucene_Search_Query_Term($pathTerm);
            $query->addSubquery($pathQuery, true);
 
            try {
                $hits = $index->find($query);
            } catch (Zend_Search_Lucene_Exception $ex) {
                $hits = array();
            }
 
            $this->view->hits = $hits;
        } else {
            $this->view->messages = $input->getMessages();
        }
    }
}

这是行动中正在发生的事情。

  • 创建一个过滤器和验证器,使我们可以确保传入的搜索符合某些规则。这也使我们可以过滤传入的文本,以防止使用任何黑客手段。

  • 因为我们不想在第一次访问页面时运行验证器,所以请确保查询为字符串。

  • 验证查询字符串,并将发现的任何错误发送到视图。

  • 获取配置对象并打开lucene索引。

  • 初始化Zend_Search_Lucene_Search_Query_Boolean对象。

  • 使用该addSubquery()方法,创建我们的搜索词并将其作为子查询添加到我们的查询中。

  • 尝试运行查询并将结果作为Zend_Search_Lucene_Search_QueryHit对象的数组返回。如果搜索中发生错误,则将$hits数组设置为空白。

  • 将$hits数组发送到视图,以便我们可以将其打印出来。

$hits数组中的Zend_Search_Lucene_Search_QueryHit对象可用于获取我们在索引阶段添加的所有信息,以及一些其他信息,例如搜索中文档的分数。下一步是将错误消息和搜索结果添加到search.phtml文件中。添加消息非常简单,但是添加搜索结果可能很复杂,尤其是因为并非我们索引中的所有PDF都具有完全相同的字段。这可能是因为我们尚未填写它们,或者我们正在索引第三方的PDF,因此可能不包含一些已添加到文档中的元标记。因此,在尝试将其打印出来之前,我们需要确保该字段存在,否则将发生异常。

这是search.phtml完整的文件。请注意,由于它旨在适应您的任何需求,因此它几乎不包含任何格式。

<form action="" method="get" name="searchForm">
    <input type="text" name="q" id="q" value="<?php echo $this->queryString; ?>" />
    <input type="submit" name="search" value="Search" />
</form>
<?php
 
if (!is_null($this->hits)) {
    echo count($this->hits) . ' results found.<hr />';
 
    foreach ($this->hits as $hit) {
        $fields = $hit->getDocument()->getFieldNames();
        echo 'id: '.$hit->id . '<br />';
        echo 'score: '.$hit->score . '<br />';
 
        echo 'Filename: '.$hit->getDocument()->Filename . '<br />';
        echo 'Key: '.$hit->getDocument()->Key . '<br />';
 
        if (in_array('Title', $fields)) {
            echo 'Title: '.$hit->getDocument()->Title . '<br />';
        }
 
        if (in_array('CreationDate', $fields)) {
            echo 'CreationDate: '.$hit->getDocument()->CreationDate . '<br />';
        }
 
        if (in_array('Author', $fields)) {
            echo 'Author: '.$hit->getDocument()->Author . '<br />';
        }
        echo '<hr />';
    }
}

我认为对该程序的一个很好的补充是将另一个搜索词添加到$query对象中,并将搜索结果限制在特定的一天。我没有使用时间戳作为该值,而是更改了App_Search_Lucene_Index_Pdfs类,以便在存储CreationDate时将其存储为yyyymmdd值。将App_Search_Lucene_Index_Pdfs类中的相应行(第66行左右)更改为以下内容。

$indexValues['CreationDate'] = date('Ymd', $dateCreated);

接下来,我在搜索操作中向$query对象添加了另一个搜索词。我已经将时间硬编码到了本节中,但是向搜索表单中添加控件以及新的验证和过滤规则应该并不难。

$pathTerm  = new Zend_Search_Lucene_Index_Term('20091023', 'CreationDate');
$pathQuery = new Zend_Search_Lucene_Search_Query_Term($pathTerm);
$query->addSubquery($pathQuery, true);

但是,添加完所有这些后,它似乎无法正常工作。数字的增加完全不会影响搜索结果。这是因为Zend_Search_Lucene使用分析器来分析和处理每个索引字段的文本。默认的文本分析器是Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive,它将仅对小写字母进行索引,这意味着在对文档进行索引时,我们的时间字段将被完全忽略。这具有使完全不可能搜索数字值的连锁效应。

为了解决这个问题,我们需要使用不同的分析器来处理字段,以便在索引中使用数字和字母。要设置新的分析器,您需要使用setDefault()Zend_Search_Lucene_Analysis_Analyzer类的静态方法,并将其传递给文本分析器对象。可以使用以下代码完成此操作。

Zend_Search_Lucene_Analysis_Analyzer::setDefault(
  new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive());

添加文档后即完成分析,因此可以通过addDocument()方法建立索引,以便setDefault()可以在此之前的任何位置添加对方法的调用。可以在App_Search_Lucene_Index_Pdfs类或App_Search_Lucene_Document类中将以上代码添加到此应用程序中。我已经尝试了每种代码,并且可以在任何位置使用,但是我认为最好的位置可能是App_Search_Lucene_Document类。这是因为将其作为从不同格式创建文档的标准是有意义的,而不是每次添加新格式时都必须将其添加到App_Search_Lucene_Index类中。

将其添加到您的代码后,您将需要重新索引文档,然后数字搜索才能起作用。

在下一篇(也是最后一篇)有关Zend Lucene和PDF文档的文章中,我将在代码中添加一个观察者,以便我们不必在每次对任何文档进行更改时都保持对整个文件目录的重新索引。我还将提供完整的源代码以供下载。

  • Zend Lucene和PDF文档第1部分:PDF元数据

  • Zend Lucene和PDF文档第2部分:PDF数据提取

  • Zend Lucene和PDF文档第3部分:为文档建立索引

  • Zend Lucene和PDF文档第4部分:搜索

  • Zend Lucene和PDF文档第5部分:结论