﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-小菜毛毛技术分享-随笔分类-搜索引擎</title><link>http://www.blogjava.net/caizh2009/category/41441.html</link><description>与大家共同成长</description><language>zh-cn</language><lastBuildDate>Sun, 27 Sep 2009 09:05:24 GMT</lastBuildDate><pubDate>Sun, 27 Sep 2009 09:05:24 GMT</pubDate><ttl>60</ttl><item><title>nutch初体验</title><link>http://www.blogjava.net/caizh2009/archive/2009/09/24/296272.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Thu, 24 Sep 2009 04:58:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/archive/2009/09/24/296272.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/296272.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/archive/2009/09/24/296272.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/296272.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/296272.html</trackback:ping><description><![CDATA[<div class="asset-body">
<p>前几天看到卢亮的 <a href="http://www.wespoke.com/archives/000879.php">Larbin 一种高效的搜索引擎爬虫工具</a> 一文提到 <a href="http://www.nutch.org/">Nutch</a>，很是感兴趣，但一直没有时间进行测试研究。趁着假期，先测试一下看看。用搜索引擎查找了一下，发现中文技术社区对 <a href="http://larbin.sourceforge.net/index-eng.html">Larbin</a> 的关注要远远大于 Nutch 。只有一年多前何东在他的<a href="http://hedong.3322.org/">竹笋炒肉</a>中对 Nutch 进行了一下<a href="http://hedong.3322.org/archives/000247.html">介绍</a>。</p>
</div>
<div class="asset-more" id="more">
<p><strong>Nutch vs Lucene</strong> <br />
Lucene 不是完整的应用程序，而是一个用于实现全文检索的软件库。<br />
Nutch 是一个应用程序，可以以 Lucene 为基础实现搜索引擎应用。</p>
<p><strong>Nutch vs GRUB</strong><br />
<a href="http://www.grub.org/">GRUB</a> 是一个分布式搜索引擎(<a href="http://www.dbanotes.net/archives/2004/12/grubwebce.html">参考</a>)。用户只能得到客户端工具(只有客户端是开源的)，其目的在于利用用户的资源建立集中式的搜索引擎。<br />
Nutch 是开源的，可以建立自己内部网的搜索引擎，也可以针对整个网络建立搜索引擎。自由(Free)而免费(Free)。</p>
<p><strong>Nutch vs Larbin</strong><br />
"Larbin只是一个爬虫，也就是说larbin只抓取网页，至于如何parse的事情则由用户自己完成。另外，如何存储到数据库以及建立索引的事情 larbin也不提供。［引自<a href="http://www.wespoke.com/archives/000879.php">这里</a>］<br />
Nutch 则还可以存储到数据库并建立索引。<br />
<img height="220" alt="Nutch Architecture.png" src="http://www.dbanotes.net/archives/images/Nutch%20Architecture.png" width="316" /> <br />
［引自<a href="http://www.nutch.org/twiki/Main/Presentations/www2004.pdf">这里</a>］ </p>
<p>Nutch 的早期版本不支持中文搜索，而最新的版本(2004-Aug-04 发布了 <a href="http://www.nutch.org/release/">0.5</a>)已经做了很大的改进。相对先前的 0.4 版本，有 20 多项的改进，结构上也更具备扩展性。0.5 版经过测试，对中文搜索支持的也很好。</p>
<p>下面是我的测试过程。</p>
<p>前提条件(这里Linux 为例，如果是 Windows 参见手册)：</p>
<ul>
    <li>Java 1.4.x 。因为我的系统上安装的Oracle 10g 已经有 Java 了。设定环境变量：NUTCH_JAVA_HOME 。
    <blockquote>
    <pre>[root@fc3 ~]# export NUTCH_JAVA_HOME=/u01/app/oracle/product/10.1.0/db_1/jdk/jre
    </pre>
    </blockquote>
    <li>Tomcat 4.x 。从<a href="http://jakarta.apache.org/tomcat/">这里</a>下载。
    <li>足够的磁盘空间。我预留了 4G 的空间。 </li>
</ul>
<p>首先下载最新的稳定版：</p>
<blockquote>
<pre>[root@fc3 ~]# wget http://www.nutch.org/release/nutch-0.5.tar.gz
</pre>
</blockquote>
<p>解压缩:</p>
<blockquote>
<pre>[root@fc3 ~]# tar -zxvf nutch-0.5.tar.gz
......
[root@fc3 ~]# mv nutch-0.5 nutch
</pre>
</blockquote>
<p>测试一下 nutch 命令：</p>
<blockquote>
<pre>[root@fc3 nutch]# bin/nutch
Usage: nutch COMMAND
where COMMAND is one of:
crawl             one-step crawler for intranets
admin             database administration, including creation
inject            inject new urls into the database
generate          generate new segments to fetch
fetchlist         print the fetchlist of a segment
fetch             fetch a segment's pages
dump              dump a segment's pages
index             run the indexer on a segment's fetcher output
merge             merge several segment indexes
dedup             remove duplicates from a set of segment indexes
updatedb          update database from a segment's fetcher output
mergesegs         merge multiple segments into a single segment
readdb            examine arbitrary fields of the database
analyze           adjust database link-analysis scoring
server            run a search server
or
CLASSNAME         run the class named CLASSNAME
Most commands print help when invoked w/o parameters.
[root@fc3 nutch]#
</pre>
</blockquote>
<p>Nutch 的爬虫有两种方式</p>
<ul>
    <li>爬行企业内部网(Intranet crawling)。针对少数网站进行。用 <em>crawl</em> 命令。
    <li>爬行整个互联网。 使用低层的 <em>inject, generate, fetch</em> 和 <em>updatedb</em> 命令。具有更强的可控制性。 </li>
