<p>安装 Solr<br />
Solr 是用 Java 技术实现的。要运行 Solr 及其管理工具,您必须安装 Java V1.5 软件开发包 (Java 5 SDK)。几个提供商都提供了 Java V1.5 SDK ——<br />
例如,Sun Microsystems、IBM® 和 BEA Systems —— 并且每个实现都能够驱动 Solr。只需选择适用于您的操作系统的 Java 包并遵循相应的说明来完成安装。</p>
<p>如果 Java 软件已经安装并且 Java 可执行文件已在 PATH 中,请运行 java -version 来确定拥有的 Java 代码。</p>
<p>在这里,让我们使用 Mac OS X V10.5 Leopard 操作系统作为演示的基础。Apple 的 Leopard 附带了 Java V1.5。只要对 Apache 的默认配置进行微小更改,<br />
Leopard 也可以运行 PHP 应用程序。在 Leopard 终端窗口中运行 java -version 将生成以下输出。<br />
清单 1. 在 Leopard 终端窗口中运行 java -version<br />
$ which java<br />
/usr/bin/java<br />
<br />
$ java -version<br />
java version "1.5.0_13"<br />
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)<br />
Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)<br />
注:Leopard 将允许您在 /Applications/Utilities/Java 的 Java Preferences 应用程序中的 Java V1.4 与 V1.5 之间来回切换。如果 Leopard 安装显示 V1.4,则打开 Java Preferences 并按照图 1 所示更改设置。<br />
图 1. Leopard 中的 Java Preferences 应用程序<br />
Leopard 中的 Java Preferences 应用程序<br />
要安装 Solr,请访问 Apache.org,单击 Resources > Download,选择一个方便访问的项目镜像,并且在所示文件夹内浏览选择 Solr V1.2 的 tarball(.tgz 文件)。下载将传输名称类似 apache-solr-1.2.0.tgz 的文件。用下列代码解压缩 tarball。<br />
清单 2. 解压缩 tarball</p>
<p>$ tar xzf apache-solr-1.2.0.tgz<br />
<br />
$ ls -F apache-solr-1.2.0<br />
CHANGES.txt NOTICE.txt dist/ lib/<br />
KEYS.txt README.txt docs/ src/<br />
LICENSE.txt build.xml example/<br />
在新创建的目录中,名为 dist 的文件夹包含绑定为 Java 归档 (JAR) 的 Solr 代码。子目录 example/exampledocs 包含已经格式化的数据示例 —— 通常为 XML 代码 —— 并且准备好供 Solr 索引。<br />
example 目录包含一个完整的样例 Solr 应用程序。要运行它,只需用应用程序归档 start.jar 启动 Java 引擎。<br />
清单 3. 启动 Java 引擎</p>
<p>$ java -jar start.jar<br />
2007-11-10 15:00:16.672::INFO: Logging to STDERR via org.mortbay.log.StdErrLog<br />
2007-11-10 15:00:16.866::INFO: jetty-6.1.3<br />
...<br />
INFO: SolrUpdateServlet.init() done<br />
2007-11-10 15:00:18.694::INFO: Started SocketConnector @ 0.0.0.0:8983<br />
应用程序现在可以在 8983 端口上使用。启动浏览器并在地址栏中键入 http://localhost:8983/solr/admin/。这是用于管理 Solr 的接口(要停止 Solr 服务器,请在命令行中键入 Ctrl+C 组合键)。<br />
但是 Solr 索引中还没有数据可供管理或查询。<br />
把数据装入 Solr<br />
Solr 非常灵活,支持创建有效索引的各种数据类型和规则。而且虽然 Solr 支持数据类型和规则十分广泛,但是如果标准组件不够用,还可以通过编写新的 Java 类进一步自定义 Solr。<br />
给定一组数据类型和规则,您就可以创建一个 Solr 模式来描述数据和控制应当怎样构造索引。然后导出数据来匹配模式并将数据装入 Solr。Solr 将动态创建索引,在记录被创建、修改或删除时立即更新每个索引。<br />
可以在 Apache.org 的 Solr 源代码库中找到默认的 Solr 模式。为供参考,下面显示了默认模式的代码片段。<br />
清单 3. 默认的 Solr 模式代码片段</p>
<p><schema name="example" version="1.1"><br />
...<br />
<fields><br />
<field name="id" type="string" indexed="true" stored="true" required="true" /><br />
<field name="name" type="text" indexed="true" stored="true"/><br />
<field name="nameSort" type="string" indexed="true" stored="false"/><br />
<field name="cat" type="text" indexed="true" stored="true" multiValued="true"/><br />
...<br />
</fields><br />
<br />
<uniqueKey>id</uniqueKey><br />
...<br />
<copyField source="name" dest="nameSort"/><br />
...<br />
</schema><br />
模式的大部分内容都无需加以说明,但是有一些方面需要注意:<br />
如示,字段 id 是字符串 (type="string") 并且应当被索引 (indexed="true")。它也是一个必需字段 (required="true")。使用此模式,载入 Solr 的每条记录必须为这个字段提供值。<uniqueKey>id</uniqueKey> 修饰词进一步说明 id 字段必须惟一(Solr 不要求 ID 字段惟一;这只是在默认索引模式中建立的规则)。属性 stored="true" 表示 id 字段应当可检索。<br />
为什么不把 stored 设为 false?您可以使用不可检索的字段来以不同方式给结果排序,比方说使用 nameSort,它是 name 字段的副本(最后一行中的 copyField 命令),但是行为不同。注意,nameSort 是 string,而 name 是 text。默认索引模式将采取稍微不同的方式来处理这两种类型。<br />
字段 cat 是 multiValued。记录可以为此字段定义多个值。例如,如果应用程序管理内容,则可以给一篇文章指定多个标题。您可以使用 cat 字段(或者自定义类似字段)来捕捉所有标题。<br />
清单 4 显示了 example/exampledocs/ipod_other.xml 文件,该文件表示 iPod 附件分类中的两个条目。<br />
清单 4. 为默认 Solr 索引模式格式化的数据</p>
<p><add><br />
<doc><br />
<field name="id">F8V7067-APL-KIT</field><br />
<field name="name">Belkin Mobile Power Cord for iPod w/ Dock</field><br />
<field name="manu">Belkin</field><br />
<field name="cat">electronics</field><br />
<field name="cat">connector</field><br />
<field name="features">car power adapter, white</field><br />
<field name="weight">4</field><br />
<field name="price">19.95</field><br />
<field name="popularity">1</field><br />
<field name="inStock">false</field><br />
</doc><br />
<br />
<doc><br />
<field name="id">IW-02</field><br />
<field name="name">iPod & iPod Mini USB 2.0 Cable</field><br />
<field name="manu">Belkin</field><br />
<field name="cat">electronics</field><br />
<field name="cat">connector</field><br />
<field name="features">car power adapter for iPod, white</field><br />
<field name="weight">2</field><br />
<field name="price">11.50</field><br />
<field name="popularity">1</field><br />
<field name="inStock">false</field><br />
</doc><br />
</add><br />
add 元素是用于将封装记录添加到索引中的 Solr 命令。每条记录都将被捕捉到 doc 元素中,该元素将使用一组名为 field 的元素来指定字段值。字段 weight、price、inStock、manu、features 和 popularity 都是在默认 Solr 索引模式中定义的其他字段。features 字段拥有与 cat 相同的属性,但是语义不同:它列举了产品的功能,数量可能较多。<br />
搜索汽车零部件<br />
本例将索引汽车零部件集。每个汽车零部件都有多个字段,表 1 中显示了最重要的字段样例。第一列中列出了字段名。第二列将提供简要描述,而第三列将列出逻辑类型。第四列将显示用于表示数据的索引类型(按照 清单 5 的模式中的定义)。<br />
表 1. 汽车零部件记录的字段<br />
名称 描述 类型 Solr 类型<br />
部件号(惟一,强制) 标识号 字符串 partno<br />
名称 简要描述 字符串 name<br />
型号(必需,多值) 型号,例如 “Camaro” 字符串 model<br />
型号年份(多值) 型号年份,例如 2001 字符串 year<br />
价格 单价 浮点 price<br />
库存 是否有存货 布尔 inStock<br />
功能 零部件的功能 字符串 features<br />
时间标记 活动记录 字符串 timestamp<br />
重量 装运重量 浮点 weight<br />
清单 3 显示了汽车零部件索引所使用的 Solr 模式部分。它大部分都是基于默认 Solr 模式。使用的具体字段 —— 名称和属性 —— 只是替换了在默认模式中找到的 fields 元素(如 清单 1 中所示)。<br />
清单 5. 汽车零部件索引模式</p>
<p><?xml version="1.0" encoding="utf-8" ?><br />
<schema name="autoparts" version="1.0"><br />
...<br />
<fields><br />
<field name="partno" type="string" indexed="true"<br />
stored="true" required="true" /><br />
<br />
<field name="name" type="text" indexed="true"<br />
stored="true" required="true" /><br />
<br />
<field name="model" type="text_ws" indexed="true" stored="true"<br />
multiValued="true" required="true" /><br />
<br />
<field name="year" type="text_ws" indexed="true" stored="true"<br />
multiValued="true" omitNorms="true" /><br />
<br />
<field name="price" type="sfloat" indexed="true"<br />
stored="true" required="true" /><br />
<br />
<field name="inStock" type="boolean" indexed="true"<br />
stored="true" default="false" /><br />
<br />
<field name="features" type="text" indexed="true"<br />
stored="true" multiValued="true" /><br />
<br />
<field name="timestamp" type="date" indexed="true"<br />
stored="true" default="NOW" multiValued="false" /><br />
<br />
<field name="weight" type="sfloat" indexed="true" stored="true" /><br />
</fields><br />
<br />
<uniqueKey>partno</uniqueKey><br />
<br />
<defaultSearchField>name</defaultSearchField><br />
</schema><br />
针对以上字段,需要将汽车零部件数据库导出并格式化以上传到 Solr 中,如清单 6 所示。<br />
清单 6. 为进行索引而格式化的汽车零部件数据库</p>
<p><add><br />
<doc><br />
<field name="partno">1</field><br />
<field name="name">Spark plug</field><br />
<field name="model">Boxster</field><br />
<field name="model">924</field><br />
<field name="year">1999</field><br />
<field name="year">2000</field><br />
<field name="price">25.00</field><br />
<field name="inStock">true</field><br />
</doc><br />
<doc><br />
<field name="partno">2</field><br />
<field name="name">Windshield</field><br />
<field name="model">911</field><br />
<field name="year">1991</field><br />
<field name="year">1999</field><br />
<field name="price">15.00</field><br />
<field name="inStock">false</field><br />
</doc><br />
</add><br />
让我们安装新索引模式并把数据装入 Solr。首先,通过使用 Ctrl+C 组合键来停止 Solr 守护进程(如果它仍在运行)。在 example/solr/conf/schema.xml 中制作现有 Solr 模式的归档。接下来,通过清单 6 创建一个文本文件,将其保存到 /tmp/schema.xml 中,然后复制到 example/solr/conf/schema.xml 中。为清单 7 中所示的数据创建另一个文件。现在,您可以重新启动 Solr 并使用示例所提供的 posting 实用程序。<br />
清单 7. 启用带有新模式的 Solr</p>
<p>$ cd apache-solr-1.2/example<br />
$ cp solr/conf/schema.xml solr/conf/default_schema.xml<br />
$ chmod a-w solr/conf/default_schema.xml<br />
<br />
$ vi /tmp/schema.xml<br />
...<br />
$ cp /tmp/schema.xml solr/conf/schema.xml<br />
<br />
$ vi /tmp/parts.xml<br />
...<br />
<br />
$ java -jar start.jar<br />
...<br />
2007-11-11 16:56:48.279::INFO: Started SocketConnector @ 0.0.0.0:8983<br />
<br />
$ java -jar exampledocs/post.jar /tmp/parts.xml<br />
SimplePostTool: version 1.2<br />
SimplePostTool: WARNING: Make sure your XML documents are encoded in UTF-8, <br />
other encodings are not currently supported<br />
SimplePostTool: POSTing files to http://localhost:8983/solr/update...<br />
SimplePostTool: POSTing file parts.xml<br />
SimplePostTool: COMMITting Solr index changes...<br />
成功!如果需要检验索引是否存在并包含两个文档,请将浏览器再次指向 http://localhost:8983/solr/admin/。您应当会在页面顶部看到 “(autoparts)”。如果看到了,请单击页面中间的查询框并键入 partno: 1 or partno: 2。<br />
结果应当类似下面的内容:<br />
3 on 10 0 partno: 1 OR partno: 2 2.2<br />
true Boxster 924 Spark plug 1 25.0 2007-11-11T21:58:45.899Z 1999 2000<br />
false 911 Windshield 2 15.0 2007-11-11T21:58:45.953Z 1991 1999<br />
尝试其他一些查询。Lucene 维基中介绍了 Lucene 查询(Solr 中的搜索引擎)的语法(请参阅 参考资料)。<br />
您还应当尝试再次编辑和载入数据。由于声明了 partno 字段惟一,因此重复上传同一个部件号将只用新记录替换旧索引记录。除了 add 命令之外,您还可以使用 commit、optimize 和 delete。最后一个命令可以按照 ID 删除一条特定记录,也可以通过查询删除多条记录。<br />
现在使用 PHP<br />
最后,PHP 将参与到这个示例中。<br />
至少有两个 PHP Solr API。最健壮的实现是 Donovan Jimenez 的 PHP Solr Client(请参阅 参考资料)。代码是在与 Solr 相同的条款下许可开发的,它有扩展文档,并且与 Solr V1.2 兼容。撰写本文时的最新版本是在 2007 年 10 月 2 日发布的。<br />
Solr Client 将提供四个 PHP 类:<br />
Apache_Solr_Service 表示 Solr 服务器。使用这些方法来 ping 服务器,添加和删除文档,提交更改,优化索引,以及运行查询。<br />
Apache_Solr_Document 收录 Solr 文档。该类的方法将管理(关键字,值)对和多值字段。字段值可以通过直接解除引用来访问,例如 $document->title = 'Something'; ... echo $document->title;。<br />
Apache_Solr_Response 封装 Solr 响应。这段代码依赖于 json_decode() 函数,该函数是 PHP V5.2.0 和更高版本附带的,也可以用 PHP Extension Community Library(PECL —— 请参阅 参考资料)来安装。<br />
Apache_Solr_Service_Balancer 将增强 Apache_Solr_Service,允许您连接到一个分发中的多项 Solr 服务。本文中没有介绍该类。<br />
下载 PHP Solr Client(请参阅 参考资料)并将其解压缩到工作目录中。切换到 SolrPhpClient。接下来,查看文件 Apache/Solr/Service.php。撰写本文时,第 335 行缺少了拖尾分号。编辑该文件并添加分号(如果有必要)。另请查看文件 Apache/Solr/Document.php。在第 112 行至第 117 行中应当读出如下内容。<br />
if (!is_array($this->_fields[$key]))<br />
{<br />
$this->_fields[$key] = array($this->_fields[$key]);<br />
}<br />
<br />
$this->_fields[$key][] = $value;<br />
更正文件后,可以在其他 PHP 库旁边安装 Apache 目录。<br />
下列代码显示了连接 Solr 服务、向索引中添加两个文档和运行先前使用的零部件号查询的 PHP 应用程序。<br />
清单 8. 用于连接、载入和查询 Solr 索引的样例 PHP 应用程序</p>
<p><?php<br />
require_once( 'Apache/Solr/Service.php' );<br />
<br />
//<br />
//<br />
// Try to connect to the named server, port, and url<br />
//<br />
$solr = new Apache_Solr_Service( 'localhost', '8983', '/solr' );<br />
<br />
if ( ! $solr->ping() ) {<br />
echo 'Solr service not responding.';<br />
exit;<br />
}<br />
<br />
//<br />
//<br />
// Create two documents to represent two auto parts.<br />
// In practice, documents would likely be assembled from a<br />
// database query.<br />
//<br />
$parts = array(<br />
'spark_plug' => array(<br />
'partno' => 1,<br />
'name' => 'Spark plug',<br />
'model' => array( 'Boxster', '924' ),<br />
'year' => array( 1999, 2000 ),<br />
'price' => 25.00,<br />
'inStock' => true,<br />
),<br />
'windshield' => array(<br />
'partno' => 2,<br />
'name' => 'Windshield',<br />
'model' => '911',<br />
'year' => array( 1999, 2000 ),<br />
'price' => 15.00,<br />
'inStock' => false,<br />
)<br />
);<br />
<br />
$documents = array();<br />
<br />
foreach ( $parts as $item => $fields ) {<br />
$part = new Apache_Solr_Document();<br />
<br />
foreach ( $fields as $key => $value ) {<br />
if ( is_array( $value ) ) {<br />
foreach ( $value as $datum ) {<br />
$part->setMultiValue( $key, $datum );<br />
}<br />
}<br />
else {<br />
$part->$key = $value;<br />
}<br />
}<br />
<br />
$documents[] = $part;<br />
}<br />
<br />
//<br />
//<br />
// Load the documents into the index<br />
//<br />
try {<br />
$solr->addDocuments( $documents );<br />
$solr->commit();<br />
$solr->optimize();<br />
}<br />
catch ( Exception $e ) {<br />
echo $e->getMessage();<br />
}<br />
<br />
//<br />
//<br />
// Run some queries. Provide the raw path, a starting offset<br />
// for result documents, and the maximum number of result<br />
// documents to return. You can also use a fourth parameter<br />
// to control how results are sorted and highlighted,<br />
// among other options.<br />
//<br />
$offset = 0;<br />
$limit = 10;<br />
<br />
$queries = array(<br />
'partno: 1 OR partno: 2',<br />
'model: Boxster',<br />
'name: plug'<br />
);<br />
<br />
foreach ( $queries as $query ) {<br />
$response = $solr->search( $query, $offset, $limit );<br />
<br />
if ( $response->getHttpStatus() == 200 ) {<br />
// print_r( $response->getRawResponse() );<br />
<br />
if ( $response->response->numFound > 0 ) {<br />
echo "$query <br />";<br />
<br />
foreach ( $response->response->docs as $doc ) {<br />
echo "$doc->partno $doc->name <br />";<br />
}<br />
<br />
echo '<br />';<br />
}<br />
}<br />
else {<br />
echo $response->getHttpStatusMessage();<br />
}<br />
}<br />
?><br />
首先,代码将连接到给定端口和路径上名为 Solr 的服务器,并将使用 ping() 方法来检验服务器是否可运行。<br />
接下来,代码将把表示为 PHP 数组的记录转换为 Solr 文档。如果字段有单个值,则简单的访问程序将把(关键字,值)对添加到文档中。如果字段有多个值,则用特殊函数 setMultiValue() 把值列表赋给关键字。您可以看到此过程非常类似于 Solr 文档的 XML 表示。<br />
作为一项优化,addDocuments() 将把多个文档插入索引。后续的 commit() 和 optimize() 函数将完成添加操作。<br />
在底部,多个查询将从索引中检索数据。您可以通过两个函数查看结果:getRawResponse() 函数将生成完整的未解析结果,而 docs() 函数将返回带有指定访问程序的文档数组。<br />
如果查询未能从 Solr 获得确认,代码将输出错误消息。空结果集将不产生输出。<br />
结束语<br />
Solr 令人难以置信地强大,并且使用 PHP API 可以迅速地与任何平台集成。但更棒的是,Solr 易于安装和运行,并且可以根据需要启用高级功能。最棒的是,Solr 是免费的。不用购买搜索引擎。省下您的钱,使用 Solr 吧。<br />
访问 Solr Web 站点了解包括排序、分类结果和复制在内的高级配置的更多信息。Lucene Web 站点是另一个信息源,因为它是 Solr 系统下的搜索技术。</p>