您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
使用 PHP 和 Apache Solr 实现企业搜索
 
作者:Martin Streicher 来源:IBM 发布于 2016-7-15
  2898  次浏览      21
 

在 “用 PHP 构建自定义搜索引擎” 中,我结合使用 PHP 与开源 Sphinx 搜索引擎创建了文本密集型数据库查询的快速备选方法,例如 MySQL 中的 LIKE 和 MATCH(要获得与 Sphinx 相关的信息,请参阅 参考资料)。

Sphinx 非常易于安装和维护,并且功能十分强大。而且,Sphinx 的最新版本现在提供了一个本机 MySQL 引擎,不需要运行独立的 Sphinx 守护进程。V0.9.8(撰写本文时的最新版本)还添加了地理距离(geodistance)查询,用于查找在给定位置的某个距离范围内的记录;并添加了名为多查询(multi-query)的功能,它是在单一网络连接中绑定多个查询和结果集的优化功能功能。

Sphinx 一直以来不断改进,并且是购物站点、博客和许多其他应用程序的理想选择。根据 Sphinx 站点的数据,一个应用程序现在将索引 700 百万个文档,或者索引大约 1.2 百万兆字节数据。因此,我毫不犹豫地向您推荐 Sphinx。

但 是,当应用程序或站点变得越来越流行、使用量逐步增加时,Sphinx 可能无法提供相应的几项功能。尤其是,Sphinx 仍然不能够自动复制或分发其索引,使其守护进程成为单点故障(作为解决方法,几台计算机可以索引同一个数据库,并且可以将这些系统聚合在一起)。 Sphinx 不突出显示搜索结果(如 Google 在显示缓存页面时所做的操作),不保留或缓存最近搜索到的结果,并且不支持正则表达式 (regex) 或与日期相关的操作。

如果要寻找那些功能或者准备采用企业级解决方案,请考虑 Apache Software Foundation 的 Solr 项目。基于 Lucene 搜索引擎并且在 Apache Software License 许可下以开源形式提供,Solr 是(根据 Lucene 站点)“基于 Lucene Java? 搜索库、配有 XML/HTTP 和 JSON API、命中结果突出显示、分面组配式搜索、缓存、复制和 Web 管理界面的开源企业搜索服务器”。

其中值得注意的是,大流量的 Web 站点、Netflix、Digg 和 CNET 的 News.com 和 CNET Reviews 使用 Solr 来增强搜索功能。由 Solr 驱动的公共站点的长串列表可以在 Solr 维基中找到(请参阅 参考资料)。

了解如何使用 Solr 和 PHP 创建搜索汽车零部件数据库的小型应用程序。虽然示例数据库只是包含一些记录,但是它轻轻松松就能包含数百万条记录。本文中使用的所有源代码均可从 下载 部分获得。

安装 Solr

要将 Solr 与 PHP 结合使用,您必须安装 Solr,设计索引,准备 Solr 要索引的数据,载入索引,编写执行查询的 PHP 代码和显示结果。创建可搜索索引所需的大部分工作可以通过命令行执行。当然,Solr 的 PHP 编程接口也会影响索引的内容。

Solr 是用 Java 技术实现的。要运行 Solr 及其管理工具,您必须安装 Java V1.5 软件开发包 (Java 5 SDK)。几个提供商都提供了 Java V1.5 SDK —— 例如,Sun MicrosystemsIBM?BEA Systems —— 并且每个实现都能够驱动 Solr。只需选择适用于您的操作系统的 Java 包并遵循相应的说明来完成安装。

在 许多情况下,安装 Java V1.5 就像运行自提取归档和接受许可证协议条款一样简单。归档中的脚本在几秒钟内就能完成所有大部分艰巨任务。其他操作系统(例如 Debian)将在 APT 系统库中提供 Java 5 SDK。例如,如果使用 Debian 或 Ubuntu,则可以用 sudo apt-get install sun-java5-jdk 安装 Java V1.5 软件。

APT 还将自动下载使用 Java 5 SDK 所需的所有依赖性,非常方便。

如果 Java 软件已经安装并且 Java 可执行文件已在 PATH 中,请运行 java -version 来确定拥有的 Java 代码。

在 这里,让我们使用 Mac OS X V10.5 Leopard 操作系统作为演示的基础。Apple 的 Leopard 附带了 Java V1.5。只要对 Apache 的默认配置进行微小更改,Leopard 也可以运行 PHP 应用程序。在 Leopard 终端窗口中运行 java -version 将生成以下输出。

