apache solr_使用PHP和Apache Solr进行企业搜索

apache solr

在“ 使用PHP构建自定义搜索引擎 ”中,我结合了PHP和开放源代码Sphinx搜索引擎,以创建一种快速替代文本密集型数据库查询的替代方法,例如LIKE ,对于MySQL, MATCH 。 (请参阅相关主题的狮身人面像相关的信息。)

Sphinx易于安装和维护,并且功能强大。 此外,最新版本的Sphinx现在提供了本机MySQL引擎,从而不再需要运行单独的Sphinx守护程序。 V0.9.8(撰写本文时为最新版本)还添加了地理距离查询,以查找与给定位置之间的距离以及称为多重查询的功能所包围的记录,该功能将多个查询和结果集捆绑在一个网络中连接。

Sphinx会随着时间的推移不断改进,是购物网站,博客和许多其他应用程序的理想选择。 根据Sphinx网站的说法,现在有一个应用程序索引了7亿个文档,约合1.2 TB的数据。 我毫不犹豫地推荐Sphinx。

但是,随着应用程序或站点的流行和使用量的增加,Sphinx还不支持您可能希望使用和提供的若干功能。 特别是,Sphinx尚未自动复制或分发其索引,从而使其守护程序成为单点故障。 (作为一种解决方法,多台计算机可以为同一个数据库建立索引,并且可以对这些系统进行集群。)Sphinx不会突出显示搜索结果(就像Google在显示缓存的页面时一样),不会保留或缓存最近的结果,并且不支持正则表达式(regex)或基于日期的操作。

如果您寻求这些功能或准备使用企业级解决方案,请考虑Apache Software Foundation的Solr项目。 Solr基于Lucene搜索引擎,并根据自由Apache软件许可的条款作为开源提供,Solr是(根据Lucene站点)“基于Lucene Java™搜索库并带有XML / HTTP的开源企业搜索服务器。和JSON API,突出显示,分面搜索,缓存,复制和Web管理界面。”

Netflix,Digg和CNET的News.com和CNET Reviews等其他流量大的网站都使用Solr来增强搜索功能。 在Solr Wiki中可以找到由Solr支持的公共站点的详细列表(请参阅参考资料 )。

了解如何使用Solr和PHP创建一个小的应用程序来搜索汽车零件数据库。 尽管示例数据库仅包含少量记录,但它也可以轻松地包含数百万条记录。 本文中使用的所有源代码都可以从“ 下载”部分获得。

安装Solr

要将Solr与PHP结合使用,您必须安装Solr,设计索引,准备要由Solr索引的数据,加载索引,编写PHP代码以执行查询并显示结果。 创建可搜索索引所需的许多工作可以从命令行执行。 当然,PHP的Solr编程接口也可能影响索引的内容。

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操作系统作为演示基础。 苹果公司的Leopard包含Java V1.5。 只需更改默认的Apache配置,Leopard即可运行PHP应用程序。 在Leopard终端窗口中运行java -version会产生以下结果。

清单1.在Leopard终端窗口中运行java -version
$ which java
/usr/bin/java

$ java -version
java version "1.5.0_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)
Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)

注意 :Leopard允许您在/ Applications / Utilities / Java中的Java Preferences应用程序中的Java V1.4和V1.5之间切换。 如果您的Leopard安装报告为V1.4,请打开Java首选项并更改设置,使其类似于图1。

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

要安装Solr,请访问Apache.org ,单击“ 资源”>“下载” ,选择一个方便的项目镜像,然后在显示的文件夹中导航以选择Solr V1.2的压缩包(.tgz文件)。 下载将传输一个类似于apache-solr-1.2.0.tgz的文件。 使用以下代码解压缩tarball。

清单2.解压缩tarball
$ tar xzf apache-solr-1.2.0.tgz

$ ls -F apache-solr-1.2.0
CHANGES.txt NOTICE.txt  dist/ lib/
KEYS.txt  README.txt  docs/   src/
LICENSE.txt build.xml example/

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

示例目录包含完整的示例Solr应用程序。 要运行它,只需使用应用程序归档文件start.jar启动Java引擎。

清单3.启动Java引擎
$ java -jar start.jar
2007-11-10 15:00:16.672::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
2007-11-10 15:00:16.866::INFO:  jetty-6.1.3
...
INFO: SolrUpdateServlet.init() done
2007-11-10 15:00:18.694::INFO:  Started SocketConnector @ 0.0.0.0:8983

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

但是,Solr索引中没有任何数据可以管理或查询。

将数据加载到Solr

Solr开箱即用非常灵活,支持多种数据类型和规则来创建有效索引。 而且,尽管标准组件不够用,但是您可以通过编写新的Java类来进一步自定义Solr。

给定一组数据类型和规则,然后可以创建Solr模式来描述数据并控制应如何构造索引。 然后,您导出数据以匹配模式,并将数据加载到Solr中。 Solr会即时创建索引,在创建,修改或删除记录时立即更新每个索引。

可以在Apache.org上找到默认的Solr模式,作为Solr源代码存储库的一部分。 作为参考,下面显示了默认架构的代码段。

