IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Open source  >

理解 Zend 框架,第 7 部分: 搜索

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 中级

Tyler Anderson (tyleranderson5@yahoo.com), 工程师, Stexar Corp.

2006 年 10 月 26 日

继续 “理解 Zend 框架” 这一系列,上一次我们介绍了如何使用 Zend 框架在提要阅读器应用程序中发送电子邮件,现在您将使用 Zend 框架在提要阅读器应用程序中搜索已保存文章的标题和内容,并查看所得到的排列好的结果。

关于本系列

本系列按顺序记录了构建在线提要阅读器 Chomp 的过程,同时对使用近期引入的开放源码 PHP Zend 框架的所有主要方面进行了解释。

第 1 部分 中,我们探讨了 Zend 框架的全部概念,包括一系列相关类和对 MVC 模式的总体探讨。在 第 2 部分 中,我们详述了这部分内容以展示如何在 Zend 框架应用程序中实现 MVC 模式。我们还创建了用户注册和登录过程,将用户信息添加到数据库中并重新获取这些信息。

第 3 部分和第 4 部分探讨实际的 RSS 和 Atom 提要。在 第 3 部分 中,我们使用户能够订阅单独的提要并显示列于这些提要中的条目。还讨论了 Zend 框架的一些表单处理、验证数据及清除提要条目功能。而 第 4 部分 则解释了如何创建代理以从不含提要的站点中提取数据。

本系列余下的部分涉及到对 Chomp 应用程序进行增值。第 5 部分 中介绍了如何使用 Zend_PDF 模块,允许用户为已保存文章、图像及搜索结果创建一个定制的 PDF。在 第 6 部分 中,我们使用 Zend_Mail 模块提醒用户有新的文章。第 7 部分(本文)将探讨搜索已保存内容并返回排列好的结果。在第 8 部分中,我们将创建自己的混合体以添加 Amazon、Flickr 和 Yahoo! 中的信息。而在第 9 部分中,我们将使用 JavaScript 对象表示法为网站添加 Ajax 交互方式。





回页首


介绍

本文解释了如何使用 Zend_Search 模块针对一个特定的搜索词搜索当前已经存在的和已保存的日志条目,并返回排列好的结果。您将学到如下内容:

  • 如何使用 Zend_Search 模块和相关类来索引和搜索数据。
  • 如何使用 Zend_Search 模块执行不同类型的简单及高级搜索。

在本文的结尾,您将能够搜索保存在提要阅读器中的提要条目。首先,构建一个用于构建搜索索引并为该索引添加新内容的函数。接下来,创建两个提供搜索功能的行为:searchviewSearchResultssearch 行为提供一个执行搜索的表单,viewSearchResults 行为处理表单中的输入并将排列好的结果返回给您。





回页首


构建搜索索引

Zend 框架提供了一种出色的使用简便的搜索机制。这个搜索机制通过在一个不能通过网络访问的目录下创建索引来运行。随后能够为这个索引添加一些可搜索的子条目,并用多种方法来搜索这些索引过的条目。这就是本文所关注的,所以让我们先创建索引。

创建索引并为其添加条目

首先,我们需要一个为搜索索引创建新条目的辅助函数。然后创建这个索引(如果尚未存在的话)并把条目添加到其中。在 FeedController.php 的顶部创建这个辅助函数,如下所示。


清单 1. 创建搜索索引并把条目添加到其中

<?php
define('INDEX', 'c:\nonWWWAcessibleDirectory\myIndex');

function addEntryToSearchIndex($url, $contents, 
                               $feedname, $articletitle='')
{
    $doc = new Zend_Search_Lucene_Document();
        
    $doc->addField(Zend_Search_Lucene_Field::Text('url', $url));
    $doc->addField(Zend_Search_Lucene_Field::Text('feedname',
                                                  $feedname));
    if($articletitle != '')
        $doc->addField(Zend_Search_Lucene_Field::Text('articletitle', 
                                                      $articletitle));
    $doc->addField(Zend_Search_Lucene_Field::UnStored('contents', 
                                                      $contents));
        
    $newIndex = !is_dir(INDEX);
    $index = new Zend_Search_Lucene(INDEX, $newIndex);
    $index->addDocument($doc);
    $index->commit();
}