清单 1. 在 Leopard 终端窗口中运行 java -version

:Leopard 将允许您在 /Applications/Utilities/Java 的 Java Preferences 应用程序中的 Java V1.4 与 V1.5 之间来回切换。如果 Leopard 安装显示 V1.4,则打开 Java Preferences 并按照图 1 所示更改设置。

图 1. Leopard 中的 Java Preferences 应用程序

要安装 Solr,请访问 Apache.org,单击 Resources > Download,选择一个方便访问的项目镜像,并且在所示文件夹内浏览选择 Solr V1.2 的 tarball(.tgz 文件)。下载将传输名称类似 apache-solr-1.2.0.tgz 的文件。用下列代码解压缩 tarball。

清单 2. 解压缩 tarball

在新创建的目录中,名为 dist 的文件夹包含绑定为 Java 归档 (JAR) 的 Solr 代码。子目录 example/exampledocs 包含已经格式化的数据示例 —— 通常为 XML 代码 —— 并且准备好供 Solr 索引。

example 目录包含一个完整的样例 Solr 应用程序。要运行它,只需用应用程序归档 start.jar 启动 Java 引擎。

清单 3. 启动 Java 引擎

应用程序现在可以在 8983 端口上使用。启动浏览器并在地址栏中键入 http://localhost:8983/solr/admin/。这是用于管理 Solr 的接口(要停止 Solr 服务器,请在命令行中键入 Ctrl+C 组合键)。

但是 Solr 索引中还没有数据可供管理或查询。

把数据装入 Solr

Solr 非常灵活,支持创建有效索引的各种数据类型和规则。而且虽然 Solr 支持数据类型和规则十分广泛,但是如果标准组件不够用,还可以通过编写新的 Java 类进一步自定义 Solr。

给定一组数据类型和规则,您就可以创建一个 Solr 模式来描述数据和控制应当怎样构造索引。然后导出数据来匹配模式并将数据装入 Solr。Solr 将动态创建索引,在记录被创建、修改或删除时立即更新每个索引。

可以在 Apache.org 的 Solr 源代码库中找到默认的 Solr 模式。为供参考,下面显示了默认模式的代码片段。

清单 3. 默认的 Solr 模式代码片段

模式的大部分内容都无需加以说明,但是有一些方面需要注意:

(1)如示,字段 id 是字符串 (type="string") 并且应当被索引 (indexed="true")。它也是一个必需字段 (required="true")。使用此模式,载入 Solr 的每条记录必须为这个字段提供值。<uniqueKey>id</uniqueKey> 修饰词进一步说明 id 字段必须惟一(Solr 不要求 ID 字段惟一;这只是在默认索引模式中建立的规则)。属性 stored="true" 表示 id 字段应当可检索。为什么不把 stored 设为 false?您可以使用不可检索的字段来以不同方式给结果排序,比方说使用 nameSort,它是 name 字段的副本(最后一行中的 copyField 命令),但是行为不同。注意,nameSort 是 string,而 name 是 text。默认索引模式将采取稍微不同的方式来处理这两种类型。

(2)字段 cat 是 multiValued。记录可以为此字段定义多个值。例如,如果应用程序管理内容,则可以给一篇文章指定多个标题。您可以使用 cat 字段(或者自定义类似字段)来捕捉所有标题。

清单 4 显示了 example/exampledocs/ipod_other.xml 文件,该文件表示 iPod 附件分类中的两个条目。

清单 4. 为默认 Solr 索引模式格式化的数据

add 元素是用于将封装记录添加到索引中的 Solr 命令。每条记录都将被捕捉到 doc 元素中,该元素将使用一组名为 field 的元素来指定字段值。字段 weight、price、inStock、manu、features 和 popularity 都是在默认 Solr 索引模式中定义的其他字段。features 字段拥有与 cat 相同的属性,但是语义不同:它列举了产品的功能,数量可能较多。

搜索汽车零部件

本例将索引汽车零部件集。每个汽车零部件都有多个字段,表 1 中显示了最重要的字段样例。第一列中列出了字段名。第二列将提供简要描述,而第三列将列出逻辑类型。第四列将显示用于表示数据的索引类型(按照 清单 5 的模式中的定义)。

表 1. 汽车零部件记录的字段

