• Zend Framework 1.10.1 开始使用 Zend_Search_Lucene


    开始使用 Zend_Search_Lucene
    目录

    •介绍 Zend_Search_Lucene
    •Lucene 索引结构
    •索引开始和创建
    •索引
    •搜索
    •被支持的查询
    •搜索结果分页
    返回目录

    介绍 Zend_Search_Lucene
    Zend_Search_Lucene 组件的目的是提供一个即可使用的全文搜索解决方案。它不要求任何 PHP 扩展 UTF-8mbstring 或者安装额外的软件,在 Zend_Framework 安装以后立刻能被使用。

    Zend_Search_Lucene 是流行开源全文搜索引擎 Apache Lucene 的一个纯 PHP 端口。查看 http://lucene.apache.org 获得更多信息。

    为了可以搜索,信息必须被建立索引。Zend_Search_Lucene 和 Java Lucene 使用一个文件概念,称之为 atomic indexing item。

    每一个文件是一套字段:<name, value> 一对,name 和 value 是 UTF-8 字符串。文件字段的任何子集可以被标记为 indexed 来包括在建立文字索引的过程中的字段数据。

    当建立索引的时候,字段值可以或不必被标记。如果一个字段不被标记,字段值以一个词组保存;否则,当前的分析器被用作标记。

    在 Zend_Search_Lucene 包中提供了几个分析器。默认的分析器工作在 ASCII 文字(因为 UTF-8 分析器需要打开 mbstring 扩展)。它是大小写敏感,而且它会忽略数字。如果你需要改变这个行为,可以使用其它的分析器或者创建你自己的分析器。

    注意:在索引和搜索的过程中使用分析器
    重要提示!搜索语句同样使用当前的分析器标记,所以在索引和搜索过程中必须设置相同的默认分析器。这将保证源文件和搜索文字以同样的方式转化成词组。

    字段值可选的被保存在一个索引内。这允许当搜索的时候,原始的字段数据可以从索引中被检索。这个是把搜索结果和原始数据关联起来的唯一方法(在索引优化或者自动优化以后,内部的文件 ID 可能被改变)。

    要记住的是,Lucene 索引不是一个数据库。除了备份文件系统目录,它不提供索引备份机制。它不提供事务缓存(transactional)机制,虽然迸发索引更新和迸发更新和读取被支持。在数据检索速度上它比不上数据库。

    所以好的主意:

    •不要把 Lucene 索引当储存器使用,因为它可能戏剧性的降低搜索的命中率(search hit retrieving performance)。只保存独一无二的文件标识符(文件路径,URL,数据库唯一 ID)和在一个索引内的相关数据。比如,标题,注解,目录,语言信息,化身。(注意:一个字段可能被包括在索引过程,但不被储存,或者被储存,但不被索引)。
    •如果因为某种原因它被破坏,写下功能可能完整的重建一个索引。
    在索引中的独立的文件,可能有完全不同的字段组合。不同文件中的同样的字段不必拥有相同的属性。例如,一个字段可以为一个文件被索引,同时被其它文件的索引忽略。存储,标识,或者把字段值当作一个二进制字符串处理,也是如此。

    返回目录

    Lucene 索引结构
    为了全面的使用 Zend_Search_Lucene 的最大功能,你应该了解它的内置索引结构

    一个索引以一套文件的形式被保存在一个单独的目录中。

    一个索引由数量众多的独立片断组成,这些片断储存有信息,这些信息是关于被索引文件的一个子集。每一个片断有它自己的词组字典,词组字典索引,和文件存储(保存字段值)Zend_Search_Lucene。所有的片断数据保存在 _xxxxx.cfs 文件中,xxxxx 是片断的名字。

    一旦一个索引片断文件被创建,它不能被更新。新的文件被加到新的片断中。删除的文件只被以 deleted 标记在一个可选择的 <segmentname>.del 文件中。

    文件更新将会以删除和添加这两个独立的操作来完成,虽然它是通过调用 update() API Zend_Search_Lucene API 做到的。这简化了添加新文件,并允许和搜索操作同时更新。

    在另外一方面,使用几个片断()增加了搜索时间:

    •如果词组字典到达了一个饱合点,大多数情况下,那么搜索一个片断 N 次将比搜索 N 个片断的速度要快。
    •索引优化把两个或者更多的片断合并进一个单独的新片断。一个新的片断被添加到索引片断列表,旧的片断被排除在外。
    •片断列表更新的过程如同一个原子操作。这样可以同时添加新的文件,进行索引优化,和在索引内搜索。
    •索引自动优化在每一个新的片断产生以后进行。它把最小的片断合并进大的片断,然后大的片断合并到更大的片断,如果我们有足够的片断来合并的话。
    索引自动优化被这三个选项控制:

    MaxBufferedDocs(在缓冲内存中文件被写入到一个新的片断之前,要求的文件的最小数量)

    MaxMergeDocs(被一个优化操作合并的文件的最多的数量)

    MergeFactor(用来决定由自动优化操作合并的片断标记体的频繁程度)。

    如果每执行一个脚本我们添加一个文件,那么 MaxBufferedDocs 事实上没有被使用(在脚本执行结束的时候只有一个新片断和一个文件被创建,这时自动优化进程开始)。

    返回目录

    打开和创建索引
    全部的索引操作(例如,创建一个新的索引,给索引添加一个文件,删除一个文件,搜索整个索引)需要一个索引对象。可以使用以下两种方法的任何一个:

    例一 Lucene 索引创建

    view plaincopy to clipboardprint?
    01.$index = Zend_Search_Lucene::create($indexPath); 
      $index = Zend_Search_Lucene::create($indexPath);

    例二 Lucene 索引打开

    view plaincopy to clipboardprint?
    01.$index = Zend_Search_Lucene::open($indexPath); 
      $index = Zend_Search_Lucene::open($indexPath);

    返回目录

    索引
    索引由添加一个新文件到一个已经存在或者新的索引完成:

    view plaincopy to clipboardprint?
    01.$index->addDocument($doc); 
    $index->addDocument($doc);

    有两种办法来创建一个文件对象。第一个是手动的

    例一 手动文件建造

    view plaincopy to clipboardprint?
    01.$doc = new Zend_Search_Lucene_Document();  
    02.$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));  
    03.$doc->addField(Zend_Search_Lucene_Field::Text('title', $docTitle));  
    04.$doc->addField(Zend_Search_Lucene_Field::unStored('contents', $docBody));  
    05.$doc->addField(Zend_Search_Lucene_Field::binary('avatar', $avatarData)); 
      $doc = new Zend_Search_Lucene_Document();
      $doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
      $doc->addField(Zend_Search_Lucene_Field::Text('title', $docTitle));
      $doc->addField(Zend_Search_Lucene_Field::unStored('contents', $docBody));
      $doc->addField(Zend_Search_Lucene_Field::binary('avatar', $avatarData));

    第二种办法是从 HTML 或者微软 Office 2007 文件中载入:

    例二 文件载入

    view plaincopy to clipboardprint?
    01.$doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);  
    02.$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($path);  
    03.$doc = Zend_Search_Lucene_Document_Pptx::loadPptFile($path);  
    04.$doc = Zend_Search_Lucene_Document_Xlsx::loadXlsFile($path); 
      $doc = Zend_Search_Lucene_Document_Html::loadHTML($htmlString);
      $doc = Zend_Search_Lucene_Document_Docx::loadDocxFile($path);
      $doc = Zend_Search_Lucene_Document_Pptx::loadPptFile($path);
      $doc = Zend_Search_Lucene_Document_Xlsx::loadXlsFile($path);

    如果一个文件以一个被支持的格式载入,它仍然可以通过新的用户定义字段来手动扩展。

    索引策略
    你应该在你的应用程序的结构设计中定义索引策略。

    你可能需要一个随选定制的索引设置(和 OLTP 系统相似的东西)。在这样的系统,你通常为每一用户请求添加一个文件。这样,MaxBufferedDocs 选项将对系统没有影响。在另外一方面,MaxMergeDocs 由于它允许你限制脚本执行时间的最大值,就真的有用了。MergeFactor 应该被设置到一个值,这个值保持平均索引时间(它也受平均自动优化时间影响)和搜索效率(索引优化水平依赖于片断的数量)之间的平衡。

    如果你主要是进行批量索引更新,你的设置应该使用一个 MaxBufferedDocs 选项,把它设置到被可用的内存支持的最大值。MaxMergeDocs 和 MergeFactor 不得不设置到尽可能的降低自动优化相关的值。全部的索引拿破仑应该在索引以后进行。

    例三 索引优化

    view plaincopy to clipboardprint?
    01.$index->optimize(); 
    $index->optimize();

    在一些配置中,更有效的是,通过组织更新请求到一个队列,和在一个单独的脚本执行中进行几个更新请求来序列化索引更新。这将降低索引过分打开,同时允许利用索引文件缓冲。

    返回目录

    搜索
    搜索通过使用 find() 方法来运行。

    例一 搜索全部索引

    view plaincopy to clipboardprint?
    01.$hits = $index->find($query);  
    02.foreach ($hits as $hit) {  
    03.    printf("%d %f %s\n", $hit->id, $hit->score, $hit->title);  
    04.} 
    $hits = $index->find($query);
    foreach ($hits as $hit) {
        printf("%d %f %s\n", $hit->id, $hit->score, $hit->title);
    }

    这个例子展示了两个特殊搜索 hit 属性 -- id 和 score 的用法。

    id 是在一个 Lucene 索引内使用的一个内置的文件标识符。它可以被用来执行许多操作,包括从索引删除一个文件。

    例二 删除一个已经被索引的文件

    view plaincopy to clipboardprint?
    01.$index->delete($id); 
    $index->delete($id);

    或者从索引中检索文件:

    view plaincopy to clipboardprint?
    01.$doc = $index->getDocument($id); 
    $doc = $index->getDocument($id);

    注意:内置文件标识符
    重要提示!内置的文件标识符可以通过索引优化或者自动优化进程改变,但是它在一个单一的脚本执行过程中它不会改变,除非 addDocument()(可能包括一个自动拿破仑过程)或者 optimize() 方法被调用。

    score 字段是一个命中得分。默认的,搜索结果将按照 score 排序(最好的结果首先返回)。

    按照特定的字段值来给结果排序也是可以的。查看 Zend_Search_Lucene 文档来获得更多信息。

    这个例子也展示了一个能力,访问储存字段的能力(例如,$hit->title)。在第一次访问非 id 或者 score 的 hit 属性的时候,文件存储字段被加载,然后返回相应的字段值。

    这引起一个歧义,对于拥有自己 id 或者 score 字段的文件而言;因此,不建议在存储文件内使用这些字段名。尽管如此,仍然可以通过 getDocument() 方法来访问他们:

    例四 访问文件内的原始 id 和 score 字段

    view plaincopy to clipboardprint?
    01.$id    = $hit->getDocument()->id;   
    02.$score = $hit->getDocument()->score;  
    03.   
    $id    = $hit->getDocument()->id;
    $score = $hit->getDocument()->score;
      

    返回目录

    支持的查询
    Zend_Search_Lucene 和 Jave Lucene 支持一个强有力的查询语言。它允许搜索单独的词组,短语,词组范围;使用外卡和模糊搜索,使用布尔值操作符来合并查询,诸如此类。

    更详细的查询语言资料可以在 Zend_Search_Lucene 组件文档中找到。

    下面是一些普通查询类型和策略的例子。

    例一 查询一个单独的字

    hello

    在全部文件字段中搜索 hello 这个单词

    注意:默认搜索字段
    重要记号!Jave Lucene 默认的只搜索全部的 contents 字段,但是 Zend_Search_Lucene 搜索全部的字段。这个行为可以使用 Zend_Search_Lucene::setDefaultSearchField($fieldName) 方法来改变。

    例二 查询多个字

    hello dolly

    搜索两个单词,两个单词都是可选的,结果必须出现两个单词中的一个。

    例三 在一个查询中要求单词

    +hello dolly

    搜索两个单词,hello 是必须的,dolly 是可选的

    例四 在查询的文件中禁止单词

    +hello -dolly

    搜索两个单词,hello 是必须的,dolly 是禁止的。也就是说,如果文件匹配 hello,但是包含 dolly 这个单词,它不会返回在搜索结果中。

    例五 查询短语

    "hello dolly"

    搜索 hello dolly 这个短语;只有这个短语的文件才会匹配。

    例六 在特殊的字段中查询

    title:"The Right Way" AND text:go

    在标题(title)内搜索 The Right Way 这个短语,和在正文(text)内出现的 word 这个单词。

    例七 在特殊字段内搜索同时整个文件

    title:"The Right Way" AND go

    在标题内搜索 The Right Way 这个短语,同时在文件内任何字段中出现的 word 这个单词。

    例八 在特殊字段内和整个文件内搜索(可选择)

    title:Do it right

    在标题字段内搜索 Do,同时在全部字段内搜索 it 和 right;与之任何一个匹配的文件将返回结果。

    例九 使用通配符 ? 查询

    te?t

    搜索匹配 te?t 模式的单词,? 可以是任何一个字符。

    例十 使用通配符 * 查询

    test*

    搜索匹配 test* 的单词,* 是任何的0或多个字母序列。

    例十一 查询一个短语包括的范围

    mod_date:[20020101 TO 20030101]

    搜索一个短语的范围(包括在内的)

    例十二 搜索一个短语之外的范围

    title:{Aido to Carmen}

    搜索一个短语的范围(排除在外的)

    例十三 模糊搜索

    roam~

    模糊搜索 roam 单词

    例十四 布尔搜索

    (framework OR library) AND php

    布尔搜索

    所有的搜索可以通过 Zend_Search_Lucene 的 query construction API 来构建。还有,查询解析和查询构建可以结合:

    例十五 合并查询解析和构建

    view plaincopy to clipboardprint?
    01.$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);   
    02.$query = new Zend_Search_Lucene_Search_Query_Boolean();  
    03.$query->addSubquery($userQuery, true /* required */ 
    04.$query->addSubquery($constructedQuery, true /* required */); 
    $userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
    $query = new Zend_Search_Lucene_Search_Query_Boolean();
    $query->addSubquery($userQuery, true /* required */
    $query->addSubquery($constructedQuery, true /* required */);

    返回目录

    搜索结果分页
    在之前提到的,搜索结果命中对象使用懒惰装载来储存文件字段。当任何被储存的字段被访问,整个文件将会被装载。

    如果你只想对某些文件进行操作,不要检索全部的文件。

    view plaincopy to clipboardprint?
    01.$cacheId = md5($query);  
    02. if (!$resultSet = $cache->load($cacheId)) {  
    03.     $hits = $index->find($query);  
    04.     $resultSet = array();  
    05.     foreach ($hits as $hit) {  
    06.         $resultSetEntry          = array();  
    07.         $resultSetEntry['id']    = $hit->id;  
    08.         $resultSetEntry['score'] = $hit->score;  
    09.   
    10.         $resultSet[] = $resultSetEntry;  
    11.     }  
    12.   
    13.     $cache->save($resultSet, $cacheId);  
    14. }  
    15.   
    16. $publishedResultSet = array();  
    17. for ($resultId = $startId; $resultId < $endId; $resultId++) {  
    18.     $publishedResultSet[$resultId] = array(  
    19.         'id'    => $resultSet[$resultId]['id'],  
    20.         'score' => $resultSet[$resultId]['score'],  
    21.         'doc'   => $index->getDocument($resultSet[$resultId]['id']),  
    22.     );  
    23. }  

    aliyun活动 https://www.aliyun.com/acts/limit-buy?userCode=re2o7acl
  • 相关阅读:
    神经网络编程入门
    RBF神经网络通用函数 newrb, newrbe
    机器学习-RBF高斯核函数处理
    单元测试的基本概念
    File.Copy的时候Could not find a part of the path
    xunit inlinedata classdata memberdata
    xunit输出output到控制台
    Getting Started with xUnit.net (desktop)
    confluence的使用
    Why is an 'Any CPU' application running as x86 on a x64 machine?
  • 原文地址:https://www.cnblogs.com/wangbin/p/1831711.html
Copyright © 2020-2023  润新知