class FeedController extends Zend_Controller_Action
...

首先,定义这个不能通过网络访问的目录,此目录将包含搜索索引,即索引过的条目将会保存在这里。在这里,传递了文章的 URL、文章的内容、提要名称或 Web 页面名称以及文章的标题。然后,创建随后将添加到索引中的新文档,并将其保存到 $doc 变量中。接下来为它添加四个字段。第一个是 URL,将它作为 Text 存储。这意味着实际的 URL 将同该索引一起存储,并且当随后搜索该索引时能够获取它。这意味着如果条目相匹配,就能够获得这个 URL 并将其返回给用户。

接下来要存储的是 feedname 字段,它和 URL 的存储方式相同,并且如果定义了 articletitle,也需要将其存储到索引中。最终,将文章的内容存储为 UnStored 类型。这种类型意味着将以通常的方式对数据进行索引,数据不必同索引一起存储,所以不能在匹配的结果中获得该数据。这样就够了,因为我们只需要 URL、提要名称或 Web 页面名称以及文章标题(如果已经定义的话)。

最后,创建索引并将其存储在 $index 中。如果索引目录已经存在,就不需要创建一个新的索引了,否则需要创建一个新的索引(由 $newIndex 指定)。然后,将之前创建的搜索条目($doc)添加到索引中并提交,将这些更改保存到索引中。

是不是很酷?您现在已经学到了创建并将条目添加到索引中的手段。接下来,回到代码中将要调用此函数的地方。

为索引添加条目

既然已经创建了 addEntryToSearchIndex 函数,就可以开始将要搜索的条目添加到索引中了。在 FeedController 类的 saveEntryAction 方法中添加如下代码。


清单 2. 将条目添加到索引中

...
                echo 'Error occurred, full text not saved,'.
                     ' please reload.';
                return;
            }
        }

        addEntryToSearchIndex($channelLink,
                              Zend_Filter::noTags($fullText),
                              $feedTitle,
                              $channelTitle);

        $db = Zend::registry('db');
...

在这里,仅调用了新方法,传递了 URL($channelLink 变量)和对条目的描述或条目的全文。将 $fullText 字符串传递到 Zend_Filter::noTags 方法中,因而将移除掉所有的 HTML 标记(不需要对那些标记进行索引)。还传递了提要名称($feedTitle 变量)及文章标题($channelTitle 变量)。请注意,每一次在索引中保存提要时,不论提要是什么,该提要都会被保存到索引中。另一个您可能想探究的任务是确保不添加重复的条目。

那样就完成了本文的探讨。现在通过保存提要条目来为索引添加内容,如 第 5 部分 所示。在下一部分中,将开始在 FeedController 类中将创建的新行为中使用索引。





回页首


将新的搜索行为添加到 FeedController 中

您已经拥有了一个索引。现在是时候使用这个索引了。本文创建了两个新的行为方法:searchActionviewSearchResultsAction。第一个方法生成一个显示给用户的搜索页面,第二个方法根据从第一个方法中接收到的参数执行搜索,并将结果返回给用户。

searchAction 方法

在这里,将 searchAction 方法添加到 FeedController 类中,该类将 searchResults 视图显示给用户。使用下列方法完成上述任务。


清单 3. searchAction 方法

    public function searchAction()
    {
        $view = Zend::registry('view');
        $view->title = "Search Results";
        echo $view->render('searchResults.php');
    }

此方法仅仅将 $view 对象从注册库中提取出来(正如在本系列之前的几部分中所做的),并将其显示给用户。接下来,添加一个到主页的链接,该链接会将您带到提要阅读器的这一部分。





回页首