清单 3 显示了汽车零部件索引所使用的 Solr 模式部分。它大部分都是基于默认 Solr 模式。使用的具体字段 —— 名称和属性 —— 只是替换了在默认模式中找到的 fields 元素(如 清单 1 中所示)。

清单 5. 汽车零部件索引模式

针对以上字段,需要将汽车零部件数据库导出并格式化以上传到 Solr 中,如清单 6 所示。

清单 6. 为进行索引而格式化的汽车零部件数据库

让我们安装新索引模式并把数据装入 Solr。首先,通过使用 Ctrl+C 组合键来停止 Solr 守护进程(如果它仍在运行)。在 example/solr/conf/schema.xml 中制作现有 Solr 模式的归档。接下来,通过清单 6 创建一个文本文件,将其保存到 /tmp/schema.xml 中,然后复制到 example/solr/conf/schema.xml 中。为清单 7 中所示的数据创建另一个文件。现在,您可以重新启动 Solr 并使用示例所提供的 posting 实用程序。

清单 7. 启用带有新模式的 Solr

成功!如果需要检验索引是否存在并包含两个文档,请将浏览器再次指向 http://localhost:8983/solr/admin/。您应当会在页面顶部看到 “(autoparts)”。如果看到了,请单击页面中间的查询框并键入 partno: 1 or partno: 2。

结果应当类似下面的内容:

尝试其他一些查询。Lucene 维基中介绍了 Lucene 查询(Solr 中的搜索引擎)的语法(请参阅 参考资料)。

您还应当尝试再次编辑和载入数据。由于声明了 partno 字段惟一,因此重复上传同一个部件号将只用新记录替换旧索引记录。除了 add 命令之外,您还可以使用 commit、optimize 和 delete。最后一个命令可以按照 ID 删除一条特定记录,也可以通过查询删除多条记录。

现在使用 PHP

最后,PHP 将参与到这个示例中。

至少有两个 PHP Solr API。最健壮的实现是 Donovan Jimenez 的 PHP Solr Client(请参阅 参考资料)。代码是在与 Solr 相同的条款下许可开发的,它有扩展文档,并且与 Solr V1.2 兼容。撰写本文时的最新版本是在 2007 年 10 月 2 日发布的。

Solr Client 将提供四个 PHP 类:

(1)Apache_Solr_Service 表示 Solr 服务器。使用这些方法来 ping 服务器,添加和删除文档,提交更改,优化索引,以及运行查询。

(2) Apache_Solr_Document 收录 Solr 文档。该类的方法将管理(关键字,值)对和多值字段。字段值可以通过直接解除引用来访问,例如 $document->title = 'Something'; ... echo $document->title;。

(3) Apache_Solr_Response 封装 Solr 响应。这段代码依赖于 json_decode() 函数,该函数是 PHP V5.2.0 和更高版本附带的,也可以用 PHP Extension Community Library(PECL —— 请参阅 参考资料)来安装。

(4)Apache_Solr_Service_Balancer 将增强 Apache_Solr_Service,允许您连接到一个分发中的多项 Solr 服务。本文中没有介绍该类。

下载 PHP Solr Client(请参阅 参考资料) 并将其解压缩到工作目录中。切换到 SolrPhpClient。接下来,查看文件 Apache/Solr/Service.php。撰写本文时,第 335 行缺少了拖尾分号。编辑该文件并添加分号(如果有必要)。另请查看文件 Apache/Solr/Document.php。在第 112 行至第 117 行中应当读出如下内容。

更正文件后,可以在其他 PHP 库旁边安装 Apache 目录。

下列代码显示了连接 Solr 服务、向索引中添加两个文档和运行先前使用的零部件号查询的 PHP 应用程序。

