使用 PHP 和 ApacheSolr 实现企业搜索

<p>安装 Solr<br /> Solr 是用 Java 技术实现的。要运行 Solr 及其管理工具,您必须安装 Java V1.5 软件开发包 (Java 5 SDK)。几个提供商都提供了 Java V1.5 SDK &mdash;&mdash;<br /> 例如,Sun Microsystems、IBM&reg; 和 BEA Systems &mdash;&mdash; 并且每个实现都能够驱动 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 &quot;1.5.0_13&quot;<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 &gt; 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 包含已经格式化的数据示例 &mdash;&mdash; 通常为 XML 代码 &mdash;&mdash; 并且准备好供 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>&lt;schema name=&quot;example&quot; version=&quot;1.1&quot;&gt;<br /> ...<br /> &lt;fields&gt;<br /> &lt;field name=&quot;id&quot; type=&quot;string&quot; indexed=&quot;true&quot; stored=&quot;true&quot; required=&quot;true&quot; /&gt;<br /> &lt;field name=&quot;name&quot; type=&quot;text&quot; indexed=&quot;true&quot; stored=&quot;true&quot;/&gt;<br /> &lt;field name=&quot;nameSort&quot; type=&quot;string&quot; indexed=&quot;true&quot; stored=&quot;false&quot;/&gt;<br /> &lt;field name=&quot;cat&quot; type=&quot;text&quot; indexed=&quot;true&quot; stored=&quot;true&quot; multiValued=&quot;true&quot;/&gt;<br /> ...<br /> &lt;/fields&gt;<br /> <br /> &lt;uniqueKey&gt;id&lt;/uniqueKey&gt;<br /> ...<br /> &lt;copyField source=&quot;name&quot; dest=&quot;nameSort&quot;/&gt;<br /> ...<br /> &lt;/schema&gt;<br /> 模式的大部分内容都无需加以说明,但是有一些方面需要注意:<br /> 如示,字段 id 是字符串 (type=&quot;string&quot;) 并且应当被索引 (indexed=&quot;true&quot;)。它也是一个必需字段 (required=&quot;true&quot;)。使用此模式,载入 Solr 的每条记录必须为这个字段提供值。&lt;uniqueKey&gt;id&lt;/uniqueKey&gt; 修饰词进一步说明 id 字段必须惟一(Solr 不要求 ID 字段惟一;这只是在默认索引模式中建立的规则)。属性 stored=&quot;true&quot; 表示 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>&lt;add&gt;<br /> &lt;doc&gt;<br /> &lt;field name=&quot;id&quot;&gt;F8V7067-APL-KIT&lt;/field&gt;<br /> &lt;field name=&quot;name&quot;&gt;Belkin Mobile Power Cord for iPod w/ Dock&lt;/field&gt;<br /> &lt;field name=&quot;manu&quot;&gt;Belkin&lt;/field&gt;<br /> &lt;field name=&quot;cat&quot;&gt;electronics&lt;/field&gt;<br /> &lt;field name=&quot;cat&quot;&gt;connector&lt;/field&gt;<br /> &lt;field name=&quot;features&quot;&gt;car power adapter, white&lt;/field&gt;<br /> &lt;field name=&quot;weight&quot;&gt;4&lt;/field&gt;<br /> &lt;field name=&quot;price&quot;&gt;19.95&lt;/field&gt;<br /> &lt;field name=&quot;popularity&quot;&gt;1&lt;/field&gt;<br /> &lt;field name=&quot;inStock&quot;&gt;false&lt;/field&gt;<br /> &lt;/doc&gt;<br /> <br /> &lt;doc&gt;<br /> &lt;field name=&quot;id&quot;&gt;IW-02&lt;/field&gt;<br /> &lt;field name=&quot;name&quot;&gt;iPod &amp; iPod Mini USB 2.0 Cable&lt;/field&gt;<br /> &lt;field name=&quot;manu&quot;&gt;Belkin&lt;/field&gt;<br /> &lt;field name=&quot;cat&quot;&gt;electronics&lt;/field&gt;<br /> &lt;field name=&quot;cat&quot;&gt;connector&lt;/field&gt;<br /> &lt;field name=&quot;features&quot;&gt;car power adapter for iPod, white&lt;/field&gt;<br /> &lt;field name=&quot;weight&quot;&gt;2&lt;/field&gt;<br /> &lt;field name=&quot;price&quot;&gt;11.50&lt;/field&gt;<br /> &lt;field name=&quot;popularity&quot;&gt;1&lt;/field&gt;<br /> &lt;field name=&quot;inStock&quot;&gt;false&lt;/field&gt;<br /> &lt;/doc&gt;<br /> &lt;/add&gt;<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 /> 型号(必需,多值) 型号,例如 &ldquo;Camaro&rdquo; 字符串 model<br /> 型号年份(多值) 型号年份,例如 2001 字符串 year<br /> 价格 单价 浮点 price<br /> 库存 是否有存货 布尔 inStock<br /> 功能 零部件的功能 字符串 features<br /> 时间标记 活动记录 字符串 timestamp<br /> 重量 装运重量 浮点 weight<br /> 清单 3 显示了汽车零部件索引所使用的 Solr 模式部分。它大部分都是基于默认 Solr 模式。使用的具体字段 &mdash;&mdash; 名称和属性 &mdash;&mdash; 只是替换了在默认模式中找到的 fields 元素(如 清单 1 中所示)。<br /> 清单 5. 汽车零部件索引模式</p> <p>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;<br /> &lt;schema name=&quot;autoparts&quot; version=&quot;1.0&quot;&gt;<br /> ...<br /> &lt;fields&gt;<br /> &lt;field name=&quot;partno&quot; type=&quot;string&quot; indexed=&quot;true&quot;<br /> stored=&quot;true&quot; required=&quot;true&quot; /&gt;<br /> <br /> &lt;field name=&quot;name&quot; type=&quot;text&quot; indexed=&quot;true&quot;<br /> stored=&quot;true&quot; required=&quot;true&quot; /&gt;<br /> <br /> &lt;field name=&quot;model&quot; type=&quot;text_ws&quot; indexed=&quot;true&quot; stored=&quot;true&quot;<br /> multiValued=&quot;true&quot; required=&quot;true&quot; /&gt;<br /> <br /> &lt;field name=&quot;year&quot; type=&quot;text_ws&quot; indexed=&quot;true&quot; stored=&quot;true&quot;<br /> multiValued=&quot;true&quot; omitNorms=&quot;true&quot; /&gt;<br /> <br /> &lt;field name=&quot;price&quot; type=&quot;sfloat&quot; indexed=&quot;true&quot;<br /> stored=&quot;true&quot; required=&quot;true&quot; /&gt;<br /> <br /> &lt;field name=&quot;inStock&quot; type=&quot;boolean&quot; indexed=&quot;true&quot;<br /> stored=&quot;true&quot; default=&quot;false&quot; /&gt;<br /> <br /> &lt;field name=&quot;features&quot; type=&quot;text&quot; indexed=&quot;true&quot;<br /> stored=&quot;true&quot; multiValued=&quot;true&quot; /&gt;<br /> <br /> &lt;field name=&quot;timestamp&quot; type=&quot;date&quot; indexed=&quot;true&quot;<br /> stored=&quot;true&quot; default=&quot;NOW&quot; multiValued=&quot;false&quot; /&gt;<br /> <br /> &lt;field name=&quot;weight&quot; type=&quot;sfloat&quot; indexed=&quot;true&quot; stored=&quot;true&quot; /&gt;<br /> &lt;/fields&gt;<br /> <br /> &lt;uniqueKey&gt;partno&lt;/uniqueKey&gt;<br /> <br /> &lt;defaultSearchField&gt;name&lt;/defaultSearchField&gt;<br /> &lt;/schema&gt;<br /> 针对以上字段,需要将汽车零部件数据库导出并格式化以上传到 Solr 中,如清单 6 所示。<br /> 清单 6. 为进行索引而格式化的汽车零部件数据库</p> <p>&lt;add&gt;<br /> &lt;doc&gt;<br /> &lt;field name=&quot;partno&quot;&gt;1&lt;/field&gt;<br /> &lt;field name=&quot;name&quot;&gt;Spark plug&lt;/field&gt;<br /> &lt;field name=&quot;model&quot;&gt;Boxster&lt;/field&gt;<br /> &lt;field name=&quot;model&quot;&gt;924&lt;/field&gt;<br /> &lt;field name=&quot;year&quot;&gt;1999&lt;/field&gt;<br /> &lt;field name=&quot;year&quot;&gt;2000&lt;/field&gt;<br /> &lt;field name=&quot;price&quot;&gt;25.00&lt;/field&gt;<br /> &lt;field name=&quot;inStock&quot;&gt;true&lt;/field&gt;<br /> &lt;/doc&gt;<br /> &lt;doc&gt;<br /> &lt;field name=&quot;partno&quot;&gt;2&lt;/field&gt;<br /> &lt;field name=&quot;name&quot;&gt;Windshield&lt;/field&gt;<br /> &lt;field name=&quot;model&quot;&gt;911&lt;/field&gt;<br /> &lt;field name=&quot;year&quot;&gt;1991&lt;/field&gt;<br /> &lt;field name=&quot;year&quot;&gt;1999&lt;/field&gt;<br /> &lt;field name=&quot;price&quot;&gt;15.00&lt;/field&gt;<br /> &lt;field name=&quot;inStock&quot;&gt;false&lt;/field&gt;<br /> &lt;/doc&gt;<br /> &lt;/add&gt;<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/。您应当会在页面顶部看到 &ldquo;(autoparts)&rdquo;。如果看到了,请单击页面中间的查询框并键入 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-&gt;title = &#39;Something&#39;; ... echo $document-&gt;title;。<br /> Apache_Solr_Response 封装 Solr 响应。这段代码依赖于 json_decode() 函数,该函数是 PHP V5.2.0 和更高版本附带的,也可以用 PHP Extension Community Library(PECL &mdash;&mdash; 请参阅 参考资料)来安装。<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-&gt;_fields[$key]))<br /> {<br /> $this-&gt;_fields[$key] = array($this-&gt;_fields[$key]);<br /> }<br /> <br /> $this-&gt;_fields[$key][] = $value;<br /> 更正文件后,可以在其他 PHP 库旁边安装 Apache 目录。<br /> 下列代码显示了连接 Solr 服务、向索引中添加两个文档和运行先前使用的零部件号查询的 PHP 应用程序。<br /> 清单 8. 用于连接、载入和查询 Solr 索引的样例 PHP 应用程序</p> <p>&lt;?php<br /> require_once( &#39;Apache/Solr/Service.php&#39; );<br /> <br /> //<br /> //<br /> // Try to connect to the named server, port, and url<br /> //<br /> $solr = new Apache_Solr_Service( &#39;localhost&#39;, &#39;8983&#39;, &#39;/solr&#39; );<br /> <br /> if ( ! $solr-&gt;ping() ) {<br /> echo &#39;Solr service not responding.&#39;;<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 /> &#39;spark_plug&#39; =&gt; array(<br /> &#39;partno&#39; =&gt; 1,<br /> &#39;name&#39; =&gt; &#39;Spark plug&#39;,<br /> &#39;model&#39; =&gt; array( &#39;Boxster&#39;, &#39;924&#39; ),<br /> &#39;year&#39; =&gt; array( 1999, 2000 ),<br /> &#39;price&#39; =&gt; 25.00,<br /> &#39;inStock&#39; =&gt; true,<br /> ),<br /> &#39;windshield&#39; =&gt; array(<br /> &#39;partno&#39; =&gt; 2,<br /> &#39;name&#39; =&gt; &#39;Windshield&#39;,<br /> &#39;model&#39; =&gt; &#39;911&#39;,<br /> &#39;year&#39; =&gt; array( 1999, 2000 ),<br /> &#39;price&#39; =&gt; 15.00,<br /> &#39;inStock&#39; =&gt; false,<br /> )<br /> );<br /> <br /> $documents = array();<br /> <br /> foreach ( $parts as $item =&gt; $fields ) {<br /> $part = new Apache_Solr_Document();<br /> <br /> foreach ( $fields as $key =&gt; $value ) {<br /> if ( is_array( $value ) ) {<br /> foreach ( $value as $datum ) {<br /> $part-&gt;setMultiValue( $key, $datum );<br /> }<br /> }<br /> else {<br /> $part-&gt;$key = $value;<br /> }<br /> }<br /> <br /> $documents[] = $part;<br /> }<br /> <br /> //<br /> //<br /> // Load the documents into the index<br /> //<br /> try {<br /> $solr-&gt;addDocuments( $documents );<br /> $solr-&gt;commit();<br /> $solr-&gt;optimize();<br /> }<br /> catch ( Exception $e ) {<br /> echo $e-&gt;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 /> &#39;partno: 1 OR partno: 2&#39;,<br /> &#39;model: Boxster&#39;,<br /> &#39;name: plug&#39;<br /> );<br /> <br /> foreach ( $queries as $query ) {<br /> $response = $solr-&gt;search( $query, $offset, $limit );<br /> <br /> if ( $response-&gt;getHttpStatus() == 200 ) {<br /> // print_r( $response-&gt;getRawResponse() );<br /> <br /> if ( $response-&gt;response-&gt;numFound &gt; 0 ) {<br /> echo &quot;$query &lt;br /&gt;&quot;;<br /> <br /> foreach ( $response-&gt;response-&gt;docs as $doc ) {<br /> echo &quot;$doc-&gt;partno $doc-&gt;name &lt;br /&gt;&quot;;<br /> }<br /> <br /> echo &#39;&lt;br /&gt;&#39;;<br /> }<br /> }<br /> else {<br /> echo $response-&gt;getHttpStatusMessage();<br /> }<br /> }<br /> ?&gt;<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>
RangeTime:0.007575s
RangeMem:227.58 KB
返回顶部 留言