将一个搜索链接添加到主页中

还没有一个到达提要阅读器中 /feed/search 区域的方法,是吗?将下列链接添加到 viewFeeds.php 中的 viewFeeds 视图中,如下所示。


清单 4. 修改 viewFeeds 视图

...
  [<a href="feed/viewSavedEntries">View Saved Entries/
                                   Generate PDF</a>]<br>
  [<a href="feed/search">Search Saved Entries</a>]<br>
  <h1>CHOMP! The Feed Reader</h1>
...

这仅仅将搜索已保存条目的链接显示给了用户(参见图 1)。


图 1. 修改过的 viewFeeds 视图
修改过的 viewFeeds 视图

单击这个链接导致了一个错误,这是因为该搜索视图尚不存在。接下来就要创建这个视图。

搜索视图

这个视图允许用户输入针对索引的搜索。创建这个视图 searchResults.php,如下所示。


清单 5. 搜索视图

<html>
<head>
    <title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
  [<a href='/'>Back to Main Menu</a>]<br>
  <h1><?php echo $this->escape($this->title); ?></h1>
  
  <form method='GET' action='/feed/viewSearchResults'>
    Query: <input name='query'><br>
    Choose a field to search:<br>
    <input type='radio' name='field' value='raw' checked="yes">
        Raw String (allows fancy search types)<br>
    <input type='radio' name='field'
 value='contents'>Contents<br>
    <input type='radio' name='field' value='feedname'>Feed
 Title<br>
    <input type='radio' name='field' value='articletitle'>
        Article Title<br>
    Slop (not allowed for Raw String searches):
        <input name='slop' value='0'><br>
    <input type='Submit' value='Search'>
  </form>
</body>
</html>

这个页面允许进行不同种类的搜索,在下面的 viewSearchResultsAction 方法中了解到更多的内容。这个视图提供了一个表单,在表单中允许用户键入一个搜索字符串,还提供了一系列单选按钮,从而让用户能够搜索索引中的特定条目。请注意,该表单的方法是 GET,这是因为执行搜索没有副作用,所以 GET 在这里是安全的。

最后,提供了一个 slop 字段。slop 被定义为短语中字符串之间允许的间隔距离。因此如果 slop 的值为 0,短语为 "hey you",那么必须按照查询中定义的位置寻找这个短语。如果 slop 值为 1,那么 "hey ... you" 也是可接受的,其中 ... 被定义为单个单词。如果 slop 的值为 2,那么 "hey ... ... you""you hey" 都是可接受的。因而,slop 值越高,在短语中字符串的间隔就可以越远。这样就能够针对可接受的搜索结果配置 near 因子。

搜索视图如图 2 所示。


图 2. 搜索视图
搜索视图

接下来,看一下可用的搜索类型。

搜索类型