清单 8. 用于连接、载入和查询 Solr 索引的样例 PHP 应用程序
<?php
  require_once( 'Apache/Solr/Service.php' );
  
  // 
  // 
  // Try to connect to the named server, port, and url
  // 
  $solr = new Apache_Solr_Service( 'localhost', '8983', '/solr' );
  
  if ( ! $solr->ping() ) {
    echo 'Solr service not responding.';
    exit;
  }
  
  //
  //
  // Create two documents to represent two auto parts.
  // In practice, documents would likely be assembled from a 
  //   database query. 
  //
  $parts = array(
    'spark_plug' => array(
      'partno' => 1,
      'name' => 'Spark plug',
      'model' => array( 'Boxster', '924' ),
      'year' => array( 1999, 2000 ),
      'price' => 25.00,
      'inStock' => true,
    ),
    'windshield' => array(
      'partno' => 2,
      'name' => 'Windshield',
      'model' => '911',
      'year' => array( 1999, 2000 ),
      'price' => 15.00,
      'inStock' => false,
    )
  );
    
  $documents = array();
  
  foreach ( $parts as $item => $fields ) {
    $part = new Apache_Solr_Document();
    
    foreach ( $fields as $key => $value ) {
      if ( is_array( $value ) ) {
        foreach ( $value as $datum ) {
          $part->setMultiValue( $key, $datum );
        }
      }
      else {
        $part->$key = $value;
      }
    }
    
    $documents[] = $part;
  }
    
  //
  //
  // Load the documents into the index
  // 
  try {
    $solr->addDocuments( $documents );
    $solr->commit();
    $solr->optimize();
  }
  catch ( Exception $e ) {
    echo $e->getMessage();
  }
  
  //
  // 
  // Run some queries. Provide the raw path, a starting offset
  //   for result documents, and the maximum number of result
  //   documents to return. You can also use a fourth parameter
  //   to control how results are sorted and highlighted, 
  //   among other options.
  //
  $offset = 0;
  $limit = 10;
  
  $queries = array(
    'partno: 1 OR partno: 2',
    'model: Boxster',
    'name: plug'
  );

  foreach ( $queries as $query ) {
    $response = $solr->search( $query, $offset, $limit );
    
    if ( $response->getHttpStatus() == 200 ) { 
      // print_r( $response->getRawResponse() );
      
      if ( $response->response->numFound > 0 ) {
        echo "$query <br />";

        foreach ( $response->response->docs as $doc ) { 
          echo "$doc->partno $doc->name <br />";
        }
        
        echo '<br />';
      }
    }
    else {
      echo $response->getHttpStatusMessage();
    }
  }
?>

首先,代码将连接到给定端口和路径上名为 Solr 的服务器,并将使用 ping() 方法来检验服务器是否可运行。

接下来,代码将把表示为 PHP 数组的记录转换为 Solr 文档。如果字段有单个值,则简单的访问程序将把(关键字,值)对添加到文档中。如果字段有多个值,则用特殊函数 setMultiValue() 把值列表赋给关键字。您可以看到此过程非常类似于 Solr 文档的 XML 表示。

作为一项优化,addDocuments() 将把多个文档插入索引。后续的 commit() 和 optimize() 函数将完成添加操作。

在底部,多个查询将从索引中检索数据。您可以通过两个函数查看结果:getRawResponse() 函数将生成完整的未解析结果,而 docs() 函数将返回带有指定访问程序的文档数组。

如果查询未能从 Solr 获得确认,代码将输出错误消息。空结果集将不产生输出。

结束语

Solr 令人难以置信地强大,并且使用 PHP API 可以迅速地与任何平台集成。但更棒的是,Solr 易于安装和运行,并且可以根据需要启用高级功能。最棒的是,Solr 是免费的。不用购买搜索引擎。省下您的钱,使用 Solr 吧。

访问 Solr Web 站点了解包括排序、分类结果和复制在内的高级配置的更多信息。Lucene Web 站点是另一个信息源,因为它是 Solr 系统下的搜索技术。

 

   
2898 次浏览       21
相关文章

Java微服务新生代之Nacos
深入理解Java中的容器
Java容器详解
Java代码质量检查工具及使用案例
相关文档

Java性能优化
Spring框架
SSM框架简单简绍
从零开始学java编程经典
相关课程

高性能Java编程与系统性能优化
JavaEE架构、 设计模式及性能调优
Java编程基础到应用开发
JAVA虚拟机原理剖析
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

Java 中的中文编码问题
Java基础知识的三十个经典问答
玩转 Java Web 应用开发
使用Spring更好地处理Struts
用Eclipse开发iPhone Web应用
插件系统框架分析
更多...   

Struts+Spring+Hibernate
基于J2EE的Web 2.0应用开发
J2EE设计模式和性能调优
Java EE 5企业级架构设计
Java单元测试方法与技术
Java编程方法与技术

Struts+Spring+Hibernate/EJB+性能优化
华夏基金 ActiveMQ 原理与管理
某民航公司 Java基础编程到应用开发
某风电公司 Java 应用开发平台与迁移
日照港 J2EE应用开发技术框架与实践
某跨国公司 工作流管理JBPM
东方航空公司 高级J2EE及其前沿技术
更多...