清单3.默认的Solr模式片段
<schema name="example" version="1.1">
  ...
  <fields>
  <field name="id" type="string" indexed="true" stored="true" required="true" /> 
  <field name="name" type="text" indexed="true" stored="true"/>
  <field name="nameSort" type="string" indexed="true" stored="false"/>
  <field name="cat" type="text" indexed="true" stored="true" multiValued="true"/>
  ...
  </fields>
  
  <uniqueKey>id</uniqueKey>
  ...
  <copyField source="name" dest="nameSort"/>
  ...
</schema>

许多模式是不言自明的,但是有些方面需要澄清:

  • 如图所示,字段id是一个字符串( type="string" ),应该被索引( indexed="true" )。 这也是必填字段( required="true" )。 使用此架构,Solr中加载的每个记录都必须对此字段具有一个值。 <uniqueKey>id</uniqueKey>修饰符进一步声明id字段必须唯一。 (Solr不需要唯一的ID字段;这只是在默认索引架构中建立的规则。)属性stored="true"表示应可检索该id字段。

    为什么你会永远集storedfalse ? 您可以使用nameSort字段对结果进行不同的nameSort ,例如nameSort ,它是name字段的副本(由于最后一行的copyField命令),但是具有不同的行为。 注意nameSort是一个string ,而nametext 。 默认的索引架构对待这两种类型略有不同。

  • 字段catmultiValued 。 一条记录可以为此字段定义几个值。 例如,如果您的应用程序管理内容,则可以为一个故事分配几个主题。 您可以使用cat字段(或定义自己的相似字段)来捕获所有主题。

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

清单4.为默认Solr索引模式格式化的数据
<add>
<doc>
  <field name="id">F8V7067-APL-KIT</field>
  <field name="name">Belkin Mobile Power Cord for iPod w/ Dock</field>
  <field name="manu">Belkin</field>
  <field name="cat">electronics</field>
  <field name="cat">connector</field>
  <field name="features">car power adapter, white</field>
  <field name="weight">4</field>
  <field name="price">19.95</field>
  <field name="popularity">1</field>
  <field name="inStock">false</field>
</doc>

<doc>
  <field name="id">IW-02</field>
  <field name="name">iPod & iPod Mini USB 2.0 Cable</field>
  <field name="manu">Belkin</field>
  <field name="cat">electronics</field>
  <field name="cat">connector</field>
  <field name="features">car power adapter for iPod, white</field>
  <field name="weight">2</field>
  <field name="price">11.50</field>
  <field name="popularity">1</field>
  <field name="inStock">false</field>
</doc>
</add>

add元素是一个Solr命令,用于将封装的记录添加到索引中。 每个记录都在doc元素中捕获,该元素使用一系列命名field元素来指​​定字段值。 字段weightpriceinStockmanufeaturespopularity是默认Solr索引架构中定义的其他字段。 features字段具有与cat相同的属性,但是具有不同的语义:它枚举了产品的(可能很多)功能。

搜索汽车零件

本示例为汽车零件的集合建立索引。 每个汽车零件都有几个字段,其中最重要的字段示例如表1所示。该字段的名称在第一列中列出。 第二列提供简要说明,而第三列列出其逻辑类型。 第四列显示了用于表示基准的索引类型(如清单5中的模式所定义)。

表1.汽车零件记录的字段
名称 描述 类型 Solr类型
零件编号(唯一,必填) 身份证号码 partno
名称 简洁的描述 name
模型(必填,多值) 模型,例如“ Camaro” model
型号年份(多值) 模特年,例如2001 year
价钱 单位成本 浮动 price
有现货 是否在库存之内 布尔型 inStock
特征 零件能力 features
时间戳记 活动记录 timestamp
重量 装运重量 浮动 weight

清单3显示了用于汽车零件索引的Solr模式的一部分。 它很大程度上基于默认的Solr模式。 使用的特定字段(名称和属性)仅替换了默认值中的fields元素(如清单1所示)。

清单5.汽车零件索引架构
<?xml version="1.0" encoding="utf-8" ?>
<schema name="autoparts" version="1.0">
  ...
  <fields>
    <field name="partno" type="string" indexed="true" 
    stored="true" required="true" /> 
    
    <field name="name" type="text" indexed="true" 
    stored="true" required="true" />
    
    <field name="model" type="text_ws" indexed="true" stored="true" 
    multiValued="true" required="true" />
    
    <field name="year" type="text_ws" indexed="true" stored="true" 
    multiValued="true" omitNorms="true" />
    
    <field name="price"  type="sfloat" indexed="true" 
    stored="true" required="true" />
    
    <field name="inStock" type="boolean" indexed="true" 
     stored="true" default="false" /> 
    
    <field name="features" type="text" indexed="true" 
    stored="true" multiValued="true" />
    
    <field name="timestamp" type="date" indexed="true" 
    stored="true" default="NOW" multiValued="false" />
    
    <field name="weight" type="sfloat" indexed="true" stored="true" />
  </fields>
  
  <uniqueKey>partno</uniqueKey>
  
  <defaultSearchField>name</defaultSearchField>