有几种搜索类型,我们只关注以下几种:

  • 以任何 slop 值在一个字段中搜索任何短语
  • 使用一个原始字符串查询的高级搜索:
    • 通过提供由空格分隔的词组成的字符串来搜索短语,如:"hey you"
    • 搜索一些词,而且不包括另外一些词,如:"+hey -you"(确保文档包含 "hey"不包含 "you"
    • 搜索上述两项之一,但也可以为单词指定字段,如:"Hey -you feedname:Google"

用上述类型的高级查询进行搜索对于高级 Google 使用者来说是小菜一碟,而且任何人通过练习都能对此有所掌握。接下来,添加 viewSearchResultsAction 方法。

viewSearchResultsAction 方法

此方法执行搜索并将结果返回给用户。在 FeedController 类中创建 viewSearchResultsAction 方法,如下所示。


清单 6. viewSearchResultsAction 方法

    public function viewSearchResultsAction()
    {
        $filterGet = Zend::registry('fGet');
        $query = strtolower($filterGet->getRaw('query'));
        $slop = $filterGet->getRaw('slop');
        $field = $filterGet->getRaw('field');
        
        if($field != "raw" | $query == ''){
            $queryObj = new
                Zend_Search_Lucene_Search_Query_Phrase(explode(" ", 
                                                               $query),
                                                       null, $field);
            $queryObj->setSlop($slop);
        }
        else $queryObj = $query;

        $newIndex = !is_dir(INDEX);
        $index = new Zend_Search_Lucene(INDEX, $newIndex);
        $hits = $index->find($queryObj);

        $view = Zend::registry('view');
        $view->title = "Search Results for: $query";
        $view->hits = $hits;
        echo $view->render('viewSearchResults.php');
    }

此方法从 GET 数组中获取 $query$slop$field。如果 $field 不是 "raw",或未输入 $query,那么将创建一个特别的 Zend_Search_Lucene_Search_Query_Phrase 构造,将其存储在 $queryObj 中,并将 slop 值设为 $slop 中的值(这允许进行在前面展示过的第一种查询类型)。否则,需要将 $queryObj 设为 $query,即原始的搜索字符串(也允许使用更高级的查询类型)。然后通过调用 $index->find($queryObj) 抓取该索引并获取相匹配的结果,并将它们存储在 $hits 中。最后,创建 viewSearchResults 视图并将其显示给用户。接下来,我们将看到这个视图是如何显示结果的。

viewSearchResults 视图

这个视图遍历返回给它的结果并将它们显示给用户。在一个名为 viewSearchResults.php 的文件中创建这个视图,并对其进行定义,如下所示。


清单 7. viewSearchResults 视图

<html>
<head>
    <title><?php echo $this->escape($this->title); ?></title>
</head>
<body>
  [<a href='/'>Back to Main Menu</a>]<br>
  <h1><?php echo $this->escape($this->title); ?></h1>
  
  <table>
    <tr>
      <td></td>
      <td>Title (Click to view article)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
      <td>Relevancy</td>
    </tr>
  <?php
     $i = 1;
     foreach ($this->hits as $hit) {
         $score = $hit->score;
         $feedTitle = $hit->feedname;
         $channelTitle = $hit->articletitle;
         $url = $hit->url;

         $title = $feedTitle;
         if($channelTitle != '')
             $title = "$title > $channelTitle";
         echo "<tr><td>#" . $i++ . ":</td>";
         echo "<td><a href=\"$url\">$title</a></td>";
         echo "<td>$score</td></tr>";
     }
  ?>
  </table>
</body>
</html>

这个视图遍历从 viewSearchResultsAction 方法中发送给它的每个命中结果。每个相匹配的命中结果都以排列好的次序返回,第一个结果具有最高的相关性,并按照它们的得分存储。在这里,从每个 $hit 中抓取了 $score$feedTitle$channelTitle$url,并将它们返回给用户,同时提供了一个链接,用户可以从这里查看全文(参见图 3)。


图 3. viewSearchResults 视图
viewSearchResults 视图

的确很酷,不是吗?这样就可以了。我们的提要阅读器现在有了搜索功能。





回页首


结束语

很出色!Zend 框架中的 Zend_Search 类允许搜索提要阅读器中的已保存条目,在 “理解 Zend 框架” 系列的第 7 部分中您掌握了这个类。

本系列余下的部分涉及为 Chomp 应用程序添加更多的价值。在第 8 部分中,将使用 JavaScript 对象表示法为网站添加 Ajax 交互方式。最后,在第 9 部分中,将创建您自己的混合体,添加 Amazon、Flickr 和 Yahoo 中的信息。






回页首


下载

描述名字大小下载方法
Part 7 source codeos-php-zend7.source.zip9KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术

讨论


关于作者

Tyler Anderson 于 2004 年取得了 Brigham Young University 的计算机科学学位,2005 年 12 月又取得了同一所学校的计算机工程学硕士学位。过去,他在 DPMG.com 公司作过数据库程序员,目前他在位于 Beaverton, Ore 的 Stexar Corp. 公司担任工程师。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款