</ul>
<p>以本站(http://www.dbanotes.net)为例，先进行一下针对企业内部网的测试。</p>
在 nutch 目录中创建一个包含该网站顶级网址的文件 urls ，包含如下内容：
<blockquote>
<pre>http://www.dbanotes.net/
</pre>
</blockquote>
<p>然后编辑conf/crawl-urlfilter.txt 文件，设定过滤信息，我这里只修改了MY.DOMAIN.NAME:</p>
<blockquote>
<pre># accept hosts in MY.DOMAIN.NAME
+^http://([a-z0-9]*\.)*dbanotes.net/
</pre>
</blockquote>
<p>运行如下命令开始抓取分析网站内容：</p>
<blockquote>
<pre>[root@fc3 nutch]# bin/nutch crawl urls -dir crawl.demo -depth 2 -threads 4 &gt;&amp; crawl.log
</pre>
</blockquote>
<p>depth 参数指爬行的深度，这里处于测试的目的，选择深度为 2 ；<br />
threads 参数指定并发的进程 这是设定为 4 ；</p>
<p>在该命令运行的过程中，可以从 crawl.log 中查看 nutch 的行为以及过程:</p>
<blockquote>
<pre>......
050102 200336 loading file:/u01/nutch/conf/nutch-site.xml
050102 200336 crawl started in: crawl.demo
050102 200336 rootUrlFile = urls
050102 200336 threads = 4
050102 200336 depth = 2
050102 200336 Created webdb at crawl.demo/db
......
050102 200336 loading file:/u01/nutch/conf/nutch-site.xml
050102 200336 crawl started in: crawl.demo
050102 200336 rootUrlFile = urls
050102 200336 threads = 4
050102 200336 depth = 2
050102 200336 Created webdb at crawl.demo/db
050102 200336 Starting <acronym title="Unified Resource Locator&#13;">URL</acronym> processing
050102 200336 <strong>Using <acronym title="Unified Resource Locator&#13;">URL</acronym> filter: net.nutch.net.RegexURLFilter</strong>
......
050102 200337 Plugins: looking in: /u01/nutch/plugins
050102 200337 parsing: /u01/nutch/plugins/parse-html/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/parse-pdf/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/parse-ext/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/parse-msword/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/query-site/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/protocol-http/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/creativecommons/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/language-identifier/plugin.xml
050102 200337 parsing: /u01/nutch/plugins/query-basic/plugin.xml
050102 200337 logging at INFO
050102 200337 fetching http://www.dbanotes.net/
050102 200337 http.proxy.host = null
050102 200337 http.proxy.port = 8080
050102 200337 http.timeout = 10000
050102 200337 http.content.limit = 65536
050102 200337 http.agent = NutchCVS/0.05 (Nutch; http://www.nutch.org/docs/en/bot.html; n
utch-agent@lists.sourceforge.net)
050102 200337 fetcher.server.delay = 1000
050102 200337 http.max.delays = 100
050102 200338 http://www.dbanotes.net/: setting encoding to GB18030
050102 200338 <acronym title="Creative Commons&#13;">CC</acronym>: found http://creativecommons.org/licenses/by-nc-sa/2.0/ in rdf of http:
//www.dbanotes.net/
050102 200338 <acronym title="Creative Commons&#13;">CC</acronym>: found text in http://www.dbanotes.net/
050102 200338 status: 1 pages, 0 errors, 12445 bytes, 1067 ms
050102 200338 status: 0.9372071 pages/s, 91.12142 kb/s, 12445.0 bytes/page
050102 200339 Updating crawl.demo/db
050102 200339 Updating for crawl.demo/segments/20050102200336
050102 200339 Finishing update
64,1           7%
050102 200337 parsing: /u01/nutch/plugins/query-basic/plugin.xml
050102 200337 logging at INFO
050102 200337 fetching http://www.dbanotes.net/
050102 200337 http.proxy.host = null
050102 200337 http.proxy.port = 8080
050102 200337 http.timeout = 10000
050102 200337 http.content.limit = 65536
050102 200337 http.agent = NutchCVS/0.05 (Nutch; http://www.nutch.org/docs/en/bot.html;
nutch-agent@lists.sourceforge.net)
050102 200337 fetcher.server.delay = 1000
050102 200337 http.max.delays = 100
......
</pre>
</blockquote>
<p>之后配置 Tomcat (我的 tomcat 安装在 /opt/Tomcat) ， </p>
<blockquote>
<pre>[root@fc3 nutch]# rm -rf /opt/Tomcat/webapps/ROOT*
[root@fc3 nutch]# cp nutch*.war /opt/Tomcat/webapps/ROOT.war
[root@fc3 webapps]# cd /opt/Tomcat/webapps/
[root@fc3 webapps]# jar xvf ROOT.war
[root@fc3 webapps]# ../bin/catalina.sh start
</pre>
</blockquote>
<p>浏览器中输入 http://localhost:8080 查看结果(远程查看需要将 localhost 换成相应的IP)：</p>
<img height="360" alt="nutch web search interface.png" src="http://www.dbanotes.net/archives/images/nutch%20web%20search%20interface.png" width="648" />
<p>搜索测试：</p>
<img height="367" alt="nutch web search result.png" src="http://www.dbanotes.net/archives/images/nutch%20web%20search%20result.png" width="650" />
<p>可以看到，Nutch 亦提供快照功能。下面进行中文搜索测试:</p>
<img height="380" alt="nutch web Chinese search result.png" src="http://www.dbanotes.net/archives/images/nutch%20web%20Chinese%20search%20result.png" width="654" />
<p>注意结果中的那个&#8220;评分详解&#8221;，是个很有意思的功能(Nutch 具有一个链接分析模块)，通过这些数据可以进一步理解该算法。</p>
<p>考虑到带宽的限制，暂时不对整个Web爬行的方式进行了测试了。值得一提的是，在测试的过程中，nutch 的爬行速度还是不错的(相对我的糟糕带宽)。</p>
<p>Nutch 目前还不支持 <strong><acronym title="Portable Document Format&#13;">PDF</acronym></strong>(开发中，不够完善) 与 <strong>图片</strong> 等对象的搜索。<strong>中文分词技术</strong>还不够好，通过&#8220;评分详解&#8221;可看出，对中文，比如&#8220;数据库管理员&#8221;，是分成单独的字进行处理的。但作为一个开源搜索引擎软件，功能是可圈可点的。毕竟，主要开发者 <a href="http://www.nutch.org/blog/cutting.html">Doug Cutting</a> 就是开发 <a href="http://jakarta.apache.org/lucene/">Lucene</a> 的大牛</p>
<p>参考信息</p>
<ul>
    <li><a href="http://www.nutch.org/cgi-bin/twiki/view/Main/Nutch">Nutch Wiki</a> - http://www.nutch.org/cgi-bin/twiki/view/Main/Nutch
    <li>何东的 <a href="http://hedong.3322.org/archives/000247.html">试用Nutch</a>
    <li>车东的 <a href="http://www.chedong.com/tech/lucene.html">Lucene：基于Java的全文检索引擎简介</a> </li>
</ul>
</div>
<img src ="http://www.blogjava.net/caizh2009/aggbug/296272.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-09-24 12:58 <a href="http://www.blogjava.net/caizh2009/archive/2009/09/24/296272.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>http://www.lucene.com.cn/ LUCENE.COM.CN 中国 </title><link>http://www.blogjava.net/caizh2009/archive/2009/09/24/296270.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Thu, 24 Sep 2009 04:54:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/archive/2009/09/24/296270.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/296270.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/archive/2009/09/24/296270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/296270.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/296270.html</trackback:ping><description><![CDATA[ <img src ="http://www.blogjava.net/caizh2009/aggbug/296270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-09-24 12:54 <a href="http://www.blogjava.net/caizh2009/archive/2009/09/24/296270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> nutch和lucene的区别</title><link>http://www.blogjava.net/caizh2009/archive/2009/09/24/296269.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Thu, 24 Sep 2009 04:53:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/archive/2009/09/24/296269.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/296269.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/archive/2009/09/24/296269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/296269.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/296269.html</trackback:ping><description><![CDATA[<p>想做一个搜索引擎，最近浏览了许多社区，发现Lucene和Nutch用的很多，而这两个我总感觉难以区分概念，于是在查了些资料。下面是Lucene和Nutch创始人Doug Cutting 的访谈摘录：</p>
<p>&nbsp;Lucene其实是一个提供全文文本搜索的函数库，它不是一个应用软件。它提供很多API函数让你可以运用到各种实际应用程序中。现在，它已经成为Apache的一个项目并被广泛应用着。这里列出一些已经使用Lucene的系统。</p>
<p>Nutch是一个建立在Lucene核心之上的Web搜索的实现，它是一个真正的应用程序。也就是说，你可以直接下载下来拿过来用。它在Lucene的基础上加了网络爬虫和一些和Web相关的东东。其目的就是想从一个简单的站内索引和搜索推广到全球网络的搜索上，就像Google和Yahoo一样。当然，和那些巨人竞争，你得动一些脑筋，想一些办法。我们已经测试过100M的网页，并且它的设计用在超过1B的网页上应该没有问题。当然，让它运行在一台机器上，搜索一些服务器，也运行的很好。</p>
<p>&nbsp;&nbsp; 总的来说，我认为LUCENE会应用在本地服务器的网站内部搜索，而Nutch则扩展到整个网络、Internet的检索。当然LUCENE加上爬虫程序等就会成为Nutch，这样理解应该没错吧</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/rokii/archive/2008/03/01/2137450.aspx</p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/296269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-09-24 12:53 <a href="http://www.blogjava.net/caizh2009/archive/2009/09/24/296269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Apache Lucene 搜索文本</title><link>http://www.blogjava.net/caizh2009/archive/2009/09/17/295491.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Thu, 17 Sep 2009 11:45:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/archive/2009/09/17/295491.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/295491.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/archive/2009/09/17/295491.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/295491.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/295491.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 轻松为应用程序构建搜索和索引功能                                                                                                                                                          ...&nbsp;&nbsp;<a href='http://www.blogjava.net/caizh2009/archive/2009/09/17/295491.html'>阅读全文</a><img src ="http://www.blogjava.net/caizh2009/aggbug/295491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-09-17 19:45 <a href="http://www.blogjava.net/caizh2009/archive/2009/09/17/295491.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Java 开源工具建立一个灵活的搜索引擎</title><link>http://www.blogjava.net/caizh2009/archive/2009/08/29/293035.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Fri, 28 Aug 2009 16:25:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/archive/2009/08/29/293035.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/293035.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/archive/2009/08/29/293035.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/293035.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/293035.html</trackback:ping><description><![CDATA[<span style="line-height: 1.3em; font-family: '宋体'">为应用程序添加搜索能力经常是一个常见的需求。本文介绍了一个框架，开发者可以使用它以最小的付出实现搜索引擎功能，理想情况下只需要一个配置文件。该框架基于若干开源的库和工具，如 Apache Lucene，Spring 框架，cpdetector 等。它支持多种资源。其中两个典型的例子是数据库资源和文件系统资源。Indexer 对配置的资源进行索引并传输到中央服务器，之后这些索引可以通过 API 进行搜索。Spring 风格的配置文件允许清晰灵活的自定义和调整。核心 API 也提供了可扩展的接口。 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">引言</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">为应用程序添加搜索能力经常是一个常见的需求。尽管已经有若干程序库提供了对搜索基础设施的支持，然而对于很多人而言，使用它们从头开始建立一个搜索引擎将是一个付出不小而且可能乏味的过程。另一方面，很多的小型应用对于搜索功能的需求和应用场景具有很大的相似性。本文试图以对多数小型应用的适用性为出发点，用 Java 语言构建一个灵活的搜索引擎框架。使用这个框架，多数情形下可以以最小的付出建立起一个搜索引擎。最理想的情况下，甚至只需要一个配置文件。特殊的情形下，可以通过灵活地对框架进行扩展满足需求。当然，如题所述，这都是借助开源工具的力量。 </span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">基础知识</span><wbr><br />
<br />
Apache Lucene <span style="line-height: 1.3em; font-family: '宋体'">是开发搜索类应用程序时最常用的 Java 类库，我们的框架也将基于它。为了下文更好的描述，我们需要先了解一些有关 Lucene 和搜索的基础知识。注意，本文不关注索引的文件格式、分词技术等话题。 </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">什么是搜索和索引</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">从用户的角度来看，搜索的过程是通过关键字在某种资源中寻找特定的内容的过程。而从计算机的角度来看，实现这个过程可以有两种办法。一是对所有资源逐个与关键字匹配，返回所有满足匹配的内容；二是如同字典一样事先建立一个对应表，把关键字与资源的内容对应起来，搜索时直接查找这个表即可。显而易见，第二个办法效率要高得多。建立这个对应表事实上就是建立逆向索引（inverted index）的过程。 </span><wbr><br />
Lucene <span style="line-height: 1.3em; font-family: '宋体'">基本概念</span><wbr><br />
<br />
Lucene <span style="line-height: 1.3em; font-family: '宋体'">是 Doug Cutting 用 Java 开发的用于全文搜索的工具库。在这里，我假设读者对其已有基本的了解，我们只对一些重要的概念简要介绍。要深入了解可以参考 参考资源 中列出的相关文章和图书。下面这些是 Lucene 里比较重要的类。 </span><wbr><br />
Document<span style="line-height: 1.3em; font-family: '宋体'">：索引包含多个 Document。而每个 Document 则包含多个 Field 对象。Document 可以是从数据库表里取出的一堆数据，可以是一个文件，也可以是一个网页等。注意，它不等同于文件系统中的文件。 </span><wbr><br />
Field<span style="line-height: 1.3em; font-family: '宋体'">：一个 Field 有一个名称，它对应 Document的一部分数据，表示文档的内容或者文档的元数据（与下文中提到的资源元数据不是一个概念）。一个 Field 对象有两个重要属性：Store ( 可以有 YES, NO, COMPACT 三种取值 ) 和 Index ( 可以有 TOKENIZED, UN_TOKENIZED, NO, NO_NORMS 四种取值 ) </span><wbr><br />
Query<span style="line-height: 1.3em; font-family: '宋体'">：抽象了搜索时使用的语句。 </span><wbr><br />
IndexSearcher<span style="line-height: 1.3em; font-family: '宋体'">：提供Query对象给它，它利用已有的索引进行搜索并返回搜索结果。 </span><wbr><br />
Hits<span style="line-height: 1.3em; font-family: '宋体'">：一个容器，包含了指向一部分搜索结果的指针。 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">使用 Lucene 来进行编制索引的过程大致为：将输入的数据源统一为字符串或者文本流的形式，然后从数据源提取数据，创建合适的 Field 添加到对应该数据源的 Document 对象之中。 </span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">系统概览</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">要建立一个通用的框架，必须对不同情况的共性进行抽象。反映到设计需要注意两点。一是要提供扩展接口；二是要尽量降低模块之间的耦合程度。我们的框架很简单地分为两个模块：索引模块和搜索模块。索引模块在不同的机器上各自进行对资源的索引，并把索引文件（事实上，下面我们会说到，还有元数据）统一传输到同一个地方（可以是在远程服务器上，也可以是在本地）。搜索模块则利用这些从多个索引模块收集到的数据完成用户的搜索请求。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">图 1 展现了整体的框架。可以看到，两个模块之间相对是独立的，它们之间的关联不是通过代码，而是通过索引和元数据。在下文中，我们将会详细介绍如何基于开源工具设计和实现这两个模块。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">图 1. 系统架构图</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">建立索引</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">可以进行索引的对象有很多，如文件、网页、RSS Feed 等。在我们的框架中，我们定义可以进行索引的一类对象为资源。从实现细节上来说，从一个资源中可以提取出多个 Document 对象。文件系统资源和数据库结果集资源都是资源的代表性例子。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">前面提到，从资源中收集到的索引被统一传送到同一个地方，以被搜索模块所用。显然除了索引之外，搜索模块需要有对资源更多的了解，如资源的名称、搜索该资源后搜索结果的呈现格式等。这些额外的附加信息称为资源的元数据。元数据和索引数据一同被收集起来，放置到某个特定的位置。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">简要地介绍过资源的概念之后，我们首先为其定义一个 Resource 接口。这个接口的声明如下。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">清单 1. Resource 接口</span><wbr><br />
public interface Resource {<br />
// RequestProcessor <span style="line-height: 1.3em; font-family: '宋体'">对象被动地从资源中提取 Document，并返回提取的数量</span><wbr><br />
public int extractDocuments(ResourceProcessor processor); <br />
<br />
// <span style="line-height: 1.3em; font-family: '宋体'">添加的 DocumentListener 将在每一个 Document 对象被提取出时被调用</span><wbr><br />
public void addDocumentListener(DocumentListener l); <br />
<br />
// <span style="line-height: 1.3em; font-family: '宋体'">返回资源的元数据</span><wbr><br />
public ResourceMetaData getMetaData(); <br />
} <br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">其中元数据包含的字段见下表。在下文中，我们还会对元数据的用途做更多的介绍。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">表 1. 资源元数据包含的字段</span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">属性 类型 含义 </span><wbr><br />
resourceName String <span style="line-height: 1.3em; font-family: '宋体'">资源的唯一名称 </span><wbr><br />
resourceDescription String <span style="line-height: 1.3em; font-family: '宋体'">资源的介绍性文字 </span><wbr><br />
hitTextPattern String <span style="line-height: 1.3em; font-family: '宋体'">当文档被搜索到时，这个 pattern 规定了结果显示的格式 </span><wbr><br />
searchableFields String[] <span style="line-height: 1.3em; font-family: '宋体'">可以被搜索的字段名称 </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">而 DocumentListener 的代码如下。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">清单 2. DocumentListener 接口</span><wbr><br />
public interface DocumentListener extends EventListener {<br />
public void documentExtracted(Document doc); <br />
} <br />
<br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">为了让索引模块能够知道所有需要被索引的资源，我们在这里使用 Spring 风格的 XML 文件配置索引模块中的所有组件，尤其是所有资源。您可以在 下载部分 查看一个示例配置文件。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">为什么选择使用 Spring 风格的配置文件？</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">这主要有两个好处： </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">仅依赖于 Spring Core 和 Spring Beans 便免去了定义配置机制和解析配置文件的负担； </span><wbr><br />
Spring <span style="line-height: 1.3em; font-family: '宋体'">的 IoC 机制降低了框架的耦合性，并使扩展框架变得简单； </span><wbr><br />
<br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">基于以上内容，我们可以大致描述出索引模块工作的过程： </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">首先在 XML 配置的 bean 中找出所有 Resource 对象； </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">对每一个调用其 extractDocuments() 方法，这一步除了完成对资源的索引外，还会在每次提取出一个 Document 对象之后，通知注册在该资源上的所有 DocumentListener； </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">接着处理资源的元数据（getMetaData() 的返回值）； </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">将缓存里的数据写入到本地磁盘或者传送给远程服务器； </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">在这个过程中，有两个地方值得注意。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">第一，对资源可以注册 DocumentListener 使得我们可以在运行时刻对索引过程有更为动态的控制。举一个简单例子，对某个文章发布站点的文章进行索引时，一个很正常的要求便是发布时间更靠近当前时间的文章需要在搜索结果中排在靠前的位置。每篇文章显然对应一个 Document 对象，在 Lucene 中我们可以通过设置 Document 的 boost 值来对其进行加权。假设其中文章发布时间的 Field 的名称为 PUB_TIME，那么我们可以为资源注册一个 DocumentListener，当它被通知时，则检测 PUB_TIME 的值，根据距离当前时间的远近进行加权。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">第二点很显然，在这个过程中，extractDocuments() 方法的实现依不同类型的资源而各异。下面我们主要讨论两种类型的资源：文件系统资源和数据库结果集资源。这两个类都实现了上面的 接口。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">文件系统资源</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">对文件系统资源的索引通常从一个基目录开始，递归处理每个需要进行索引的文件。该资源有一个字符串数组类型的 excludedFiles 属性，表示在处理文件时需要排除的文件绝对路径的正则表达式。在递归遍历文件系统树的同时，绝对路径匹配 excludedFiles 中任意一项的文件将不会被处理。这主要是考虑到一般我们只需要对一部分文件夹（比如排除可能存在的备份目录）中的一部分文件（如 doc, ppt 文件等）进行索引。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">除了所有文件共有的文件名、文件路径、文件大小和修改时间等 Field，不同类型的文件需要有不同的处理方法。为了保留灵活性，我们使用 Strategy 模式封装对不同类型文件的处理方式。为此我们抽象出一个 DocumentBuilder 的接口，该接口仅定义了一个方法如下：</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">清单 3. DocumentBuilder 接口</span><wbr><br />
public interface DocumentBuilder {<br />
Document buildDocument(InputStream is); <br />
} <br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">什么是 Strategy 模式？</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">根据 Design patterns: Elements of reusable object orientated software 一书：Strategy 模式&#8220;定义一系列的算法，把它们分别封装起来，并且使它们相互可以替换。这个模式使得算法可以独立于使用它的客户而变化。&#8221;</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">不同的 DocumentBuilder（Strategy） 用于从一个输入流中读取数据，处理不同类型的文件。对于常见的文件格式来说，都有合适的开源工具帮助进行解析。在下表中我们列举一些常见文件类型的解析办法。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">文件类型 常用扩展名 可以使用的解析办法 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">纯文本文档 txt 无需类库解析 </span><wbr><br />
RTF <span style="line-height: 1.3em; font-family: '宋体'">文档 rtf 使用 javax.swing.text.rtf.RTFEditorKit 类 </span><wbr><br />
Word <span style="line-height: 1.3em; font-family: '宋体'">文档（非 OOXML 格式） doc Apache POI （可配合使用 POI Scratchpad） </span><wbr><br />
PowerPoint <span style="line-height: 1.3em; font-family: '宋体'">演示文稿（非 OOXML 格式） xls Apache POI （可配合使用 POI Scratchpad） </span><wbr><br />
PDF <span style="line-height: 1.3em; font-family: '宋体'">文档 pdf PDFBox（可能中文支持欠佳） </span><wbr><br />
HTML <span style="line-height: 1.3em; font-family: '宋体'">文档 htm, html JTidy, Cobra </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">这里以 Word 文件为例，给出一个简单的参考实现。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">清单 4. 解析纯文本内容的实现</span><wbr><br />
// WordDocument <span style="line-height: 1.3em; font-family: '宋体'">是 Apache POI Scratchpad 中的一个类</span><wbr><br />
Document buildDocument(InputStream is) {<br />
String bodyText = null; <br />
try {<br />
WordDocument wordDoc = new WordDocument(is); <br />
StringWriter sw = new StringWriter(); <br />
wordDoc.writeAllText(sw); <br />
sw.close(); <br />
bodyText = sw.toString(); <br />
} catch (Exception e) {<br />
throw new DocumentHandlerException("Cannot extract text from a Word document", e); <br />
}<br />
if ((bodyText != null) &amp;&amp; (bodyText.trim().length() &gt; 0)) {<br />
Document doc = new Document(); <br />
doc.add(new Field("body", bodyText, Field.Store.YES, Field.Index.TOKENIZED)); <br />
return doc; <br />
}<br />
return null; <br />
}<br />
<br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">那么如何选择合适的 Strategy 来处理文件呢？UNIX 系统下的 file(1) 工具提供了从 magicnumber 获取文件类型的功能，我们可以使用 Runtime.exec() 方法调用这一命令。但这需要在有 file(1) 命令的情况下，而且并不能识别出所有文件类型。在一般的情况下我们可以简单地根据扩展名来使用合适的类处理文件。扩展名和类的映射关系写在 properties 文件中。当需要添加对新的文件类型的支持时，我们只需添加一个新的实现 DocumentBuilder 接口的类，并在映射文件中添加一个映射关系即可。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">数据库结果集资源</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">大多数应用使用数据库作为永久存储，对数据库查询结果集索引是一个常见需求。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">生成一个数据库结果集资源的实例需要先提供一个查询语句，然后执行查询，得到一个结果集。这个结果集中的内容便是我们需要进行索引的对象。extractDocuments 的实现便是为结果集中的每一行创建一个 Document 对象。和文件系统资源不同的是，数据库资源需要放入 Document 中的 Field 一般都存在在查询结果集之中。比如一个简单的文章发布站点，对其后台数据库执行查询 SELECT ID, TITLE, CONTENT FROM ARTICLE 返回一个有三列的结果集。对结果集的每一行都会被提取出一个 Document 对象，其中包含三个 Field，分别对应这三列。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">然而不同 Field 的类型是不同的。比如 ID 字段一般对应 Store.YES 和 Index.NO 的 Field；而 TITLE 字段则一般对应 Store.YES 和 Index.TOKENIZED 的 Field。为了解决这个问题，我们在数据库结果集资源的实现中提供一个类型为 Properties 的 fieldTypeMappings 属性，用于设置数据库字段所对应的 Field 的类型。对于前面的情况来说，这个属性可能会被配置成类似这样的形式：</span><wbr><br />
<br />
ID = YES, NO<br />
TITLE = YES, TOKENIZED<br />
CONTENT = NO, TOKENIZED <br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">配合这个映射，我们便可以生成合适类型的 Field，完成对结果集索引的工作。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">收集索引</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">完成对资源的索引之后，还需要让索引为搜索模块所用。前面我们已经说过这里介绍的框架主要用于小型应用，考虑到复杂性，我们采取简单地将分布在各个机器上的索引汇总到一个地方的策略。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">汇总索引的传输方式可以有很多方案，比如使用 FTP、HTTP、rsync 等。甚至索引模块和搜索模块可以位于同一台机器上，这种情况下只需要将索引进行本地拷贝即可。同前面类似，我们定义一个 Transporter 接口。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">清单 5. Transporter 接口</span><wbr><br />
public interface Transporter {<br />
public void transport(File file); <br />
} <br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">以 FTP 方式传输为例，我们使用 Commons Net 完成传输的操作。</span><wbr><br />
<br />
public void transport(File file) throws TransportException {<br />
FTPClient client = new FTPClient(); <br />
client.connect(host); <br />
client.login(username, password); <br />
client.changeWorkingDirectory(remotePath); <br />
transportRecursive(client, file); <br />
client.disconnect(); <br />
}<br />
<br />
public void transportRecursive(FTPClient client, File file) {<br />
if (file.isFile() &amp;&amp; file.canRead()) {<br />
client.storeFile(file.getName(), new FileInputStream(file)); <br />
} else if (file.isDirectory()) {<br />
client.makeDirectory(file.getName()); <br />
client.changeWorkingDirectory(file.getName()); <br />
File[] fileList = file.listFiles(); <br />
for (File f : fileList) {<br />
transportRecursive(client, f); <br />
}<br />
}<br />
}<br />
<br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">对其他传输方案也有各自的方案进行处理，具体使用哪个 Transporter 的实现被配置在 Spring 风格的索引模块配置文件中。传输的方式是灵活的。比如当需要强调安全性时，我们可以换用基于 SSL 的 FTP 进行传输。所需要做的只是开发一个使用 FTP over SSL 的 Transporter 实现，并在配置文件中更改 Transporter 的实现即可。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">进行搜索</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">在做了这么多之后，我们开始接触和用户关联最为紧密的搜索模块。注意，我们的框架不包括一个基于已经收集好的索引进行搜索是个很简单的过程。Lucene 已经提供了功能强大的 IndexSearcher 及其子类。在这个部分，我们不会再介绍如何使用这些类，而是关注在前文提到过的资源元数据上。元数据从各个资源所在的文件夹中读取得到，它在搜索模块中扮演重要的角色。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">构建一个查询</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">对不同资源进行搜索的查询方法并不一样。例如搜索一个论坛里的所有留言时，我们关注的一般是留言的标题、作者和内容；而当搜索一个 FTP 站点时，我们更多关注的是文件名和文件内容。另一方面，我们有时可能会使用一个查询去搜索多个资源的结果。这正是之前我们在前面所提到的元数据中 searchableFields 和 resourceName 属性的作用。前者指出一个资源中哪些字段是参与搜索的；后者则用于在搜索时确定使用哪个或者哪些索引。从技术细节来说，只有有了这些信息，我们才可以构造出可用的 Query 对象。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">呈现搜索结果</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">当从 IndexSearcher 对象得到搜索结果（Hits）之后，当然我们可以直接从中获取需要的值，再格式化予以输出。但一来格式化输出搜索结果（尤其在 Web 应用中）是个很常见的需求，可能会经常变更；二来结果的呈现格式应该是由分散的资源各自定义，而不是交由搜索模块来定义。基于上面两个原因，我们的框架将使用在资源收集端配置结果输出格式的方式。这个格式由资源元数据中的 hitTextPattern 属性定义。该属性是一个字符串类型的值，支持两种语法 </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">形如 ${field_name} 的子字符串都会被动态替换成查询结果中各个 Document 内 Field 的值。 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">形如 $function(...) 的被解释为函数，括号内以逗号隔开的符号都被解释成参数，函数可以嵌套。 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">例如搜索&#8220;具体&#8221;返回的搜索结果中包含一个 Document 对象，其 Field 如下表：</span><wbr><br />
<br />
Field <span style="line-height: 1.3em; font-family: '宋体'">名称 Field 内容 </span><wbr><br />
url http://example.org/article/1.html <br />
title <span style="line-height: 1.3em; font-family: '宋体'">示例标题 </span><wbr><br />
content <span style="line-height: 1.3em; font-family: '宋体'">这里是具体的内容。 </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">那么如果 hitTextPatten 被设置为&#8220;${title}</span><wbr><br />
$highlight(${content}, 5, "", "")&#8221;<span style="line-height: 1.3em; font-family: '宋体'">，返回的结果经浏览器解释后可能的显示结果如下（这只是个演示链接，请不要点击）：</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">示例标题 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">这里是具体...</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">上面提到的 $highlight() 函数用于在搜索结果中取得最匹配的一段文本，并高亮显示搜索时使用的短语，其第一个参数是高亮显示的文本，第二个参数是显示的文本长度，第三和第四个参数是高亮文本时使用的前缀和后缀。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">可以使用正则表达式和文本解析来实现前面所提到的语法。我们也可以使用 JavaCC 定义 hitTextPattern 的文法，进而生成词法分析器和语法解析器。这是更为系统并且相对而言不易出错的方法。对 JavaCC 的介绍不是本文的重点，您可以在下面的 阅读资源 中找到学习资料。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">下面列出的是一些与我们所提出的框架所相关或者类似的产品，您可以在 学习资料 中更多地了解他们。</span><wbr><br />
<br />
IBM?OmniFind?Family<br />
<br />
OmniFind <span style="line-height: 1.3em; font-family: '宋体'">是 IBM 公司推出的企业级搜索解决方案。基于 UIMA (Unstructured Information Management Architecture) 技术，它提供了强大的索引和获取信息功能，支持巨大数量、多种类型的文档资源（无论是结构化还是非结构化），并为 Lotus?Domino?和 WebSphere?Portal 专门进行了优化。</span><wbr><br />
<br />
Apache Solr<br />
<br />
Solr <span style="line-height: 1.3em; font-family: '宋体'">是 Apache 的一个企业级的全文检索项目，实现了一个基于 HTTP 的搜索服务器，支持多种资源和 Web 界面管理，它同样建立在 Lucene 之上，并对 Lucene 做了很多扩展，例如支持动态字段及唯一键，对查询结果进行动态分组和过滤等。</span><wbr><br />
<br />
Google SiteSearch<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">使用 Google 的站点搜索功能可以方便而快捷地建立一个站内搜索引擎。但是 Google 的站点搜索基于 Google 的网络爬虫，所以无法访问受保护的站点内容或者 Intranet 上的资源。另外，Google 所支持的资源类型也是有限的，我们无法对其进行扩展。</span><wbr><br />
<br />
SearchBlox? <br />
<br />
SearchBlox <span style="line-height: 1.3em; font-family: '宋体'">是一个商业的搜索引擎构建框架。它本身是一个 J2EE 组件，和我们的框架类似，也支持对网页和文件系统等资源进行索引，进而进行搜索。</span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">还需考虑的问题</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">本文介绍的思想试图利用开源的工具解决中小型应用中的常见问题。当然，作为一个框架，它还有很多不足，下面列举出一些可以进行改进的地方。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">性能考虑</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">当需要进行索引的资源数目不多时，隔一定的时间进行一次完全索引不会占用很长时间。使用一台 2G 内存，Xeon 2.66G 处理器的服务器进行实际测试，发现对数据库资源的索引占用的时间很少，一千多条记录花费的时间在 1 秒到 2 秒之内。而对 1400 多个文件进行索引耗时大约十几秒。但在大型应用中，资源的容量是巨大的，如果每次都进行完整的索引，耗费的时间会很惊人。我们可以通过跳过已经索引的资源内容，删除已不存在的资源内容的索引，并进行增量索引来解决这个问题。这可能会涉及文件校验和索引删除等。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">另一方面，框架可以提供查询缓存来提高查询效率。框架可以在内存中建立一级缓存，并使用如 OSCache 或 EHCache 实现磁盘上的二级缓存。当索引的内容变化不频繁时，使用查询缓存更会明显地提高查询速度、降低资源消耗。</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">分布式索引</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">我们的框架可以将索引分布在多台机器上。搜索资源时，查询被 flood 到各个机器上从而获得搜索结果。这样可以免去传输索引到某一台中央服务器的过程。当然也可以在非结构化的 P2P 网络上实现分布式哈希表 (DHT)，配合索引复制 (Replication)，使得应用程序更为安全，可靠，有伸缩性。在阅读资料中给出了 一篇关于构建分布式环境下全文搜索的可行性的论文。 </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">安全性</span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">目前我们的框架并没有涉及到安全性。除了依赖资源本身的访问控制（如受保护的网页和文件系统等）之外，我们还可以从两方面增强框架本身的安全性： </span><wbr><br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">考虑到一个组织的搜索功能对不同用户的权限设置不一定一样，可以支持对用户角色的定义，实行对搜索模块的访问控制。 </span><wbr><br />
<span style="line-height: 1.3em; font-family: '宋体'">在资源索引模块中实现一种机制，让资源可以限制自己暴露的内容，从而缩小索引模块的索引范围。这可以类比 robots 文件可以规定搜索引擎爬虫的行为。 </span><wbr><br />
<br />
<br />
<span style="line-height: 1.3em; font-family: '宋体'">通过上文的介绍，我们认识了一个可扩展的框架，由索引模块和搜索模块两部分组成。它可以灵活地适应不同的应用场景。如果需要更独特的需求，框架本身预留了可以扩展的接口，我们可以通过实现这些接口完成功能的定制。更重要的是这一切都是建立在开源软件的基础之上。希望本文能为您揭示开源的力量，体验用开源工具组装您自己的解决方案所带来的莫大快乐。</span><wbr>
<img src ="http://www.blogjava.net/caizh2009/aggbug/293035.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-08-29 00:25 <a href="http://www.blogjava.net/caizh2009/archive/2009/08/29/293035.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>