</schema>

在上述字段的基础上,导出并格式化以供上传到Solr的汽车零件数据库可能类似于清单6。

清单6.格式化用于索引的汽车零件数据库
<add>
<doc>
  <field name="partno">1</field>
  <field name="name">Spark plug</field>
  <field name="model">Boxster</field>
  <field name="model">924</field>
  <field name="year">1999</field>
  <field name="year">2000</field>
  <field name="price">25.00</field>
  <field name="inStock">true</field>
</doc>
<doc>
  <field name="partno">2</field>
  <field name="name">Windshield</field>
  <field name="model">911</field>
  <field name="year">1991</field>
  <field name="year">1999</field>
  <field name="price">15.00</field>
  <field name="inStock">false</field>
</doc>
</add>

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

清单7.使用新模式启动Solr
$ cd apache-solr-1.2/example
$ cp solr/conf/schema.xml solr/conf/default_schema.xml
$ chmod a-w solr/conf/default_schema.xml

$ vi /tmp/schema.xml
...
$ cp /tmp/schema.xml solr/conf/schema.xml

$ vi /tmp/parts.xml
...

$ java -jar start.jar 
...
2007-11-11 16:56:48.279::INFO:  Started SocketConnector @ 0.0.0.0:8983

$ java -jar exampledocs/post.jar /tmp/parts.xml
SimplePostTool: version 1.2
SimplePostTool: WARNING: Make sure your XML documents are encoded in UTF-8,    
  other encodings are not currently supported
SimplePostTool: POSTing files to http://localhost:8983/solr/update...
SimplePostTool: POSTing file parts.xml
SimplePostTool: COMMITting Solr index changes...

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

您的结果应类似于以下内容:

3 on 10 0 partno: 1 OR partno: 2 2.2
true Boxster 924 Spark plug 1 25.0 2007-11-11T21:58:45.899Z 1999 2000 
false 911 Windshield 2 15.0 2007-11-11T21:58:45.953Z 1991 1999

尝试其他一些查询。 Lucene查询的语法。

您还应该尝试再次编辑和加载数据。 由于partno字段声明为唯一,因此重复执行相同零件号的操作仅会将旧索引记录替换为新记录。 除了add命令之外,您还可以使用commitoptimizedelete 。 最后一条命令可以按ID删除特定记录,也可以通过查询删除许多记录。

现在是PHP

最后,PHP输入示例。

至少有两个PHP Solr API。 最可靠的实现是Donovan JimenezPHP Solr Client(请参阅参考资料 )。 该代码与Solr具有相同的条款许可,并具有大量文档,并且与Solr V1.2兼容。 撰写本文时,最新版本为2007年10月2日。

Solr客户端提供了四个PHP类:

  • Apache_Solr_Service代表一个Solr服务器。 使用这些方法来ping服务器,添加和删除文档,提交更改,优化索引以及运行查询。
  • Apache_Solr_Document包含一个Solr文档。 此类的方法管理(键,值)对和多值字段。 可以通过直接解引用来访问字段值,例如$document->title = 'Something'; ... echo $document->title; $document->title = 'Something'; ... echo $document->title;
  • Apache_Solr_Response封装了Solr响应。 此代码依赖于json_decode()函数,它捆绑了PHP V5.2.0和更高版本,或可以用PHP扩展社区图书馆安装(PECL -参见相关主题 )。
  • Apache_Solr_Service_Balancer增强了Apache_Solr_Service ,允许您连接到分发中的多个Solr服务。 该课程不在这里。

下载PHP Solr Client(请参阅参考资料 )并将其解压缩到工作目录中。 更改为SolrPhpClient。 接下来,检查文件Apache / Solr / Service.php。 在撰写本文时,第335行缺少尾随的分号。 编辑文件,并在必要时添加分号。 另外,检查文件Apache / Solr / Document.php。 第112-117行应如下所示。

if (!is_array($this->_fields[$key]))
{
  $this->_fields[$key] = array($this->_fields[$key]);
}

$this->_fields[$key][] = $value;

纠正文件后,可以将Apache目录与其他PHP库一起安装。

下面的代码显示了一个PHP应用程序,该应用程序连接Solr服务,将两个文档添加到索引,并运行先前使用的零件号查询。

清单8.一个示例PHP应用程序,用于连接,加载和查询Solr索引
<?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处获得OK,则代码将显示一条错误消息。 空结果集不发出任何输出。

更大的力量

Solr非常强大,PHP API使在任何平台上的集成都非常容易。 更好的是,Solr易于设置和操作,并且可以根据需要启用高级功能。 最重要的是,Solr是免费的。 不要为搜索引擎付费。 保存您的美元,然后去Solr。

浏览Solr网站,以了解有关高级配置的更多信息,包括排序,分类结果和复制。 Lucene网站是另一信息来源,因为它是Solr系统下的搜索技术。


翻译自: https://www.ibm.com/developerworks/opensource/library/os-php-apachesolr/index.html

apache solr

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值