﻿<?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-kingpub-文章分类-weblucene</title><link>http://www.blogjava.net/kingpub/category/14662.html</link><description>海内存知己，博客若比邻</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 06:49:08 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 06:49:08 GMT</pubDate><ttl>60</ttl><item><title>Lucene 的学习</title><link>http://www.blogjava.net/kingpub/articles/70321.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Mon, 18 Sep 2006 07:48:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/70321.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Lucene														的学习																																																														       								通过这几天的看书和学习，对				Lucene				有了更进一步的认识，所以总结一下这些天的学习成果把				Luce...&nbsp;&nbsp;<a href='http://www.blogjava.net/kingpub/articles/70321.html'>阅读全文</a><img src ="http://www.blogjava.net/kingpub/aggbug/70321.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-09-18 15:48 <a href="http://www.blogjava.net/kingpub/articles/70321.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 Lucene 加速 Web 搜索应用程序的开发</title><link>http://www.blogjava.net/kingpub/articles/69413.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Wed, 13 Sep 2006 09:08:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/69413.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/69413.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/69413.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/69413.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/69413.html</trackback:ping><description><![CDATA[
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#author" target="_blank">
						<font color="#996699">周 登朋</font>
				</a> (<a href="mailto:zhoudengpeng@yahoo.com.cn?subject=用 Lucene 加速 Web 搜索应用程序的开发&amp;cc=htc@us.ibm.com"><font color="#5c81a7">zhoudengpeng@yahoo.com.cn</font></a>), 软件工程师, 上海交通大学<br /></p>
		<p>2006 年 9 月 06 日 </p>
		<p>在本篇文章中，你会学习到如何利用 Lucene 实现高级搜索功能以及如何利用 Lucene 来创建 Web 搜索应用程序。通过这些学习，你就可以利用 Lucene 来创建自己的搜索应用程序。</p>
		<p>
				<a name="N1005E">
						<span class="atitle">
								<strong>
										<font size="4">架构概览</font>
								</strong>
						</span>
				</a>
		</p>
		<p>通常一个 Web 搜索引擎的架构分为前端和后端两部分，就像<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure1"><font color="#996699">图一</font></a>中所示。在前端流程中，用户在搜索引擎提供的界面中输入要搜索的关键词，这里提到的用户界面一般是一个带有输入框的 Web 页面，然后应用程序将搜索的关键词解析成搜索引擎可以理解的形式，并在索引文件上进行搜索操作。在排序后，搜索引擎返回搜索结果给用户。在后端流程中，网络爬虫或者机器人从因特网上获取 Web 页面，然后索引子系统解析这些 Web 页面并存入索引文件中。如果你想利用 Lucene 来创建一个 Web 搜索应用程序，那么它的架构也和上面所描述的类似，就如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure1"><font color="#996699">图一</font></a>中所示。</p>
		<p>
				<br />
				<a name="figure1">
						<strong>Figure 1. Web 搜索引擎架构</strong>
				</a>
				<br />
				<img height="380" alt="Web搜索引擎架构" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure1.gif" width="449" />
				<br />
		</p>
		<p>
				<a name="N1007F">
						<span class="atitle">
								<strong>
										<font size="4">利用 Lucene 实现高级搜索</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Lucene 支持多种形式的高级搜索，我们在这一部分中会进行探讨，然后我会使用 Lucene 的 API 来演示如何实现这些高级搜索功能。</p>
		<p>
				<a name="N10088">
						<span class="smalltitle">
								<strong>
										<font size="3">布尔操作符</font>
								</strong>
						</span>
				</a>
		</p>
		<p>大多数的搜索引擎都会提供布尔操作符让用户可以组合查询，典型的布尔操作符有 AND, OR, NOT。Lucene 支持 5 种布尔操作符，分别是 AND, OR, NOT, 加(+), 减(-)。接下来我会讲述每个操作符的用法。 </p>
		<ul>
				<li>
						<strong>OR</strong>: 如果你要搜索含有字符 A 或者 B 的文档，那么就需要使用 OR 操作符。需要记住的是，如果你只是简单的用空格将两个关键词分割开，其实在搜索的时候搜索引擎会自动在两个关键词之间加上 OR 操作符。例如，“Java OR Lucene” 和 “Java Lucene” 都是搜索含有 Java 或者含有 Lucene 的文档。 
</li>
				<li>
						<strong>AND</strong>: 如果你需要搜索包含一个以上关键词的文档，那么就需要使用 AND 操作符。例如，“Java AND Lucene” 返回所有既包含 Java 又包含 Lucene 的文档。 
</li>
				<li>
						<strong>NOT</strong>: Not 操作符使得包含紧跟在 NOT 后面的关键词的文档不会被返回。例如，如果你想搜索所有含有 Java 但不含有 Lucene 的文档，你可以使用查询语句 “Java NOT Lucene”。但是你不能只对一个搜索词使用这个操作符，比如，查询语句 “NOT Java” 不会返回任何结果。 
</li>
				<li>
						<strong>加号（+）</strong>: 这个操作符的作用和 AND 差不多，但它只对紧跟着它的一个搜索词起作用。例如，如果你想搜索一定包含 Java，但不一定包含 Lucene 的文档，就可以使用查询语句“+Java Lucene”。 
</li>
				<li>
						<strong>减号（-）</strong>: 这个操作符的功能和 NOT 一样，查询语句 “Java -Lucene” 返回所有包含 Java 但不包含 Lucene 的文档。 </li>
		</ul>
		<p>接下来我们看一下如何利用 Lucene 提供的 API 来实现布尔查询。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing1"><font color="#996699">清单1</font></a> 显示了如果利用布尔操作符进行查询的过程。</p>
		<p>
				<br />
				<a name="Listing1">
						<strong>清单1：使用布尔操作符</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">  //Test boolean operatorpublic void testOperator(String indexDirectory) throws Exception{   Directory dir = FSDirectory.getDirectory(indexDirectory,false);   IndexSearcher indexSearcher = new IndexSearcher(dir);   String[] searchWords = {"Java AND Lucene", "Java NOT Lucene", "Java OR Lucene",                     "+Java +Lucene", "+Java -Lucene"};   Analyzer language = new StandardAnalyzer();   Query query;   for(int i = 0; i &lt; searchWords.length; i++){      query = QueryParser.parse(searchWords[i], "title", language);      Hits results = indexSearcher.search(query);      System.out.println(results.length() + "search results for query " + searchWords[i]);   }}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N100BE">
						<span class="smalltitle">
								<strong>
										<font size="3">域搜索(Field Search)</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Lucene 支持域搜索，你可以指定一次查询是在哪些域(Field)上进行。例如，如果索引的文档包含两个域，<code><font face="Courier" size="2">Title</font></code> 和 <code><font face="Courier" size="2">Content</font></code>，你就可以使用查询 “Title: Lucene AND Content: Java” 来返回所有在 Title 域上包含 Lucene 并且在 Content 域上包含 Java 的文档。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing2"><font color="#996699">清单 2</font></a> 显示了如何利用 Lucene 的 API 来实现域搜索。 </p>
		<p>
				<br />
				<a name="Listing2">
						<strong>清单2：实现域搜索</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">//Test field searchpublic void testFieldSearch(String indexDirectory) throws Exception{    Directory dir = FSDirectory.getDirectory(indexDirectory,false);    IndexSearcher indexSearcher = new IndexSearcher(dir);    String searchWords = "title:Lucene AND content:Java";    Analyzer language = new StandardAnalyzer();    Query query = QueryParser.parse(searchWords, "title", language);    Hits results = indexSearcher.search(query);    System.out.println(results.length() + "search results for query " + searchWords);}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N100DD">
						<span class="smalltitle">
								<strong>
										<font size="3">通配符搜索(Wildcard Search)</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Lucene 支持两种通配符：问号（？）和星号（*）。你可以使用问号（？）来进行单字符的通配符查询，或者利用星号（*）进行多字符的通配符查询。例如，如果你想搜索 tiny 或者 tony，你就可以使用查询语句 “t?ny”；如果你想查询 Teach, Teacher 和 Teaching，你就可以使用查询语句 “Teach*”。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing3"><font color="#996699">清单3</font></a> 显示了通配符查询的过程。 </p>
		<p>
				<br />
				<a name="Listing3">
						<strong>清单3：进行通配符查询</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">//Test wildcard searchpublic void testWildcardSearch(String indexDirectory)throws Exception{   Directory dir = FSDirectory.getDirectory(indexDirectory,false);   IndexSearcher indexSearcher = new IndexSearcher(dir);   String[] searchWords = {"tex*", "tex?", "?ex*"};   Query query;   for(int i = 0; i &lt; searchWords.length; i++){      query = new WildcardQuery(new Term("title",searchWords[i]));      Hits results = indexSearcher.search(query);      System.out.println(results.length() + "search results for query " + searchWords[i]);   }}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N100F4">
						<span class="smalltitle">
								<strong>
										<font size="3">模糊查询</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Lucene 提供的模糊查询基于编辑距离算法(Edit distance algorithm)。你可以在搜索词的尾部加上字符 ~ 来进行模糊查询。例如，查询语句 “think~” 返回所有包含和 think 类似的关键词的文档。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing4"><font color="#996699">清单 4</font></a> 显示了如果利用 Lucene 的 API 进行模糊查询的代码。 </p>
		<p>
				<br />
				<a name="Listing4">
						<strong>清单4：实现模糊查询</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">//Test fuzzy searchpublic void testFuzzySearch(String indexDirectory)throws Exception{   Directory dir = FSDirectory.getDirectory(indexDirectory,false);   IndexSearcher indexSearcher = new IndexSearcher(dir);   String[] searchWords = {"text", "funny"};   Query query;   for(int i = 0; i &lt; searchWords.length; i++){      query = new FuzzyQuery(new Term("title",searchWords[i]));      Hits results = indexSearcher.search(query);      System.out.println(results.length() + "search results for query " + searchWords[i]);   }}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N1010B">
						<span class="smalltitle">
								<strong>
										<font size="3">范围搜索(Range Search)</font>
								</strong>
						</span>
				</a>
		</p>
		<p>范围搜索匹配某个域上的值在一定范围的文档。例如，查询 “age:[18 TO 35]” 返回所有 age 域上的值在 18 到 35 之间的文档。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing5"><font color="#996699">清单5</font></a>显示了利用 Lucene 的 API 进行返回搜索的过程。 </p>
		<p>
				<br />
				<a name="Listing5">
						<strong>清单5：测试范围搜索</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">//Test range searchpublic void testRangeSearch(String indexDirectory)throws Exception{    Directory dir = FSDirectory.getDirectory(indexDirectory,false);    IndexSearcher indexSearcher = new IndexSearcher(dir);    Term begin = new Term("birthDay","20000101");    Term end   = new Term("birthDay","20060606");    Query query = new RangeQuery(begin,end,true);    Hits results = indexSearcher.search(query);    System.out.println(results.length() + "search results is returned");}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N10122">
						<span class="atitle">
								<strong>
										<font size="4">在 Web 应用程序中集成 Lucene</font>
								</strong>
						</span>
				</a>
		</p>
		<p>接下来我们开发一个 Web 应用程序利用 Lucene 来检索存放在文件服务器上的 HTML 文档。在开始之前，需要准备如下环境：</p>
		<ol>
				<li>Eclipse 集成开发环境 
</li>
				<li>Tomcat 5.0 
</li>
				<li>Lucene Library 
</li>
				<li>JDK 1.5 </li>
		</ol>
		<p>这个例子使用 Eclipse 进行 Web 应用程序的开发，最终这个 Web 应用程序跑在 Tomcat 5.0 上面。在准备好开发所必需的环境之后，我们接下来进行 Web 应用程序的开发。 </p>
		<p>
				<a name="N1013D">
						<span class="smalltitle">
								<strong>
										<font size="3">1、创建一个动态 Web 项目</font>
								</strong>
						</span>
				</a>
		</p>
		<ol>
				<li>在 Eclipse 里面，选择 <strong>File &gt; New &gt; Project</strong>，然后再弹出的窗口中选择<strong>动态 Web 项目</strong>，如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure2"><font color="#996699">图二</font></a>所示。 </li>
		</ol>
		<p>
				<br />
				<a name="figure2">
						<strong>图二：创建动态Web项目</strong>
				</a>
				<br />
				<img height="473" alt="创建动态Web项目" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure2.jpg" width="496" />
				<br />
		</p>
		<ol start="2">
				<li>在创建好动态 Web 项目之后，你会看到创建好的项目的结构，如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure3"><font color="#996699">图三</font></a>所示，项目的名称为 sample.dw.paper.lucene。 </li>
		</ol>
		<p>
				<br />
				<a name="figure3">
						<strong>图三：动态 Web 项目的结构</strong>
				</a>
				<br />
				<img height="338" alt="动态 Web 项目的结构" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure3.jpg" width="329" />
				<br />
		</p>
		<p>
				<a name="N1017C">
						<span class="smalltitle">
								<strong>
										<font size="3">2. 设计 Web 项目的架构</font>
								</strong>
						</span>
				</a>
		</p>
		<p>在我们的设计中，把该系统分成如下四个子系统：</p>
		<ol>
				<li>
						<strong>用户接口</strong>: 这个子系统提供用户界面使用户可以向 Web 应用程序服务器提交搜索请求，然后搜索结果通过用户接口来显示出来。我们用一个名为 search.jsp 的页面来实现该子系统。 
</li>
				<li>
						<strong>请求管理器</strong>: 这个子系统管理从客户端发送过来的搜索请求并把搜索请求分发到搜索子系统中。最后搜索结果从搜索子系统返回并最终发送到用户接口子系统。我们使用一个 Servlet 来实现这个子系统。 
</li>
				<li>
						<strong>搜索子系统</strong>: 这个子系统负责在索引文件上进行搜索并把搜索结构传递给请求管理器。我们使用 Lucene 提供的 API 来实现该子系统。 
</li>
				<li>
						<strong>索引子系统</strong>: 这个子系统用来为 HTML 页面来创建索引。我们使用 Lucene 的 API 以及 Lucene 提供的一个 HTML 解析器来创建该子系统。 </li>
		</ol>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure4">
						<font color="#996699">图4</font>
				</a> 显示了我们设计的详细信息，我们将用户接口子系统放到 webContent 目录下面。你会看到一个名为 search.jsp 的页面在这个文件夹里面。请求管理子系统在包 <code><font face="Courier" size="2">sample.dw.paper.lucene.servlet</font></code> 下面，类 <code><font face="Courier" size="2">SearchController</font></code> 负责功能的实现。搜索子系统放在包 <code><font face="Courier" size="2">sample.dw.paper.lucene.search</font></code> 当中，它包含了两个类，<code><font face="Courier" size="2">SearchManager</font></code> 和 <code><font face="Courier" size="2">SearchResultBean</font></code>，第一个类用来实现搜索功能，第二个类用来描述搜索结果的结构。索引子系统放在包 <code><font face="Courier" size="2">sample.dw.paper.lucene.index</font></code> 当中。类 <code><font face="Courier" size="2">IndexManager</font></code> 负责为 HTML 文件创建索引。该子系统利用包 <code><font face="Courier" size="2">sample.dw.paper.lucene.util</font></code> 里面的类 <code><font face="Courier" size="2">HTMLDocParser</font></code> 提供的方法 <code><font face="Courier" size="2">getTitle</font></code> 和 <code><font face="Courier" size="2">getContent</font></code> 来对 HTML 页面进行解析。 </p>
		<p>
				<br />
				<a name="figure4">
						<strong>图四：项目的架构设计</strong>
				</a>
				<br />
				<img height="324" alt="项目的架构设计" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure4.jpg" width="384" />
				<br />
		</p>
		<p>
				<a name="N101DE">
						<span class="smalltitle">
								<strong>
										<font size="3">3. 子系统的实现</font>
								</strong>
						</span>
				</a>
		</p>
		<p>在分析了系统的架构设计之后，我们接下来看系统实现的详细信息。 </p>
		<ol>
				<li>
						<strong>用户接口</strong>: 这个子系统有一个名为 search.jsp 的 JSP 文件来实现，这个 JSP 页面包含两个部分。第一部分提供了一个用户接口去向 Web 应用程序服务器提交搜索请求，如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure5"><font color="#996699">图5</font></a>所示。注意到这里的搜索请求发送到了一个名为 SearchController 的 Servlet 上面。Servlet 的名字和具体实现的类的对应关系在 web.xml 里面指定。 </li>
		</ol>
		<p>
				<br />
				<a name="figure5">
						<strong>图5：向Web服务器提交搜索请求</strong>
				</a>
				<br />
				<img height="207" alt="向Web服务器提交搜索请求" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure5.jpg" width="532" />
				<br />
		</p>
		<p>这个JSP的第二部分负责显示搜索结果给用户，如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure6"><font color="#996699">图6</font></a>所示： </p>
		<p>
				<br />
				<a name="figure6">
						<strong>图6：显示搜索结果</strong>
				</a>
				<br />
				<img height="360" alt="显示搜索结果" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure6.jpg" width="572" />
				<br />
		</p>
		<ol start="2">
				<li>
						<strong>请求管理器</strong>: 一个名为 <code><font face="Courier" size="2">SearchController</font></code> 的 servlet 用来实现该子系统。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing6"><font color="#996699">清单６</font></a>给出了这个类的源代码。 </li>
		</ol>
		<p>
				<br />
				<a name="Listing6">
						<strong>清单６：请求管理器的实现</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">package sample.dw.paper.lucene.servlet;import java.io.IOException;import java.util.List;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import sample.dw.paper.lucene.search.SearchManager;/** * This servlet is used to deal with the search request * and return the search results to the client */public class SearchController extends HttpServlet{    private static final long serialVersionUID = 1L;    public void doPost(HttpServletRequest request, HttpServletResponse response)                      throws IOException, ServletException{        String searchWord = request.getParameter("searchWord");        SearchManager searchManager = new SearchManager(searchWord);        List searchResult = null;        searchResult = searchManager.search();        RequestDispatcher dispatcher = request.getRequestDispatcher("search.jsp");        request.setAttribute("searchResult",searchResult);        dispatcher.forward(request, response);    }    public void doGet(HttpServletRequest request, HttpServletResponse response)                     throws IOException, ServletException{        doPost(request, response);    }}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing6"><font color="#996699">清单6</font></a>中，<code><font face="Courier" size="2">doPost</font></code> 方法从客户端获取搜索词并创建类 <code><font face="Courier" size="2">SearchManager</font></code> 的一个实例，其中类 <code><font face="Courier" size="2">SearchManager</font></code> 在搜索子系统中进行了定义。然后，<code><font face="Courier" size="2">SearchManager</font></code> 的方法 search 会被调用。最后搜索结果被返回到客户端。 </p>
		<ol start="3">
				<li>
						<strong>搜索子系统</strong>: 在这个子系统中，我们定义了两个类：<code><font face="Courier" size="2">SearchManager</font></code> 和 <code><font face="Courier" size="2">SearchResultBean</font></code>。第一个类用来实现搜索功能，第二个类是个JavaBean，用来描述搜索结果的结构。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing7"><font color="#996699">清单7</font></a>给出了类 <code><font face="Courier" size="2">SearchManager</font></code> 的源代码。 </li>
		</ol>
		<p>
				<br />
				<a name="Listing7">
						<strong>清单7：搜索功能的实现</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">package sample.dw.paper.lucene.search;import java.io.IOException;import java.util.ArrayList;import java.util.List;import org.apache.lucene.analysis.Analyzer;import org.apache.lucene.analysis.standard.StandardAnalyzer;import org.apache.lucene.queryParser.ParseException;import org.apache.lucene.queryParser.QueryParser;import org.apache.lucene.search.Hits;import org.apache.lucene.search.IndexSearcher;import org.apache.lucene.search.Query;import sample.dw.paper.lucene.index.IndexManager;/** * This class is used to search the  * Lucene index and return search results */public class SearchManager {	    private String searchWord;        private IndexManager indexManager;        private Analyzer analyzer;        public SearchManager(String searchWord){        this.searchWord   =  searchWord;        this.indexManager =  new IndexManager();        this.analyzer     =  new StandardAnalyzer();    }        /**     * do search     */    public List search(){        List searchResult = new ArrayList();        if(false == indexManager.ifIndexExist()){        try {            if(false == indexManager.createIndex()){                return searchResult;            }        } catch (IOException e) {          e.printStackTrace();          return searchResult;        }        }    	        IndexSearcher indexSearcher = null;        try{            indexSearcher = new IndexSearcher(indexManager.getIndexDir());        }catch(IOException ioe){            ioe.printStackTrace();        }        QueryParser queryParser = new QueryParser("content",analyzer);        Query query = null;        try {            query = queryParser.parse(searchWord);        } catch (ParseException e) {          e.printStackTrace();        }        if(null != query &gt;&gt; null != indexSearcher){			            try {                Hits hits = indexSearcher.search(query);                for(int i = 0; i &lt; hits.length(); i ++){                    SearchResultBean resultBean = new SearchResultBean();                    resultBean.setHtmlPath(hits.doc(i).get("path"));                    resultBean.setHtmlTitle(hits.doc(i).get("title"));                    searchResult.add(resultBean);                }            } catch (IOException e) {                e.printStackTrace();            }        }        return searchResult;    }} </pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing7"><font color="#996699">清单7</font></a>中，注意到在这个类里面有三个私有属性。第一个是 <code><font face="Courier" size="2">searchWord</font></code>，代表了来自客户端的搜索词。第二个是 <code><font face="Courier" size="2">indexManager</font></code>，代表了在索引子系统中定义的类 <code><font face="Courier" size="2">IndexManager</font></code> 的一个实例。第三个是 <code><font face="Courier" size="2">analyzer</font></code>，代表了用来解析搜索词的解析器。现在我们把注意力放在方法 <code><font face="Courier" size="2">search</font></code> 上面。这个方法首先检查索引文件是否已经存在，如果已经存在，那么就在已经存在的索引上进行检索，如果不存在，那么首先调用类 <code><font face="Courier" size="2">IndexManager</font></code> 提供的方法来创建索引，然后在新创建的索引上进行检索。搜索结果返回后，这个方法从搜索结果中提取出需要的属性并为每个搜索结果生成类 <code><font face="Courier" size="2">SearchResultBean</font></code> 的一个实例。最后这些 <code><font face="Courier" size="2">SearchResultBean</font></code> 的实例被放到一个列表里面并返回给请求管理器。</p>
		<p>在类 <code><font face="Courier" size="2">SearchResultBean</font></code> 中，含有两个属性，分别是 <code><font face="Courier" size="2">htmlPath</font></code> 和 <code><font face="Courier" size="2">htmlTitle</font></code>，以及这个两个属性的 get 和 set 方法。这也意味着我们的搜索结果包含两个属性：<code><font face="Courier" size="2">htmlPath</font></code> 和 <code><font face="Courier" size="2">htmlTitle</font></code>，其中 <code><font face="Courier" size="2">htmlPath</font></code> 代表了 HTML 文件的路径，<code><font face="Courier" size="2">htmlTitle</font></code> 代表了 HTML 文件的标题。 </p>
		<ol start="4">
				<li>
						<strong>索引子系统</strong>: 类 <code><font face="Courier" size="2">IndexManager</font></code> 用来实现这个子系统。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing8"><font color="#996699">清单8</font></a> 给出了这个类的源代码。 </li>
		</ol>
		<p>
				<br />
				<a name="Listing8">
						<strong>清单8：索引子系统的实现</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">package sample.dw.paper.lucene.index;import java.io.File;import java.io.IOException;import java.io.Reader;import org.apache.lucene.analysis.Analyzer;import org.apache.lucene.analysis.standard.StandardAnalyzer;import org.apache.lucene.document.Document;import org.apache.lucene.document.Field;import org.apache.lucene.index.IndexWriter;import org.apache.lucene.store.Directory;import org.apache.lucene.store.FSDirectory;import sample.dw.paper.lucene.util.HTMLDocParser;/** * This class is used to create an index for HTML files * */public class IndexManager {    //the directory that stores HTML files     private final String dataDir  = "c:\\dataDir";    //the directory that is used to store a Lucene index    private final String indexDir = "c:\\indexDir";    /**     * create index     */    public boolean createIndex() throws IOException{        if(true == ifIndexExist()){            return true;	        }        File dir = new File(dataDir);        if(!dir.exists()){            return false;        }        File[] htmls = dir.listFiles();        Directory fsDirectory = FSDirectory.getDirectory(indexDir, true);        Analyzer  analyzer    = new StandardAnalyzer();        IndexWriter indexWriter = new IndexWriter(fsDirectory, analyzer, true);        for(int i = 0; i &lt; htmls.length; i++){            String htmlPath = htmls[i].getAbsolutePath();            if(htmlPath.endsWith(".html") || htmlPath.endsWith(".htm")){        		addDocument(htmlPath, indexWriter);        	}        }        indexWriter.optimize();        indexWriter.close();        return true;    }    /**     * Add one document to the Lucene index     */    public void addDocument(String htmlPath, IndexWriter indexWriter){        HTMLDocParser htmlParser = new HTMLDocParser(htmlPath);        String path    = htmlParser.getPath();        String title   = htmlParser.getTitle();        Reader content = htmlParser.getContent();        Document document = new Document();        document.add(new Field("path",path,Field.Store.YES,Field.Index.NO));        document.add(new Field("title",title,Field.Store.YES,Field.Index.TOKENIZED));        document.add(new Field("content",content));        try {              indexWriter.addDocument(document);    } catch (IOException e) {              e.printStackTrace();          }    }    /**     * judge if the index exists already     */    public boolean ifIndexExist(){        File directory = new File(indexDir);        if(0 &lt; directory.listFiles().length){            return true;        }else{            return false;        }    }    public String getDataDir(){        return this.dataDir;    }    public String getIndexDir(){        return this.indexDir;    }}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这个类包含两个私有属性，分别是 <code><font face="Courier" size="2">dataDir</font></code> 和 <code><font face="Courier" size="2">indexDir</font></code>。<code><font face="Courier" size="2">dataDir</font></code> 代表存放等待进行索引的 HTML 页面的路径，<code><font face="Courier" size="2">indexDir</font></code> 代表了存放 Lucene 索引文件的路径。类 <code><font face="Courier" size="2">IndexManager</font></code> 提供了三个方法，分别是 <code><font face="Courier" size="2">createIndex</font></code>, <code><font face="Courier" size="2">addDocument</font></code> 和 <code><font face="Courier" size="2">ifIndexExist</font></code>。如果索引不存在的话，你可以使用方法 <code><font face="Courier" size="2">createIndex</font></code> 去创建一个新的索引，用方法 <code><font face="Courier" size="2">addDocument</font></code> 去向一个索引上添加文档。在我们的场景中，一个文档就是一个 HTML 页面。方法 <code><font face="Courier" size="2">addDocument</font></code> 会调用由类 <code><font face="Courier" size="2">HTMLDocParser</font></code> 提供的方法对 HTML 文档进行解析。你可以使用最后一个方法 <code><font face="Courier" size="2">ifIndexExist</font></code> 来判断 Lucene 的索引是否已经存在。 </p>
		<p>现在我们来看一下放在包 <code><font face="Courier" size="2">sample.dw.paper.lucene.util</font></code> 里面的类 <code><font face="Courier" size="2">HTMLDocParser</font></code>。这个类用来从 HTML 文件中提取出文本信息。这个类包含三个方法，分别是 <code><font face="Courier" size="2">getContent</font></code>，<code><font face="Courier" size="2">getTitle</font></code> 和 <code><font face="Courier" size="2">getPath</font></code>。第一个方法返回去除了 HTML 标记的文本内容，第二个方法返回 HTML 文件的标题，最后一个方法返回 HTML 文件的路径。<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#Listing9"><font color="#996699">清单9</font></a> 给出了这个类的源代码。 </p>
		<p>
				<br />
				<a name="Listing9">
						<strong>清单9：HTML 解析器</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">package sample.dw.paper.lucene.util;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.Reader;import java.io.UnsupportedEncodingException;import org.apache.lucene.demo.html.HTMLParser;public class HTMLDocParser {    private String htmlPath;    private HTMLParser htmlParser;    public HTMLDocParser(String htmlPath){        this.htmlPath = htmlPath;        initHtmlParser();    }    private void initHtmlParser(){        InputStream inputStream = null;        try {            inputStream = new FileInputStream(htmlPath);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        if(null != inputStream){	        try {                htmlParser = new HTMLParser(new InputStreamReader(inputStream, "utf-8"));            } catch (UnsupportedEncodingException e) {                e.printStackTrace();            }        }    }    public String getTitle(){        if(null != htmlParser){            try {                return htmlParser.getTitle();            } catch (IOException e) {                e.printStackTrace();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    return "";    }    public Reader getContent(){    if(null != htmlParser){            try {                  return htmlParser.getReader();              } catch (IOException e) {                  e.printStackTrace();              }        }        return null;    }    public String getPath(){        return this.htmlPath;		    }}</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N1032A">
						<span class="smalltitle">
								<strong>
										<font size="3">5．在 Tomcat 5.0 上运行应用程序</font>
								</strong>
						</span>
				</a>
		</p>
		<p>现在我们可以在 Tomcat 5.0 上运行开发好的应用程序。 </p>
		<ol>
				<li>右键单击 <strong>search.jsp</strong>，然后选择 <strong>Run as &gt; Run on Server</strong>，如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure7"><font color="#996699">图7</font></a>所示。 </li>
		</ol>
		<p>
				<br />
				<a name="figure7">
						<strong>图7：配置 Tomcat 5.0</strong>
				</a>
				<br />
				<img height="434" alt="配置 Tomcat 5.0" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure7.jpg" width="478" />
				<br />
		</p>
		<ol start="2">
				<li>在弹出的窗口中，选择 <strong>Tomcat v5.0 Server</strong> 作为目标 Web 应用程序服务器，然后点击 <strong>Next</strong>，如<a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure8"><font color="#996699">图8</font></a> 所示： </li>
		</ol>
		<p>
				<br />
				<a name="figure8">
						<strong>图8：选择 Tomcat 5.0</strong>
				</a>
				<br />
				<img height="433" alt="选择 Tomcat 5.0" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure8.jpg" width="467" />
				<br />
		</p>
		<ol start="3">
				<li>现在需要指定用来运行 Web 应用程序的 Apache Tomcat 5.0 以及 JRE 的路径。这里你所选择的 JRE 的版本必须和你用来编译 Java 文件的 JRE 的版本一致。配置好之后，点击 <strong>Finish</strong>。如 <a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure9"><font color="#996699">图9</font></a> 所示。 </li>
		</ol>
		<p>
				<br />
				<a name="figure9">
						<strong>图9：完成Tomcat 5.0的配置</strong>
				</a>
				<br />
				<img height="436" alt="完成Tomcat 5.0的配置" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure9.jpg" width="468" />
				<br />
		</p>
		<ol start="4">
				<li>配置好之后，Tomcat 会自动运行，并且会对 search.jsp 进行编译并显示给用户。如 <a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure10"><font color="#996699">图10</font></a> 所示。 </li>
		</ol>
		<p>
				<br />
				<a name="figure10">
						<strong>图10：用户界面</strong>
				</a>
				<br />
				<img height="403" alt="用户界面" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure10.jpg" width="521" />
				<br />
		</p>
		<ol start="5">
				<li>在输入框中输入关键词 “information” 然后单击 <strong>Search</strong> 按钮。然后这个页面上会显示出搜索结果来，如 <a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure11"><font color="#996699">图11</font></a> 所示。 </li>
		</ol>
		<p>
				<br />
				<a name="figure11">
						<strong>图11：搜索结果</strong>
				</a>
				<br />
				<img height="416" alt="搜索结果" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure11.jpg" width="526" />
				<br />
		</p>
		<ol start="6">
				<li>单击搜索结果的第一个链接，页面上就会显示出所链接到的页面的内容。如 <a href="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/index.html#figure12"><font color="#996699">图12</font></a> 所示. </li>
		</ol>
		<p>
				<br />
				<a name="figure12">
						<strong>图12：详细信息</strong>
				</a>
				<br />
				<img height="419" alt="详细信息" src="http://www-128.ibm.com/developerworks/cn/web/wa-lucene2/figure12.jpg" width="525" />
				<br />
		</p>
		<p>现在我们已经成功的完成了示例项目的开发，并成功的用Lucene实现了搜索和索引功能。你可以下载这个项目的源代码。 </p>
		<p>
				<a name="N103E7">
						<span class="atitle">
								<strong>
										<font size="4">总结</font>
								</strong>
						</span>
				</a>
		</p>
		<p>Lucene 提供了灵活的接口使我们更加方便的设计我们的 Web 搜索应用程序。如果你想在你的应用程序中加入搜索功能，那么 Lucene 是一个很好的选择。在设计你的下一个带有搜索功能的应用程序的时候可以考虑使用 Lucene 来提供搜索功能。</p>
		<p>
				<span class="atitle">
						<a name="download">
								<strong>
										<font size="4">下载</font>
								</strong>
						</a>
				</span>
		</p>
		<p>
		</p>
		<table class="data-table-1" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<th>描述</th>
								<th>名字</th>
								<th style="TEXT-ALIGN: right">大小</th>
								<th>下载方法</th>
						</tr>
						<tr>
								<td class="tb-row">Lucene Web 应用程序示例</td>
								<td nowrap="">wa-lucene2_source_code.zip</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/kingpub/aggbug/69413.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-09-13 17:08 <a href="http://www.blogjava.net/kingpub/articles/69413.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebLucene 之安装习行录</title><link>http://www.blogjava.net/kingpub/articles/66655.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Wed, 30 Aug 2006 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/66655.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/66655.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/66655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/66655.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/66655.html</trackback:ping><description><![CDATA[
		<font size="2">　清人颜元曾说过：“学而必习，习又必行，固也”。若干年来，本人一直对这句话奉若神明，深感只有致用才能巩固学的效果，发现学的不足，享受学的乐趣。</font>
		<div>
				<font size="2">
						<span>　　</span>搜索引擎在近几年的发展虽没有大的突破，却逐步走向成熟，走向商业。同时，随着开源的搜索引擎逐渐增多，门槛也逐步降低，使得普通程序员也能有机会接触到这一高深的领域。由于对搜索引擎的兴趣所致，笔者也利用部分业余时间对此进行一些研究与尝试。本文及后续的文章主要记载笔者在实践中的一些操作过程及应用体会，同时参考了一些前人的文章，目的主要是为了备忘，也希望能对后来者有所参考与助益。</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>目前较为有名的开源搜索引擎Nutch、Compass、Solr等都是基于Lucene之上，而在众多基于Lucene的检索系统中，车东先生的WebLucene可以说是国人在此领域非常有影响力的作品，那就首先从WebLucene的安装开始说起吧！</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　1</span>、安装系统环境</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（1）安装Java JDK</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>安装Java JDK 1.4或更新的版本，同时设置好环境变量。这应该是这篇文章读者电脑里的基本设置，所以不再详述。</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（2）安装JavaCC 2.1</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>从JavaCC的</font>
				<span>
						<span>
								<font size="2">项目主页（<a href="https://javacc.dev.java.net/servlets/ProjectDocumentList?folderID=212&amp;expandFolder=212&amp;folderID=0">https://javacc.dev.java.net/servlets/ProjectDocumentList?folderID=212&amp;expandFolder=212&amp;folderID=0</a>）</font>
						</span>
				</span>
				<font size="2">下载到JavaCC 2.1的版本，请注意，一定要下载JavaCC 2.1版本。笔者下载了JavaCC的最新版本4.0之后，发现WebLucene并不支持此版本，只好重新去下载JavaCC 2.1。</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>解压JavaCC2_1.zip文件后，打开DOS命令提示符窗口，来到刚解压生成的目录，输入如下命令来安装JavaCC（如图1所示）：</font>
		</div>
		<div>
				<span>
						<font size="2">       java -cp ./ JavaCC2_1 -c<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_1.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_1.jpg" /></a></font>
				</span>
		</div>
		<div>
				<font size="2">
						<span>                        　　　　　　　　　　　　</span>（图1）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">在接受协议之后，选择要安装的目录路径，回车后就进行自动的安装操作了，等看到如图</font>
		</div>
		<div>
				<font size="2">2所示的提示表示已安装成功。<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_2.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_2.jpg" /></a></font>
		</div>
		<div>
				<font size="2">
						<span>           　　　　　　</span>（图2）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　</span>（3）安装Ant</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>从<a href="http://ant.apache.org/">http://ant.apache.org</a>下载Ant的最新版本，解压文件后，分别设置环境变量ANT_HOME与PATH。</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>在DOS命令提示符窗口输入ant，如果出现如图3所示的提示，则表明已安装成功。<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_3.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_3.jpg" /></a></font>
		</div>
		<div>
				<font size="2">
						<span>               　　　　</span>（图3）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　</span>（4）部署WebLucene工程<br />　　</font>
				<font size="2">本文使用的应用服务器为resin-3.0.17，将下载来的WebLucene压缩包解压到resin-3.0.17目录下的webapps目录中。</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　2</span>、Build项目</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（1）准备Build环境</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>首先将weblucene目录下的文件build.properties.default重命名为build.properties，打开此文件，将内容修改为如下的形式，读者可依据自己的实际目录来做相应调整：</font>
		</div>
		<div>
				<font size="2"># ---------------------------------------------------------</font>
		</div>
		<div>
				<font size="2"># WebLucene BUILD PROPERTIES</font>
		</div>
		<div>
				<font size="2"># ---------------------------------------------------------</font>
		</div>
		<div>
				<font size="2">jsdk_jar=E:\\resin-3.0.17\\lib\\jsdk-24.jar</font>
		</div>
		<div>
				<font size="2">javacc.home=D:\\opensource\\JavaCC2_1\\javacc2.1\\bin</font>
		</div>
		<div>
				<font size="2">javacc.zip.dir=D:\\opensource\\JavaCC2_1\\javacc2.1\\bin\\lib</font>
		</div>
		<div>
				<font size="2">javacc.zip=D:\\opensource\\JavaCC2_1\\javacc2.1\\bin\\lib\\JavaCC.zip</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（2）Build工程</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>在DOS命令提示符窗口中将当前路径调整到weblucene目录，输入“ant build”命令，如出现图4所示的提示，则表示已Build成功。如果build失败，请检查CLASSPATH中的环境变量以及build.properties文件中的相应配置是否正确。<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_4.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_4.jpg" /></a></font>
		</div>
		<div>
				<font size="2">
						<span>                             　　　　　　　　　　　</span>（图4）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　3</span>、创建索引</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>创建索引使用IndexRunner命令，位于/weblucene/WEB-INF/classes/IndexRunner.class，其参数格式为：</font>
		</div>
		<div>
				<font size="2">
						<span>　　-i xml_url       </span>输入XML的URL</font>
		</div>
		<div>
				<font size="2">
						<span>　　-o output_dir    </span>输出索引的目录</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>在dump目录中有一blog.xml文件，我们可以对其进行创建索引的测试。当然读者也可以使用自己准备的素材来进行创建索引的测试，在后续文章中笔者将会对其作进一步的讨论。</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>在DOS命令提示符窗口中进行如图5所示的操作，见到如下提示则表示创建索引成功：</font>
		</div>
		<div>
				<font size="2">　　50 rows added Total time Use:0 second<br />　　750 [main] INFO IndexRunner - Great! Indexing OK<br /><br /></font>
				<font size="2">
						<a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/">
								<img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_5.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_5.jpg" />
						</a> </font>
		</div>
		<div>
				<font size="2">
						<span>                             　　　　　　　　　　</span>（图5）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">对于输入上面这么多的命令，读者可能会不太习惯，笔者制作了一个批处理文件index.bat，可以在<a href="http://www.cnblogs.com/Files/dev2dev/WebLucene之安装习行录index.rar" _fcksavedurl="http://www.cnblogs.com/Files/dev2dev/WebLucene之安装习行录index.rar">http://www.cnblogs.com/Files/dev2dev/WebLucene之安装习行录index.rar</a></font>
				<font size="2">下载。读者下载后可依据自己的实际目录来做相应调整，然后双击此文件运行即可完成创建索引的操作了。</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　4</span>、搜索测试</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>搜索使用SearchRunner 命令，位于 /weblucene/WEB-INF/classes/SearchRunner.class，其参数格式为：<br />　　</font>
				<font size="2">-i  索引所在的目录<br />　　</font>
				<font size="2">
						<span>-f  </span>索引的字段名</font>
		</div>
		<div>
				<font size="2">　　<span>-k  </span>查询的关键字 </font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>在DOS命令提示符窗口中进行如图6所示的操作，可以看到搜索结果被打印到屏幕上：<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_6.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_6.jpg" /></a></font>
		</div>
		<div>
				<font size="2">
						<span>                                 　　　　　　　　　　　　</span>（图6）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">从上面的图中可以看出WebLucene采用了二元分词，在后续的文章中笔者将会对分词作进一步的讨论。</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>　　5</span>、部署Web应用</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（1）在resin.conf文件里加上如下的配置，读者可依据自己的实际目录来做相应调整：<br />　　&lt;host id="" root-directory="."&gt; <br /></font>
				<span>
						<font size="2">　　　&lt;web-appid='/weblucene'  document-directory="E:\resin-3.0.17\webapps\weblucene\webapp"/&gt;<br />　　&lt;/host&gt;</font>
				</span>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（2）将weblucene/webapp/WEB-INF/conf/目录下的log4j.conf.default重命名成 log4j.conf，并将文件中的log4j.appender.A1.File设置成为E:\\resin-3.0.17\\webapps\\weblucene\\webapp\\WEB-INF\\logs\\weblucene.log，请读者依据自己的实际目录来做调整。</font>
		</div>
		<div>
				<font size="2">
						<span>　　</span>（3）启动Resin应用服务器，打开浏览器在地址栏中输入<a href="http://localhost:8080/weblucene">http://localhost:8080/weblucene</a> /search.html会出现如图7所示的页面：<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_7.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_7.jpg" /></a></font>
		</div>
		<div>
				<font size="2"> <span>                             　　　　　　　</span>（图7）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">在搜索框中输入关键字“路线图”，点击查询后会出现如图8所示的页面：<br /><br /><a href="http://www.cnblogs.com/dev2dev/" _fcksavedurl="http://www.cnblogs.com/dev2dev/"><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_8.jpg" _fcksavedurl="http://p.blog.csdn.net/images/p_blog_csdn_net/zjzcl/WebLucene1_8.jpg" /></a></font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<div>
				<font size="2">
						<span>                               　　　　　　　　　　</span>（图8）</font>
		</div>
		<div>
				<font size="2">
				</font> </div>
		<font size="2">
				<span style="FONT-SIZE: 10.5pt">　　</span>
				<span style="FONT-SIZE: 10.5pt">好了，关于</span>
				<span style="FONT-SIZE: 10.5pt">WebLucene</span>
				<span style="FONT-SIZE: 10.5pt">的安装就告一段落，在后续文章中笔者将来讲述如何实现基于</span>
				<span style="FONT-SIZE: 10.5pt">WebLucene</span>
				<span style="FONT-SIZE: 10.5pt">的应用。</span> </font>
<img src ="http://www.blogjava.net/kingpub/aggbug/66655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-08-30 14:07 <a href="http://www.blogjava.net/kingpub/articles/66655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebLucene的安装实践</title><link>http://www.blogjava.net/kingpub/articles/66654.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Wed, 30 Aug 2006 06:06:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/66654.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/66654.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/66654.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/66654.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/66654.html</trackback:ping><description><![CDATA[
		<div id="content">
				<div class="post individual" id="post-34">
						<h2>(转)WebLucene的安装实践</h2>
						<div class="entrytext">
								<p>全文搜索的问题是老大难问题，谁都要用到，但是搜索技术可是google和baidu等吃饭的家伙，怎么可能让大家知道。幸好有开源项目Lucene，不 过不支持中文。中文方块字真是难，前几天关注的聊天机器人也就是被方块字给卡死了。不过有了WebLucene，车东的一个开源项目，基于Lucene， 算是给我们带来了福音。<br />　 　计划安装WebLucene，网上找了一下资料不多，比较实在的就2篇，不过很不错啦，：）。按部就班的装完，发现还有些问题。就再找了一下，居然自己 忽略了WebLucene的中文安装手册。郁闷，都怪自己的思维定势，什么都先去网上找资料。其实这个中文安装手册已经比较详细了。btw：安装手册是非 windows平台的，所以大家还是有必要找网上的windows安装经验。不过我熟悉unix/linux这些，当然会后悔找资料花了很长时间。<br />　　好的，我来介绍windows的安装过程，我的安装实践。<br />1. 安装环境准备<br />==============</p>
								<p>1.1 安装Java JDK<br />—————-<br />安装Java JDK 1.4或更新版本(SUN <a href="http://java.sun.com/products/j2se/" target="_blank">http://java.sun.com/products/j2se/</a>).<br />关于如何在相应的操作系统上安装JDK，请参考相应文档。</p>
								<p>设置环境变量：<br />JAVA_HOME=C:\j2sdk1.4.2_07<br />PATH=%JAVA_HOME%\bin;%PATH%<br />CLASSPATH=%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar</p>
								<p>1.2 安装 JavaCC 2.1<br />———————<br />从 JavaCC 项目的主页 <a href="https://javacc.dev.java.net/" target="_blank">https://javacc.dev.java.net/</a> 下载 JavaCC 2.1 版本</p>
								<p>关于如何安装 JavaCC 2.1 请参考<br />see <a href="https://javacc.dev.java.net/doc/installhelp.html" target="_blank">https://javacc.dev.java.net/doc/installhelp.html</a><br />如何找到旧版本的JavaCC<br /><a href="https://javacc.dev.java.net/files/documents/17/711/JavaCC2_1.class" target="_blank">https://javacc.dev.java.net/files/documents/17/711/JavaCC2_1.class</a></p>
								<p>命令行模式的安装：<br />java -cp ./ JavaCC2_1 -c<br />（如果不加-c就是图形模式安装）<br />我推荐下在zip版本；下载javacc2.1  点击下载此文件， 解压缩javacc2.1版本，在dos下进入目录（也就是JavaCC2_1.class的目录），执行java -cp ./ JavaCC2_1 -c，会出现一些提示信息，回车直至出现让你输入安装目录的提示Enter installation directory，输入你要安装的目录，我的是：C:\Develop\javacc2.1。安装成功后，桌面会出现一个javacc的图标（不知道是 不是一个马头，呵呵）。<br />（特别注意：感觉weblucene只支持javacc2.1，反正最新javacc3.2版本不能够用）</p>
								<p>1.3 安装 Ant<br />————<br />从 <a href="http://ant.apache.org/" target="_blank">http://ant.apache.org</a> 下载Ant关于如何安装Ant的细节请参考 <a href="http://ant.apache.org/manual/index.html" target="_blank">http://ant.apache.org/manual/index.html</a></p>
								<p>设置环境变量<br />ANT_HOME=C:\java\ant-1.6.5<br />PATH=%ANT_HOME%\bin;%PATH%</p>
								<p>在dos窗口输入ant，如果出现：<br />Buildfile: build.xml does not exist!<br />Build failed<br />则证明安装成功。</p>
								<p>1.4 安装Java应用服务器<br />———————<br />WebLucene 需要 Servlet 2.3 / JSP 1.2 兼容的Java应用服务器。 此版本在Tomcat 4.1.x和Resin 2.1.x上测试通过。</p>
								<p>
										<a href="http://jakarta.apache.org/tomcat/index.html" target="_blank">http://jakarta.apache.org/tomcat/index.html</a> 安装 Tomcat</p>
								<p>设置环境变量：<br />CATALINA_HOME=C:\Develop\Tomcat4.1</p>
								<p>Tomcat在standalone模式下，使用8080端口。<br />也可以将应用服务器和Web服务器绑定在一起使用，请参考相应的安装和环境变量设置。</p>
								<p>进 入tomcat的安装目录C:\Develop\Tomcat4.1\bin，双击打开startup.bat就可以启动tomcat。启动时会跳出一个 dos窗口，出现一大堆提示信息，最后会有一句Server startup in **** ms，这样就表示安装成功，服务启动了。</p>
								<p>1.5 将WebLucene部署到成webapps<br />—————————–<br />只需将tar.gz包解压到Tomcat的webapps目录（官方解释）<br />我的做法是将weblucene.gz解压，得到weblucene文件，没有后缀。对文件改名成weblucene.rar。再解压。对解压的文件复制到Tomcat的webapps目录<br />C:\Develop\Tomcat4.1\webapps\weblucene下面有一个文件build.properties.default，将其重命名为build.properties。</p>
								<p>修改build.properties的内容为：<br />jsdk_jar=C:\\Develop\\Tomcat4.1\\\common\\lib\\servlet.jar<br />javacc.home = C:\\Develop\\javacc2.1\\bin<br />javacc.zip.dir = C:\\Develop\\javacc2.1\\bin\\lib<br />javacc.zip = C:\\Develop\\javacc2.1\\bin\\lib\\JavaCC.zip</p>
								<p>需要检查自己的目录中，是否有servlet.jar和JavaCC.zip这两个文件，因为版本不一样，有的命名也不一样。路径是\\，而不是/，大家不要搞错了。同样建议不要使用环境变量%，因为好像这里不起作用，：（</p>
								<p>2.2 Build<br />———<br />在weblucene根目录下运行 “ant build” 命令：</p>
								<p>C:\Develop\Tomcat4.1\webapps\weblucene&gt;ant build</p>
								<p>注意：如果build失败，请检查%CLASSPATH% 环境变量，并确保重要的jar包都在相应路径下。<br />提示失败而前面的步骤（安装工具以及环境变量的设置）又都正确，那就检查build.properties文件是否正确，以及build.xml是否正确。根据提示信息，检查出了什么错误，直至成功。成功后会出现：<br />BUILD SUCCESSFUL<br />Total time: 5 seconds<br />成功Build， 下一步就是准备索引了。</p>
								<p>直接解压下载的文件即可。如果你要最新版本，按以下方法。<br />程序代码</p>
								<p>从Sourceforge.net获得Weblucene的最新版本：<br />匿名导出：<br />cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/weblucene login<br />cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/weblucene co weblucene<br />sourceforge.net 的cvs 操作起来要稍微麻烦一些，必须先通过ssh username@cvs.sourceforge.net，<br />ssh username@cvs.sourceforge.net<br />把 CVS_RSH 设置为ssh，<br />export CVS_RSH=ssh<br />然后才能够通过ext模式进行更新和提交，每次add、commit 操作都要输入一次密码:<br />cvs -d:ext:username@cvs.sourceforge.net:/cvsroot/weblucene export -D now weblucene</p>
								<p>3. 准备Index<br />============<br />(在这里要特别注意：在准备索引的时候要先检查jdk的版本，1.3版本是出错的，要1.4以上的才可以)<br />weblucene中包含了一个简单的 xml 文档 /weblucene/doc/news_sample.xml<br />关于xml的结构请参考：/weblucene/doc/weblucene_index.dtd<br />你可以执行命令创建索引。</p>
								<p>创建索引使用IndexRunner 命令：位于 /weblucene/WEB-INF/classes/IndexRunner.class),</p>
								<p>参数格式：<br />-i xml_url 输入XML的URL<br />-o output_dir 输出索引的目录</p>
								<p>注意：<br />XML数据源将被索引到 $output_dir的 index 子目录下，如果对一个旧的索引进行更新，<br />旧索引将被备份到$output_dir 的 work 子目录下，</p>
								<p>例子:<br />在dump下面有个blog.xml，这是一些文章素材，你可以对其建立索引测试，也可以使用自己的语料建立索引。<br />进入C:\Develop\tomcat4.1\webapps\weblucene\WEB-INF\classes，在这个目录下面有一个IndexRunner的文件。在该目录下面建立一个run.bat的文件，内容如下：<br />程序代码</p>
								<p>set LIB=C:\Develop\tomcat4.1\webapps\weblucene\webapp\WEB-INF\lib<br />set SERVLETLIB=C:\Develop\tomcat4.1\common\lib<br />set CLASSESLIB=C:\Develop\tomcat4.1\webapps\weblucene\webapp\WEB-INF\classes<br />set XMLPATH=C:\Develop\tomcat4.1\webapps\weblucene\dump<br />set LOGPATH=C:\Develop\tomcat4.1\webapps\weblucene\webapp\WEB-INF\var</p>
								<p>java -cp %CLASSESLIB%;%LIB%\java-getopt.jar;%LIB%\jdom.jar;%LIB%\log4j.jar;%LIB%\lucene.jar;%LIB%\xalan.jar;%LIB%\xercesImpl.jar;%SERVLETLIB%\servlet.jar;./ IndexRunner -i %XMLPATH%\blog.xml -o %LOGPATH%\blog</p>
								<p>每次需要执行索引的时间，在该目录下输入文件名直接执行或是图形界面下双击该文件均可。<br />成功后会出现：<br />50 rows added Total time Use:0 second<br />750 [main] INFO IndexRunner - Great! Indexing OK</p>
								<p>注意：确保路径中没有空格，一般tomcat4.1会目录路径上tomcat和4.1之前有空格。切记！</p>
								<p>3. 运行搜索测试<br />==============</p>
								<p>SearchRunner (位于 /weblucene/WEB-INF/classes/SearchRunner.class) 是一个命令行的索引测试工具</p>
								<p>格式：<br />%java SearchRunner -i $weblucene_home_directory/WEB-INF/var/$dir_name/index \<br />-f IndexName -k “query keywords”<br />例如：<br />还 是刚才的目录，执行命令：java -cp ../lib/java-getopt.jar;../lib/jdom.jar;../lib/log4j.jar;../lib/lucene.jar;../lib/xalan.jar;../lib/xercesImpl.jar;../lib/servlet.jar;./ SearchRunner -i ../var/blog/index -f “FullIndex” -k “SOAP”</p>
								<p>当然也可以做成bat文件。<br />程序代码</p>
								<p>set LIB=C:\Develop\tomcat4.1\webapps\weblucene\webapp\WEB-INF\lib<br />set SERVLETLIB=C:\Develop\tomcat4.1\common\lib<br />set CLASSESLIB=C:\Develop\tomcat4.1\webapps\weblucene\webapp\WEB-INF\classes<br />set XMLPATH=C:\Develop\tomcat4.1\webapps\weblucene\dump<br />set LOGPATH=C:\Develop\tomcat4.1\webapps\weblucene\webapp\WEB-INF\var</p>
								<p>java -cp %CLASSESLIB%;%LIB%\java-getopt.jar;%LIB%\jdom.jar;%LIB%\log4j.jar;%LIB%\lucene.jar;%LIB%\xalan.jar;%LIB%\xercesImpl.jar;%SERVLETLIB%\servlet.jar;./ SearchRunner -i %LOGPATH%\blog\index -f “FullIndex” -k “SOAP”</p>
								<p>这样就会出现搜索到的文章信息了。因为我们刚才建索引时，把索引建在了var/blog/目录下，所以现在就要使用../var/blog/index，如果你刚才建立的不是这个目录，记得修改。<br />SOAP的含义就是关键词，也就是你要搜索含java的文章。FullIndex就是有索引的项，你对那几个字段建立了索引，都会写在FullIndex这个字段里面。</p>
								<p>搜索结果将被打印到屏幕上：<br />程序代码</p>
								<p>— find 1<br />score=0.48 http://www.javaws.com/snipsnap/space/2003-12-08#apache.axis<br />.tcp.monitor&gt; </p>
								<div class="clear">
								</div>
						</div>
				</div>
		</div>
<img src ="http://www.blogjava.net/kingpub/aggbug/66654.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-08-30 14:06 <a href="http://www.blogjava.net/kingpub/articles/66654.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为自己的系统搞个全文搜索</title><link>http://www.blogjava.net/kingpub/articles/64177.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Thu, 17 Aug 2006 10:27:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/64177.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/64177.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/64177.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/64177.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/64177.html</trackback:ping><description><![CDATA[在本文我又提到lucene了，在java业界，提到全文检索，几乎没有什么人不知道它。<br />用google搜索一下，满世界都是有关资料。具有代表性的就是车东的“基于Java的全文索引引擎Lucene简介”，<br />我要写的也就只有最简单的三板斧，再加上支持中文的ChineseAnalyzer以及按照时间排序的搜索结果排序方法。<br />这些都可以在其他地方找到相关资料，我只是把他们提出来，作为lucence应用中经常遇到的麻烦解决办法。<br />去年MSN上面有个朋友跟我提到希望用lucene构建个网站的全文检索，我当时就觉得很简单，直说没问题没问题，<br />不过他提到一个要求就是搜索结果要安装时间排序，我查阅了些资料，发现lucene并不提供用户自定义排序方式，<br />而只能按照自己相关性算法排序。后来我在车东的weblucene项目找到了IndexOrderSearcher。<br />解决了结果排序常规需求。<br />IndexOrderSearcher跟一般IndexSearch使用差不多，仅仅在构建对象的时候多加一个参数IndexOrderSearcher.ORDER_BY_DOCID_DESC<br />IndexOrderSearcher indexsearcher = new IndexOrderSearcher("/home/lucenetest/index",IndexOrderSearcher.ORDER_BY_DOCID_DESC);<br />新版本的lucene还提供了一个MultiFieldQueryParser，可以同时检索多个字段，以前QueryParser比较麻烦。<br />private static ChineseAnalyzer chineseAnalyzer = new ChineseAnalyzer();<br />public Hits search(String queryText){<br />if (queryText == null){<br />return null;<br />}<br />Query query;<br />try{<br />query = MultiFieldQueryParser.parse(queryText, new String[]{"title"},chineseAnalyzer);<br />return indexsearcher.search(query);<br />}catch(Exception e){<br />return null;<br />}<br />}<br />下面是构建索引，定时从数据库取出数据索引，做完记录完成时间，我是把时间写入一个txt文件。<br />package com.test.search;<br /><br />import org.apache.lucene.analysis.Analyzer;<br />import org.apache.lucene.analysis.cn.*;<br />import org.apache.lucene.analysis.standard.StandardAnalyzer;<br />import org.apache.lucene.document.*;<br />import org.apache.lucene.index.*;<br /><br />import java.io.*;<br />import java.sql.*;<br />import java.util.Date;<br /><br />import com.test.db.*;<br />import com.test.utility.*;<br /><br />/**<br />* Title: SearchIndexer<br />* Description: 全文索引<br />* Copyright: Copyright (c) 2001<br />* Company: test<br />* @author Sean<br />* @version 1.0<br />*/<br />public class SearchIndexer {<br />private String indexPath = null;<br />protected Analyzer analyzer = new ChineseAnalyzer();<br /><br />public SearchIndexer(String s) {<br />this.indexPath = s;<br />}<br />/**<br />* 索引某日期以前的所有文档<br />* @param fromdate<br />* @return<br />*/<br />public final void updateIndex(String fromdate) {<br />Connection conn = DbUtil.getCon();<br />IndexWriter indexWriter = null;<br />try {<br />indexWriter = getWriter(false);<br />//索引发布系统内部文件<br />PreparedStatement pstm = conn.prepareStatement(<br />"select title,body,creationtime from document where creationtime &gt; " + fromdate +<br />" order by creationtime");<br />ResultSet rs = pstm.executeQuery();<br />while (rs.next()) {<br />String creationtime = rs.getString("creationtime");<br />String title = rs.getString("title");<br />String body = rs.getString("body");<br /><br /><br />if (title == null || body == null) {<br />continue;<br />}<br />try {<br />addDocsToIndex(title,body, creationtime,indexWriter);<br />}<br />catch (Exception ex) {<br />ex.printStackTrace();<br />}<br />}<br />indexWriter.optimize();<br />}<br />catch (Exception ex) {<br />ex.printStackTrace();<br />}<br />finally {<br />try {<br />indexWriter.close();<br />conn.close();<br />}<br />catch (Exception e) {<br />e.printStackTrace();<br />}<br />}<br />}<br />/**<br />* 检查索引文件是否存在<br />* @param s<br />* @return 索引是否存在<br />*/<br />private boolean indexExists(String s) {<br />File file = new File(s + File.separator + "segments");<br />return file.exists();<br />}<br />/**<br />* 增加一组索引<br />* @param title<br />* @param body<br />* @param creationtime<br />* @param indexwriter<br />* @return<br />*/<br />private final void addNewsToIndex(String docid, String url,String title, String body,<br />String ptime, IndexWriter indexwriter) throws<br />IOException {<br />if (indexwriter == null) {<br />return;<br />}<br />else {<br />try {<br />Document document = new Document();<br />document.add(Field.Text("title", title));<br />document.add(Field.Text("body", body));<br />document.add(new Field("creationtime", creationtime, true, true, false));<br />indexwriter.addDocument(document);<br />}<br />catch (Exception ex) {<br />ex.printStackTrace();<br />}<br />return;<br />}<br />}<br />/**<br />* 取得IndexWriter<br />* @param flag 是否新建索引<br />* @return IndexWriter<br />*/<br />private IndexWriter getWriter(boolean flag) throws IOException {<br />String s = indexPath;<br />if (s == null) {<br />throw new IOException("索引文件路径设置错误.");<br />}<br />indexPath = s + File.separator + "search";<br />IndexWriter indexwriter = null;<br />if (flag) {<br />try {<br />indexwriter = new IndexWriter(indexPath, analyzer, true);<br />}<br />catch (Exception exception) {<br />System.err.println("ERROR: Failed to create a new index writer.");<br />exception.printStackTrace();<br />}<br />}<br />else {<br />if (indexExists(indexPath)) {<br />try {<br />indexwriter = new IndexWriter(indexPath, analyzer, false);<br />}<br />catch (Exception exception1) {<br />System.err.println("ERROR: Failed to open an index writer.");<br />exception1.printStackTrace();<br />}<br />}<br />else {<br />try {<br />indexwriter = new IndexWriter(indexPath, analyzer, true);<br />}<br />catch (Exception exception2) {<br />System.err.println("ERROR: Failed to create a new index writer.");<br />exception2.printStackTrace();<br />}<br />}<br />}<br />return indexwriter;<br />}<br /><br />public static void main(String[] args) {<br />String lastUpdate = "/home/lucenetest/lastUpdate.txt";<br />SearchIndexer searchIndexer = new SearchIndexer("/home/lucenetest/index");<br />//取出上次更新时间<br />String str = Util.readTxtFile(lastUpdate);<br />if(str==null || str.length()==0){<br />str = new java.util.Date().toString();<br />}<br />searchIndexer.updateIndex(str);<br />//写入当前时间<br />Util.writeTxtFile(lastUpdate,new java.util.Date(),false);<br />}<br />}<br />写个cmd或者sh在相应操作系统下面定时执行SearchIndexer就可以了。<br /><img src ="http://www.blogjava.net/kingpub/aggbug/64177.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-08-17 18:27 <a href="http://www.blogjava.net/kingpub/articles/64177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lucene的算法原理</title><link>http://www.blogjava.net/kingpub/articles/64174.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Thu, 17 Aug 2006 10:06:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/64174.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/64174.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/64174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/64174.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/64174.html</trackback:ping><description><![CDATA[
		<p>
				<strong>Lucene的概述：</strong>
		</p>
		<p>　　Lucene(发音为 <font color="#800000">['lusen]</font> )是一个非常优秀的开源的全文搜索引擎,我们可以在它的上面开发出各种全文搜索的应用来。Lucene在国外有很高的知名度，现在已经是Apache的顶级项目，在国内，Lucene的应用也越来越多。</p>
		<p>
				<strong>Lucene的算法原理：</strong>
		</p>
		<p>　　Lucene是一个高性能的java全文检索工具包，它使用的是倒排文件索引结构。该结构及相应的生成算法如下： </p>
		<p>　0）设有两篇文章1和2 <br />　　 文章1的内容为：Tom lives in Guangzhou,I live in Guangzhou too. <br />　　 文章2的内容为：He once lived in Shanghai. </p>
		<p>　1)<font color="#800000">全文分析</font>：由于lucene是基于关键词索引和查询的，首先我们要取得这两篇文章的关键词，通常我们需要如下处理措施 <br />　　a.我们现在有的是文章内容，即一个字符串，我们先要找出字符串中的所有单词，即分词。英文单词由于用空格分隔，比较好处理。中文单词间是连在一起的需要特殊的分词处理。 <br />　　b.文章中的”in”, “once” “too”等词没有什么实际意义，中文中的“的”“是”等字通常也无具体含义，这些不代表概念的词可以过滤掉 <br />　　c.用户通常希望查“He”时能把含“he”，“HE”的文章也找出来，所以所有单词需要统一大小写。 <br />　　d.用户通常希望查“live”时能把含“lives”，“lived”的文章也找出来，所以需要把“lives”，“lived”还原成“live” <br />　　e.文章中的标点符号通常不表示某种概念，也可以过滤掉 <br />　<font color="#008080">在lucene中以上措施由Analyzer类完成</font></p>
		<p>　经过上面处理后 <br />　　文章1的所有关键词为：[tom] [live] [guangzhou] [i] [live] [guangzhou] <br />　　文章2的所有关键词为：[he] [live] [shanghai] </p>
		<p>　2) <font color="#800000">倒排索引</font>：有了关键词后，我们就可以建立倒排索引了。上面的对应关系是：“文章号”对“文章中所有关键词”。倒排索引把这个关系倒过来，变成<font color="#008080">：“关键词”对“拥有该关键词的所有文章号”。</font>文章1，2经过倒排后变成 <br />关键词 文章号 <br />　　guangzhou 1 <br />　　he 2 <br />　　i 1 <br />　　live 1,2 <br />　　shanghai 2 <br />　　tom 1 </p>
		<p>　　通常仅知道关键词在哪些文章中出现还不够，我们还需要知道关键词在文章中出现次数和出现的位置，通常有两种位置：a)字符位置，即记录该词是文章中第几个字符（优点是关键词亮显时定位快）；b)关键词位置，即记录该词是文章中第几个关键词（优点是节约索引空间、词组（phase）查询快），lucene中记录的就是这种位置。 </p>
		<p>加上“出现频率”和“出现位置”信息后，我们的索引结构变为： </p>
		<p> </p>
		<p>
		</p>
		<table height="226" cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
				<tbody>
						<tr>
								<td> 关键词</td>
								<td> 文章号</td>
								<td> [出现频率]</td>
								<td> 出现位置</td>
						</tr>
						<tr>
								<td> guangzhou</td>
								<td> 1</td>
								<td> [2]</td>
								<td> 3，6</td>
						</tr>
						<tr>
								<td> he</td>
								<td> 2</td>
								<td> [1] </td>
								<td> 1</td>
						</tr>
						<tr>
								<td> i </td>
								<td> 1</td>
								<td> [1]</td>
								<td> 4</td>
						</tr>
						<tr>
								<td> live </td>
								<td> 1</td>
								<td> [2]</td>
								<td> 2，5</td>
						</tr>
						<tr>
								<td> </td>
								<td> 2</td>
								<td> [1]</td>
								<td> 2</td>
						</tr>
						<tr>
								<td> shanghai</td>
								<td> 2</td>
								<td> [1] </td>
								<td> 3</td>
						</tr>
						<tr>
								<td> tom </td>
								<td> 1</td>
								<td> [1]</td>
								<td> 1</td>
						</tr>
				</tbody>
		</table>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>
		</p>
		<p>　　以live 这行为例我们说明一下该结构：live在文章1中出现了2次，文章2中出现了一次，它的出现位置为“2,5,2”这表示什么呢？我们需要结合文章号和出现频率来分析，文章1中出现了2次，那么“2,5”就表示live在文章1中出现的两个位置，文章2中出现了一次，剩下的“2”就表示live是文章2中第 2个关键字。 <br />　　以上就是lucene索引结构中最核心的部分。我们注意到关键字是<font color="#008080">按字符顺序排列的（lucene没有使用B树结构），</font>因此lucene可以用<font color="#008080">二元搜索算法</font>快速定位关键词。 <br />　　实现时 lucene将上面三列分别作为<font color="#800000">词典文件</font>（Term Dictionary）、<font color="#800000">频率文件</font>(frequencies)、<font color="#800000">位置文件</font> (positions)保存。其中<font color="#008080">词典文件不仅保存有每个关键词，还保留了指向频率文件和位置文件的指针，</font>通过指针可以找到该关键字的频率信息和位置信息。 </p>
		<p>　　Lucene中使用了<font color="#800000">field</font>的概念，用于<font color="#008080">表达信息所在位置（如标题中，文章中，url中），</font>在建索引中，该<font color="#008080">field信息也记录在词典文件中</font>，每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。 <br />　　为了减小索引文件的大小，Lucene对索引还使用了<font color="#800000">压缩技术</font>。首先，对词典文件中的关键词进行了压缩，关键词压缩为&lt;前缀长度，后缀&gt;，例如：当前词为“阿拉伯语”，上一个词为“阿拉伯”，那么“阿拉伯语”压缩为&lt;3，语&gt;。其次大量用到的是对数字的压缩，数字只保存与上一个值的差值（这样可以减小数字的长度，进而减少保存该数字需要的字节数）。例如当前文章号是16389（不压缩要用3个字节保存），上一文章号是16382，压缩后保存7（只用一个字节）。 <font color="#008080">注意是“上一个词”。由于词典是按顺序排列的，这种压缩方法的效果会非常显著。</font></p>
		<p>　　下面我们可以通过对该索引的查询来解释一下为什么要建立索引。 <br />假设要查询单词 “live”，lucene先对词典二元查找、找到该词，通过指向频率文件的指针读出所有文章号，然后返回结果。词典通常非常小，因而，整个过程的时间是毫秒级的。 <br />而用普通的顺序匹配算法，不建索引，而是对所有文章的内容进行字符串匹配，这个过程将会相当缓慢，当文章数目很大时，时间往往是无法忍受的。</p>
		<p>
				<strong>全文检索框架的实现机制：</strong>
		</p>
		<p>
				<strong>
				</strong>　　Lucene的API接口设计的比较通用，输入输出结构都很像数据库的表==&gt;记录==&gt;字段，所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看：可以先把Lucene当成一个支持全文索引的数据库系统。</p>
		<p>比较一下Lucene和数据库：</p>
		<p>
		</p>
		<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
				<tbody>
						<tr>
								<td> Lucene</td>
								<td> 数据库</td>
						</tr>
						<tr>
								<td>
										<p> 索引数据源：doc(field1,field2...) doc(field1,field2...) </p>
										<p>            \  indexer /<br />        _____________<br />        | Lucene Index |<br />            --------------<br />           / searcher \</p>
										<p>结果输出：Hits(doc(field1,field2) doc(field1...))</p>
								</td>
								<td>
										<p> 索引数据源：record(field1,field2...) record(field1..)  </p>
										<p>            \  SQL: insert/ <br />          _____________<br />           |   DB  Index   |<br />               -------------  <br />            / SQL: select \</p>
										<p>结果输出：results(record(field1,field2..) record(field1...))</p>
								</td>
						</tr>
						<tr>
								<td>
										<p> Document：一个需要进行索引的“单元,一个Document由多个字段组成 </p>
								</td>
								<td>
										<p> Record：记录，包含多个字段<br /></p>
								</td>
						</tr>
						<tr>
								<td>
										<p>Field：字段</p>
								</td>
								<td>Field：字段</td>
						</tr>
						<tr>
								<td>
										<p>Hits：查询结果集，由匹配的Document组成</p>
								</td>
								<td> RecordSet：查询结果集，由多个Record组成</td>
						</tr>
				</tbody>
		</table>
		<p>
				<strong>
				</strong>
		</p>
		<p>
				<strong>全文检索 ≠ like "%keyword%"</strong>
		</p>
		<p>　　由于数据库索引不是为全文索引设计的，因此，使用like "%keyword%"时，数据库索引是不起作用的，在使用like查询时，搜索过程又变成类似于一页页翻书的遍历过程了，所以对于含有模糊查询的数据库服务来说，LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配：like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。</p>
		<p>　　通常比较厚的书籍后面常常附关键词索引表（比如：北京：12, 34页，上海：3,77页……），它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样，想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高，另外一个原因是它是排好序的。对于检索系统来说核心是一个排序问题。</p>
		<p>　　所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制，将数据源（比如多篇文章）排序顺序存储的同时，有另外一个排好序的关键词列表，用于存储关键词==&gt;文章映射关系，利用这样的映射关系索引：[关键词==&gt;出现关键词的文章编号，出现次数（甚至包括位置：起始偏移量，结束偏移量），出现频率]，检索过程就是把模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程。从而大大提高了多关键词查询的效率，所以，全文检索问题归结到最后是一个排序问题。</p>
		<p>　　由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题，这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制，并提供了扩展接口，以方便针对不同应用的定制。</p>
		<p>　　可以通过一下表格对比一下数据库的模糊查询：</p>
		<p>
		</p>
		<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
				<tbody>
						<tr>
								<td> </td>
								<td> Lucene全文索引引擎</td>
								<td> 数据库</td>
						</tr>
						<tr>
								<td> 索引</td>
								<td> 将数据源中的数据都通过全文索引一一建立反向索引</td>
								<td> 对于LIKE查询来说，数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配，比有索引的搜索速度要有多个数量级的下降。</td>
						</tr>
						<tr>
								<td> 匹配效果</td>
								<td> 通过词元(term)进行匹配，通过语言分析接口的实现，可以实现对中文等非英语的支持。</td>
								<td> 使用：like "%net%" 会把netherlands也匹配出来，<br />多个关键词的模糊匹配：使用like "%com%net%"：就不能匹配词序颠倒的xxx.net..xxx.com</td>
						</tr>
						<tr>
								<td> 匹配度</td>
								<td> 有匹配度算法，将匹配程度（相似度）比较高的结果排在前面。</td>
								<td> 没有匹配程度的控制：比如有记录中net出现5词和出现1次的，结果是一样的</td>
						</tr>
						<tr>
								<td> 结果输出</td>
								<td> 通过特别的算法，将最匹配度最高的头100条结果输出，结果集是缓冲式的小批量读取的。</td>
								<td> 返回所有的结果集，在匹配条目非常多的时候（比如上万条）需要大量的内存存放这些临时结果集。</td>
						</tr>
						<tr>
								<td> 可定制性</td>
								<td> 通过不同的语言分析接口实现，可以方便的定制出符合应用需要的索引规则（包括对中文的支持）</td>
								<td> 没有接口或接口复杂，无法定制</td>
						</tr>
						<tr>
								<td> 结论</td>
								<td> 高负载的模糊查询应用，需要负责的模糊查询的规则，索引的资料量比较大</td>
								<td> 使用率低，模糊匹配规则简单或者需要模糊查询的资料量少</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font color="#008080">
						<span style="FONT-WEIGHT: bold">
								<span style="FONT-WEIGHT: bold">
										<font color="#008080">全文检索和数据库应用最大的不同在于：让<span style="FONT-WEIGHT: bold">最相关的</span></font>
										<font color="#008080">头100条结果满足98%以上用户的需求。</font>
								</span>
						</span>
				</font>
				<span style="FONT-WEIGHT: bold">
						<br />Lucene的创新之处：</span>
		</p>
		<p>　　大部分的搜索（数据库）引擎都是用B树结构来维护索引，索引的更新会导致大量的IO操作，Lucene在实现中，对此稍微有所改进：不是维护一个索引文件，而是在扩展索引的时候不断创建新的索引文件，然后定期的把这些新的小索引文件合并到原先的大索引中（针对不同的更新策略，批次的大小可以调整），这样在不影响检索的效率的前提下，提高了索引的效率。</p>
		<p>Lucene和其他一些全文检索系统/应用的比较：</p>
		<p>
		</p>
		<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
				<tbody>
						<tr>
								<td> </td>
								<td> Lucene</td>
								<td> 其他开源全文检索系统</td>
						</tr>
						<tr>
								<td> 增量索引和批量索引</td>
								<td> 可以进行增量的索引(Append)，可以对于大量数据进行批量索引，并且接口设计用于优化批量索引和小批量的增量索引。</td>
								<td> 很多系统只支持批量的索引，有时数据源有一点增加也需要重建索引。</td>
						</tr>
						<tr>
								<td> 数据源</td>
								<td> Lucene没有定义具体的数据源，而是一个文档的结构，因此可以非常灵活的适应各种应用（只要前端有合适的转换器把数据源转换成相应结构）。</td>
								<td> 很多系统只针对网页，缺乏其他格式文档的灵活性。</td>
						</tr>
						<tr>
								<td> 索引内容抓取</td>
								<td> Lucene的文档是由多个字段组成的，甚至可以控制那些字段需要进行索引，那些字段不需要索引，近一步索引的字段也分为需要分词和不需要分词的类型：<br />   需要进行分词的索引，比如：标题，文章内容字段<br />   不需要进行分词的索引，比如：作者/日期字段</td>
								<td> 缺乏通用性，往往将文档整个索引了</td>
						</tr>
						<tr>
								<td> 语言分析</td>
								<td> 通过语言分析器的不同扩展实现：<br />可以过滤掉不需要的词：an the of 等，<br />西文语法分析：将jumps jumped jumper都归结成jump进行索引/检索<br />非英文支持：对亚洲语言，阿拉伯语言的索引支持</td>
								<td> 缺乏通用接口实现</td>
						</tr>
						<tr>
								<td> 查询分析</td>
								<td> 通过查询分析接口的实现，可以定制自己的查询语法规则：<br />比如： 多个关键词之间的 + - and or关系等</td>
								<td> 功能较强大</td>
						</tr>
						<tr>
								<td> 并发访问</td>
								<td> 能够支持多用户的使用</td>
								<td> 功能较强大</td>
						</tr>
				</tbody>
		</table>
		<p>
				<strong>关于亚洲语言的的切分词问题(Word Segment)</strong>
				<br />　　对于中文来说，全文索引首先还要解决一个语言分析的问题，对于英文来说，语句中单词之间是天然通过空格分开的，但亚洲语言的中日韩文语句中的字是一个字挨一个，所有，首先要把语句中按“词”进行索引的话，这个词如何切分出来就是一个很大的问题。<br />　　首先，肯定不能用单个字符作(si-gram)为索引单元，否则查“上海”时，不能让含有“海上”也匹配。<br />但一句话：“北京天安门”，计算机如何按照中文的语言习惯进行切分呢？<br />　　“北京 天安门” 还是“北 京 天安门”？让计算机能够按照语言习惯进行切分，往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。<br />　　另外一个解决的办法是采用自动切分算法：将单词按照2元语法(bigram)方式切分出来，比如：<br />　　　　"北京天安门" ==&gt; "北京 京天 天安 安门"。<br />这样，在查询的时候，无论是查询"北京" 还是查询"天安门"，将查询词组按同样的规则进行切分："北京"，"天安安门"，多个关键词之间按与"and"的关系组合，同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言：韩文，日文都是通用的。<br />　　基于自动切分的最大优点是没有词表维护成本，实现简单，缺点是索引效率低，但对于中小型应用来说，基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多，而对于英文，索引文件一般只有原文件的30%-40%不同。 </p>
		<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
				<tbody>
						<tr>
								<td> </td>
								<td> 自动切分</td>
								<td> 词表切分</td>
						</tr>
						<tr>
								<td> 实现</td>
								<td> 实现非常简单</td>
								<td> 实现复杂</td>
						</tr>
						<tr>
								<td> 查询</td>
								<td> 增加了查询分析的复杂程度</td>
								<td> 适于实现比较复杂的查询语法规则</td>
						</tr>
						<tr>
								<td> 存储效率</td>
								<td> 索引冗余大，索引几乎和原文一样大</td>
								<td> 索引效率高，为原文大小的30％左右</td>
						</tr>
						<tr>
								<td> 维护成本</td>
								<td> 无词表维护成本</td>
								<td> 词表维护成本非常高：中日韩等语言需要分别维护。<br />还需要包括词频统计等内容</td>
						</tr>
						<tr>
								<td> 适用领域</td>
								<td> 嵌入式系统：运行环境资源有限<br />分布式系统：无词表同步问题<br />多语言环境：无词表维护成本</td>
								<td> 对查询和存储效率要求高的专业搜索引擎<br /></td>
						</tr>
				</tbody>
		</table>
		<p>目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法，大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。</p>
		<p>Lucene的结构框架：<br />　　注意：Lucene中的一些比较复杂的词法分析是用JavaCC生成的（JavaCC：JavaCompilerCompiler，纯Java的词法分析生成器），所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器，还需要从<a href="https://javacc.dev.java.net/">https://javacc.dev.java.net/</a>下载javacc。<br />　　lucene的组成结构：对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口。 </p>
		<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
				<tbody>
						<tr>
								<td> org.apache.Lucene.search/</td>
								<td> 搜索入口</td>
						</tr>
						<tr>
								<td> org.apache.Lucene.index/</td>
								<td> 索引入口</td>
						</tr>
						<tr>
								<td> org.apache.Lucene.analysis/</td>
								<td> 语言分析器</td>
						</tr>
						<tr>
								<td> org.apache.Lucene.queryParser/</td>
								<td>查询分析器</td>
						</tr>
						<tr>
								<td> org.apache.Lucene.document/</td>
								<td> 存储结构</td>
						</tr>
						<tr>
								<td> org.apache.Lucene.store/ </td>
								<td> 底层IO/存储结构</td>
						</tr>
						<tr>
								<td> org.apache.Lucene.util/</td>
								<td> 一些公用的数据结构</td>
						</tr>
				</tbody>
		</table>
		<p>
				<strong>从Lucene学到更多：</strong>
				<br />　　Luene的确是一个面对对象设计的典范。</p>
		<ol>
				<li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要； 
</li>
				<li>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务； 
</li>
				<li>所有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修改其他模块。  
</li>
				<li>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。 </li>
		</ol>
		<p>
				<br />这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。<br />　　此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</p>
		<ol>
				<li>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。 
</li>
				<li>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的。 
</li>
				<li>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。 
</li>
				<li>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的操作。 </li>
		</ol>
		<p>摘引：<a href="http://www.chedong.com/tech/lucene.html">车东BLOG</a>：<a href="http://www.chedong.com/tech/lucene.html">http://www.chedong.com/tech/lucene.html</a></p>其他Lucene文章的连接<br /><a href="http://cvs.apache.org/dist/jakarta/lucene/" target="_blank">Lucene工具包下载</a>,下载后将得到一个名为<a href="http://apache.oregonstate.edu/jakarta/lucene/binaries/lucene-1.4-final.zip">lucene-1.4-final.zip</a>的压缩文件，解压后，里面有一个名为lucene-1.4-final.jar的文件，这就是Lucene组件包.　 lucene的api地址：<a href="http://jakarta.apache.org/lucene/docs/api/index.html" target="_blank">http://jakarta.apache.org/lucene/docs/api/index.html</a><br /><a href="http://liyu2000.nease.net/article/Lucene/Lucene.htm" target="_blank">优秀的开放源代码全文检索系统Lucene</a><br /><a href="http://www.blog.edu.cn/user2/56031/cmd.shtml?do=blogs&amp;id=47544&amp;uid=56031" target="_blank">雪地雏鹰-[信息检索]</a><br /><a href="http://www.jalorsoft.com/holen/" target="_blank">Lucene研究</a><br /><a href="http://www.cn-java.com/target/news.php?news_id=2909" target="_blank">Lucene实现的全文检索</a><img src ="http://www.blogjava.net/kingpub/aggbug/64174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-08-17 18:06 <a href="http://www.blogjava.net/kingpub/articles/64174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于Java的全文索引引擎Lucene简介</title><link>http://www.blogjava.net/kingpub/articles/63614.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Tue, 15 Aug 2006 02:05:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/63614.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/63614.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/63614.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/63614.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/63614.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a class="postTitle2" id="viewpost1_TitleUrl" href="/gaich/archive/2005/06/30/6928.html">基于Java的全文索引引擎Lucene简介 </a>
		</div>
		<h1>
				<font size="4">关键词：Lucene java full-text search engine Chinese word segment</font>
		</h1>
		<h1>
				<span style="FONT-WEIGHT: bold">
				</span>
				<font size="4">内容摘要：</font>
		</h1>
		<h1>
				<font size="4">Lucene是一个基于Java的全文索引工具包。</font>
		</h1>
		<p>作者： 车东 Email: chedongATbigfoot.com/chedongATchedong.com</p>
		<p>写于：2002/08 最后更新：
<script language="Javascript" src="http://www.chedong.com/referer.js"></script>
 08/15/2006 10:01:50<br /><a href="http://hi.baidu.com/chedong/blog/item/9f40ab18c31dd40735fa41fe.html#comment">Feed Back &gt;&gt;</a> (<a href="http://www.linuxforum.net/doc/smartq-grand.html">Read this before you ask question</a>)<a href="http://creativecommons.org/licenses/by-sa/2.5/"><br /><img alt="Creative Commons License" src="http://www.creativecommons.cn/images/public/somerights.gif" border="0" /></a><iframe name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-1309797784693300&amp;dt=1155607310454&amp;lmt=1155607310&amp;format=468x60_as&amp;output=html&amp;url=http%3A%2F%2Fwww.blogjava.net%2Fgaich%2Farchive%2F2005%2F06%2F30%2F6928.aspx&amp;color_bg=FFFFFF&amp;color_text=000000&amp;color_link=0000FF&amp;color_url=008000&amp;color_border=FFFFFF&amp;ref=http%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3Dlucene%26ie%3DUTF-8%26oe%3DGB2312%26hl%3Dzh-CN%26domains%3Dblogjava.net%26sitesearch%3Dblogjava.net&amp;u_h=768&amp;u_w=1024&amp;u_ah=740&amp;u_aw=1024&amp;u_cd=32&amp;u_tz=480&amp;u_java=true" frameborder="0" width="468" scrolling="no" height="60" allowtransparency=""><img height="1" width="1" border="0" src="http://pagead2.googlesyndication.com/pagead/imp.gif?client=ca-pub-1309797784693300&amp;dt=1155607310454&amp;lmt=1155607310&amp;format=468x60_as&amp;output=html&amp;url=http%3A%2F%2Fwww.blogjava.net%2Fgaich%2Farchive%2F2005%2F06%2F30%2F6928.aspx&amp;color_bg=FFFFFF&amp;color_text=000000&amp;color_link=0000FF&amp;color_url=008000&amp;color_border=FFFFFF&amp;ref=http%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3Dlucene%26ie%3DUTF-8%26oe%3DGB2312%26hl%3Dzh-CN%26domains%3Dblogjava.net%26sitesearch%3Dblogjava.net&amp;u_h=768&amp;u_w=1024&amp;u_ah=740&amp;u_aw=1024&amp;u_cd=32&amp;u_tz=480&amp;u_java=true&amp;event=noiframe" /></iframe> 03/16/2005 16:27:52</p>
		<ol>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#intro">基于Java的全文索引引擎Lucene简介：关于作者和Lucene的历史</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#compare">全文检索的实现：Luene全文索引和数据库索引的比较</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#segment">中文切分词机制简介：基于词库和自动切分词算法的比较</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#demo">具体的安装和使用简介：系统结构介绍和演示</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#hacking">Hacking Lucene：简化的查询分析器，删除的实现，定制的排序，应用接口的扩展</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#learn">从Lucene我们还可以学到什么</a>
				</li>
		</ol>
		<p>
				<a name="intro">
						<b>基于Java的全文索引/检索引擎——Lucene</b>
				</a>
		</p>
		<p>Lucene不是一个完整的全文索引应用，而是是一个用Java写的全文索引引擎工具包，它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。</p>
		<p>Lucene的作者：Lucene的贡献者<a href="http://www.nutch.org/blog/cutting.html">Doug Cutting</a>是一位资深全文索引/检索专家，曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者，后在Excite担任高级系统架构设计师，目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。</p>
		<p>Lucene的发展历程：早先发布在作者自己的<a href="http://www.lucene.com/">www.lucene.com</a>，后来发布在<a href="http://sourceforge.net/projects/lucene/">SourceForge</a>，2001年年底成为APACHE基金会jakarta的一个子项目：<a href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/</a></p>
		<p>已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎，比较著名的有：</p>
		<ul>
				<li>
						<a href="http://www.jivesoftware.com/">J</a>
						<a href="http://www.jivesoftware.com/">ive</a>：WEB论坛系统； 
</li>
				<li>
						<a href="http://eyebrowse.tigris.org/">Eyebrows</a>：邮件列表HTML归档/浏览/查询系统，本文的主要参考文档“<a href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html">TheLucene search engine: Powerful, flexible, and free</a>”作者就是EyeBrows系统的主要开发者之一，而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。 
</li>
				<li>
						<a href="http://xml.apache.org/cocoon/index.html">Cocoon</a>:基于XML的web发布框架，全文检索部分使用了Lucene 
</li>
				<li>
						<p align="left">
								<a href="http://www.eclipse.org/">Eclipse</a>:基于Java的开放开发平台，帮助部分的全文索引使用了Lucene</p>
				</li>
		</ul>
		<p>对于中文用户来说，最关心的问题是其是否支持中文的全文检索。但通过后面对于Lucene的结构的介绍，你会了解到由于Lucene良好架构设计，对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。</p>
		<p>
				<b>
						<a name="compare">全文检索的实现机制</a>
				</b>
		</p>
		<p>Lucene的API接口设计的比较通用，输入输出结构都很像数据库的表==&gt;记录==&gt;字段，所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看：可以先把<b>Lucene当成一个支持全文索引的数据库系统</b>。</p>
		<p>比较一下Lucene和数据库：</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="50%">Lucene</td>
								<td align="middle" width="50%">数据库</td>
						</tr>
						<tr>
								<td width="50%">
										<pre>索引数据源：doc(field1,field2...) doc(field1,field2...)<br />                  \  indexer /<br />                 _____________<br />                | Lucene Index|<br />                --------------<br />                 / searcher \<br /> 结果输出：Hits(doc(field1,field2) doc(field1...))</pre>
								</td>
								<td width="50%">
										<pre> 索引数据源：record(field1,field2...) record(field1..)<br />              \  SQL: insert/<br />               _____________<br />              | DB  Index   |<br />               -------------<br />              / SQL: select \<br />结果输出：results(record(field1,field2..) record(field1...))</pre>
								</td>
						</tr>
						<tr>
								<td width="50%">Document：一个需要进行索引的“单元”<br />一个Document由多个字段组成</td>
								<td width="50%">Record：记录，包含多个字段</td>
						</tr>
						<tr>
								<td width="50%">Field：字段</td>
								<td width="50%">Field：字段</td>
						</tr>
						<tr>
								<td width="50%">Hits：查询结果集，由匹配的Document组成</td>
								<td width="50%">RecordSet：查询结果集，由多个Record组成</td>
						</tr>
				</tbody>
		</table>
		<p>
				<b>全文检索 ≠ like "%keyword%"</b>
		</p>
		<p>通常比较厚的书籍后面常常附关键词索引表（比如：北京：12, 34页，上海：3,77页……），它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样，想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高，另外一个原因是它是排好序的。<b>对于检索系统来说核心是一个排序问题</b>。</p>
		<p align="left">由于数据库索引不是为全文索引设计的，因此，<b>使用like "%keyword%"时，数据库索引是不起作用的</b>，在使用like查询时，搜索过程又变成类似于一页页翻书的遍历过程了，所以对于含有模糊查询的数据库服务来说，LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配：like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。</p>
		<p>所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制，将数据源（比如多篇文章）排序顺序存储的同时，有另外一个排好序的关键词列表，用于存储关键词==&gt;文章映射关系，利用这样的映射关系索引：[关键词==&gt;出现关键词的文章编号，出现次数（甚至包括位置：起始偏移量，结束偏移量），出现频率]，检索过程就是把<b>模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程</b>。从而大大提高了多关键词查询的效率，所以，全文检索问题归结到最后是一个排序问题。</p>
		<p>由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题，这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制，并提供了扩展接口，以方便针对不同应用的定制。</p>
		<p>可以通过一下表格对比一下数据库的模糊查询：</p>
		<table height="283" width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="9%" height="16">　</td>
								<td align="middle" width="47%" height="16">Lucene全文索引引擎</td>
								<td align="middle" width="40%" height="16">数据库</td>
						</tr>
						<tr>
								<td width="9%" height="48">索引</td>
								<td width="47%" height="48">将数据源中的数据都通过全文索引一一建立反向索引</td>
								<td width="40%" height="48">对于LIKE查询来说，数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配，比有索引的搜索速度要有多个数量级的下降。</td>
						</tr>
						<tr>
								<td width="9%" height="49">匹配效果</td>
								<td width="47%" height="49">通过词元(term)进行匹配，通过语言分析接口的实现，可以实现对中文等非英语的支持。</td>
								<td width="40%" height="49">使用：like "%net%" 会把netherlands也匹配出来，<br />多个关键词的模糊匹配：使用like "%com%net%"：就不能匹配词序颠倒的xxx.net..xxx.com</td>
						</tr>
						<tr>
								<td width="9%" height="32">匹配度</td>
								<td width="47%" height="32">有匹配度算法，将匹配程度（相似度）比较高的结果排在前面。</td>
								<td width="40%" height="32">没有匹配程度的控制：比如有记录中net出现5词和出现1次的，结果是一样的。</td>
						</tr>
						<tr>
								<td width="9%" height="32">结果输出</td>
								<td width="47%" height="32">通过特别的算法，将最匹配度最高的头100条结果输出，结果集是缓冲式的小批量读取的。</td>
								<td width="40%" height="32">返回所有的结果集，在匹配条目非常多的时候（比如上万条）需要大量的内存存放这些临时结果集。</td>
						</tr>
						<tr>
								<td width="9%" height="32">可定制性</td>
								<td width="47%" height="32">通过不同的语言分析接口实现，可以方便的定制出符合应用需要的索引规则（包括对中文的支持）</td>
								<td width="40%" height="32">没有接口或接口复杂，无法定制</td>
						</tr>
						<tr>
								<td width="9%" height="32">结论</td>
								<td width="47%" height="32">高负载的模糊查询应用，需要负责的模糊查询的规则，索引的资料量比较大</td>
								<td width="40%" height="32">使用率低，模糊匹配规则简单或者需要模糊查询的资料量少</td>
						</tr>
				</tbody>
		</table>
		<p>
				<span style="FONT-WEIGHT: bold">全文检索和数据库应用最大的不同在于：让</span>
				<span style="FONT-WEIGHT: bold">最相关的</span>
				<span style="FONT-WEIGHT: bold">头100条结果满足98%以上用户的需求<br /></span>
				<br />Lucene的创新之处：</p>
		<p>大部分的搜索（数据库）引擎都是用B树结构来维护索引，索引的更新会导致大量的IO操作，Lucene在实现中，对此稍微有所改进：不是维护一个索引文件，而是在扩展索引的时候不断创建新的索引文件，然后定期的把这些新的小索引文件合并到原先的大索引中（针对不同的更新策略，批次的大小可以调整），这样在不影响检索的效率的前提下，提高了索引的效率。</p>
		<p>Lucene和其他一些全文检索系统/应用的比较：</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="18%">　</td>
								<td align="middle" width="45%">Lucene</td>
								<td align="middle" width="37%">其他开源全文检索系统</td>
						</tr>
						<tr>
								<td width="18%">增量索引和批量索引</td>
								<td width="45%">可以进行增量的索引(Append)，可以对于大量数据进行批量索引，并且接口设计用于优化批量索引和小批量的增量索引。</td>
								<td width="37%">很多系统只支持批量的索引，有时数据源有一点增加也需要重建索引。</td>
						</tr>
						<tr>
								<td width="18%">数据源</td>
								<td width="45%">Lucene没有定义具体的数据源，而是一个文档的结构，因此可以非常灵活的适应各种应用（只要前端有合适的转换器把数据源转换成相应结构），</td>
								<td width="37%">很多系统只针对网页，缺乏其他格式文档的灵活性。</td>
						</tr>
						<tr>
								<td width="18%">索引内容抓取</td>
								<td width="45%">Lucene的文档是由多个字段组成的，甚至可以控制那些字段需要进行索引，那些字段不需要索引，近一步索引的字段也分为需要分词和不需要分词的类型：<br />   需要进行分词的索引，比如：标题，文章内容字段<br />   不需要进行分词的索引，比如：作者/日期字段</td>
								<td width="37%">缺乏通用性，往往将文档整个索引了</td>
						</tr>
						<tr>
								<td width="18%">语言分析</td>
								<td width="45%">通过语言分析器的不同扩展实现：<br />可以过滤掉不需要的词：an the of 等，<br />西文语法分析：将jumps jumped jumper都归结成jump进行索引/检索<br />非英文支持：对亚洲语言，阿拉伯语言的索引支持</td>
								<td width="37%">缺乏通用接口实现</td>
						</tr>
						<tr>
								<td width="18%">查询分析</td>
								<td width="45%">通过查询分析接口的实现，可以定制自己的查询语法规则：<br />比如： 多个关键词之间的 + - and or关系等</td>
								<td width="37%">　</td>
						</tr>
						<tr>
								<td width="18%">并发访问</td>
								<td width="45%">能够支持多用户的使用</td>
								<td width="37%">　</td>
						</tr>
				</tbody>
		</table>
		<p>　</p>
		<p>
				<b>
						<a name="segment">关于亚洲语言的的切分词问题(Word Segment)</a>
				</b>
		</p>
		<p>对于中文来说，全文索引首先还要解决一个语言分析的问题，对于英文来说，语句中单词之间是天然通过空格分开的，但亚洲语言的中日韩文语句中的字是一个字挨一个，所有，首先要把语句中按“词”进行索引的话，这个词如何切分出来就是一个很大的问题。</p>
		<p>首先，肯定不能用单个字符作(si-gram)为索引单元，否则查“上海”时，不能让含有“海上”也匹配。</p>
		<p>但一句话：“北京天安门”，计算机如何按照中文的语言习惯进行切分呢？<br />“北京 天安门” 还是“北 京 天安门”？让计算机能够按照语言习惯进行切分，往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。</p>
		<p>另外一个解决的办法是采用自动切分算法：将单词按照2元语法(bigram)方式切分出来，比如：<br />"北京天安门" ==&gt; "北京 京天 天安 安门"。</p>
		<p>这样，在查询的时候，无论是查询"北京" 还是查询"天安门"，将查询词组按同样的规则进行切分："北京"，"天安安门"，多个关键词之间按与"and"的关系组合，同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言：韩文，日文都是通用的。</p>
		<p>基于自动切分的最大优点是没有词表维护成本，实现简单，缺点是索引效率低，但对于中小型应用来说，基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多，而对于英文，索引文件一般只有原文件的30%-40%不同，</p>
		<table height="68" width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="11%" height="18">
										<br />
								</td>
								<td align="middle" width="39%" height="18">自动切分</td>
								<td align="middle" width="50%" height="18">词表切分</td>
						</tr>
						<tr>
								<td width="11%" height="16">实现</td>
								<td width="39%" height="16">实现非常简单</td>
								<td width="50%" height="16">实现复杂</td>
						</tr>
						<tr>
								<td width="11%" height="16">查询</td>
								<td width="39%" height="16">增加了查询分析的复杂程度，</td>
								<td width="50%" height="16">适于实现比较复杂的查询语法规则</td>
						</tr>
						<tr>
								<td width="11%" height="16">存储效率</td>
								<td width="39%" height="16">索引冗余大，索引几乎和原文一样大</td>
								<td width="50%" height="16">索引效率高，为原文大小的30％左右</td>
						</tr>
						<tr>
								<td width="11%" height="16">维护成本</td>
								<td width="39%" height="16">无词表维护成本</td>
								<td width="50%" height="16">词表维护成本非常高：中日韩等语言需要分别维护。<br />还需要包括词频统计等内容</td>
						</tr>
						<tr>
								<td width="11%" height="16">适用领域</td>
								<td width="39%" height="16">嵌入式系统：运行环境资源有限<br />分布式系统：无词表同步问题<br />多语言环境：无词表维护成本</td>
								<td width="50%" height="16">对查询和存储效率要求高的专业搜索引擎<br /></td>
						</tr>
				</tbody>
		</table>
		<p>目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法，大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。</p>
		<p>
				<a name="demo">
						<b>安装和使用</b>
				</a>
		</p>
		<p>下载：<a href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/</a></p>
		<p>注意：Lucene中的一些比较复杂的词法分析是用JavaCC生成的（JavaCC：JavaCompilerCompiler，纯Java的词法分析生成器），所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器，还需要从<a href="https://javacc.dev.java.net/">https://javacc.dev.java.net/</a>下载javacc。</p>
		<p>lucene的组成结构：对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td width="27%">org.apache.Lucene.search/</td>
								<td width="73%">搜索入口</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.index/</td>
								<td width="73%">索引入口</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.analysis/</td>
								<td width="73%">语言分析器</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.queryParser/</td>
								<td width="73%">查询分析器</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.document/</td>
								<td width="73%">存储结构</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.store/ </td>
								<td width="73%">底层IO/存储结构</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.util/</td>
								<td width="73%">一些公用的数据结构</td>
						</tr>
				</tbody>
		</table>
		<p>简单的例子演示一下Lucene的使用方法：</p>索引过程：从命令行读取文件名（多个），将文件分路径(path字段)和内容(body字段)2个字段进行存储，并对内容进行全文索引：索引的单位是Document对象，每个Document对象包含多个字段Field对象，针对不同的字段属性和数据输出的需求，对字段还可以选择不同的索引/存储字段规则，列表如下： 
<table border="1"><tbody><tr><th>方法</th><th>切词</th><th>索引</th><th>存储</th><th>用途</th></tr><tr><td>Field.Text(String name, String value)</td><td>Yes</td><td>Yes</td><td>Yes</td><td valign="top">切分词索引并存储，比如：标题，内容字段</td></tr><tr><td>Field.Text(String name, Reader value)</td><td>Yes</td><td>Yes</td><td>No</td><td valign="top">切分词索引不存储，比如：META信息，<br />不用于返回显示，但需要进行检索内容</td></tr><tr><td>Field.Keyword(String name, String value)</td><td>No</td><td>Yes</td><td>Yes</td><td valign="top">不切分索引并存储，比如：日期字段</td></tr><tr><td>Field.UnIndexed(String name, String value)</td><td>No</td><td>No</td><td>Yes</td><td valign="top">不索引，只存储，比如：文件路径</td></tr><tr><td>Field.UnStored(String name, String value)</td><td>Yes</td><td>Yes</td><td>No</td><td valign="top">只全文索引，不存储</td></tr></tbody></table><pre>public class IndexFiles { <br />  //使用方法：: IndexFiles [索引输出目录] [索引的文件列表] ... <br />  public static void main(String[] args) throws Exception {<br />    String indexPath = args[0];<br />    IndexWriter writer;<br />    //用指定的语言分析器构造一个新的写索引器（第3个参数表示是否为追加索引）<br />    writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<br /><br />    for (int i=1; i&lt;args.length; i++) {<br />      System.out.println("Indexing file " + args[i]);<br />      InputStream is = new FileInputStream(args[i]);<br /><br />      //构造包含2个字段Field的Document对象<br />      //一个是路径path字段，不索引，只存储<br />      //一个是内容body字段，进行全文索引，并存储<br />      Document doc = new Document();<br />      doc.add(Field.UnIndexed("path", args[i]));<br />      doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<br />      //将文档写入索引<br />      writer.addDocument(doc);<br />      is.close();<br />    };<br />    //关闭写索引器<br />    writer.close();<br />  }<br />}<br />　</pre><p>索引过程中可以看到：</p><ul><li>语言分析器提供了抽象的接口，因此语言分析(Analyser)是可以定制的，虽然lucene缺省提供了2个比较通用的分析器SimpleAnalyser和StandardAnalyser，这2个分析器缺省都不支持中文，所以要加入对中文语言的切分规则，需要修改这2个分析器。 
</li><li>Lucene并没有规定数据源的格式，而只提供了一个通用的结构（Document对象）来接受索引的输入，因此输入的数据源可以是：数据库，WORD文档，PDF文档，HTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。 
</li><li>对于大批量的数据索引，还可以通过调整IndexerWrite的文件合并频率属性（mergeFactor）来提高批量索引的效率。 </li></ul><p>检索过程和结果显示：</p><p>搜索结果返回的是Hits对象，可以通过它再访问Document==&gt;Field中的内容。</p><p>假设根据body字段进行全文检索，可以将查询结果的path字段和相应查询的匹配度(score)打印出来，</p><pre>public class Search { <br />  public static void main(String[] args) throws Exception {<br />    String indexPath = args[0], queryString = args[1];<br />    //指向索引目录的搜索器<br />    Searcher searcher = new IndexSearcher(indexPath);<br />    //查询解析器：使用和索引同样的语言分析器<br />    Query query = QueryParser.parse(queryString, "body", <br />                              new SimpleAnalyzer());<br />    //搜索结果使用Hits存储<br />    Hits hits = searcher.search(query);<br />    //通过hits可以访问到相应字段的数据和查询的匹配度<br />    for (int i=0; i&lt;hits.length(); i++) {<br />      System.out.println(hits.doc(i).get("path") + "; Score: " + <br />                         hits.score(i));<br />    };<br />  }<br />}</pre>在整个检索过程中，语言分析器，查询分析器，甚至搜索器（Searcher）都是提供了抽象的接口，可以根据需要进行定制。 
<p><b><a name="hacking">Hacking Lucene</a></b></p><p><b>简化的查询分析器</b></p><p>个人感觉lucene成为JAKARTA项目后，画在了太多的时间用于调试日趋复杂QueryParser，而其中大部分是大多数用户并不很熟悉的，目前LUCENE支持的语法：</p><p>Query ::= ( Clause )*<br />Clause ::= ["+", "-"] [&lt;TERM&gt; ":"] ( &lt;TERM&gt; | "(" Query ")")</p><p>中间的逻辑包括：and or + - &amp;&amp;||等符号，而且还有"短语查询"和针对西文的前缀/模糊查询等，个人感觉对于一般应用来说，这些功能有一些华而不实，其实能够实现目前类似于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以，Lucene早期版本的QueryParser仍是比较好的选择。</p><p><b>添加修改删除指定记录（Document）</b></p><p>Lucene提供了索引的扩展机制，因此索引的动态扩展应该是没有问题的，而指定记录的修改也似乎只能通过记录的删除，然后重新加入实现。如何删除指定的记录呢？删除的方法也很简单，只是需要在索引时根据数据源中的记录ID专门另建索引，然后利用IndexReader.delete(Termterm)方法通过这个记录ID删除相应的Document。</p><p><b>根据某个字段值的排序功能</b></p><p>lucene缺省是按照自己的相关度算法（score）进行结果排序的，但能够根据其他字段进行结果排序是一个在LUCENE的开发邮件列表中经常提到的问题，很多原先基于数据库应用都需要除了基于匹配度（score）以外的排序功能。而从全文检索的原理我们可以了解到，任何不基于索引的搜索过程效率都会导致效率非常的低，如果基于其他字段的排序需要在搜索过程中访问存储字段，速度回大大降低，因此非常是不可取的。</p><p>但这里也有一个折中的解决方法：在搜索过程中能够影响排序结果的只有索引中已经存储的docID和score这2个参数，所以，基于score以外的排序，其实可以通过将数据源预先排好序，然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访问不在索引中的某个字段值。</p><p>这里需要修改的是IndexSearcher中的HitCollector过程：</p><pre>...<br />　scorer.score(new HitCollector() {<br />	private float minScore = 0.0f;<br />	public final void collect(int doc, float score) {<br />	  if (score &gt; 0.0f &amp;&amp;			  // ignore zeroed buckets<br />	      (bits==null || bits.get(doc))) {	  // skip docs not in bits<br />	    totalHits[0]++;<br />	    if (score &gt;= minScore) {<br />              /* 原先：Lucene将docID和相应的匹配度score例入结果命中列表中：<br />	       * hq.put(new ScoreDoc(doc, score));	  // update hit queue<br />               * 如果用doc 或 1/doc 代替 score，就实现了根据docID顺排或逆排<br />               * 假设数据源索引时已经按照某个字段排好了序，而结果根据docID排序也就实现了<br />               * 针对某个字段的排序，甚至可以实现更复杂的score和docID的拟合。<br />               */<br />              hq.put(new ScoreDoc(doc, (float) 1/doc )); <br />	      if (hq.size() &gt; nDocs) {		  // if hit queue overfull<br />		hq.pop();			  // remove lowest in hit queue<br />		minScore = ((ScoreDoc)hq.top()).score; // reset minScore<br />	      }<br />	    }<br />	  }<br />	}<br />      }, reader.maxDoc());</pre><p><b>更通用的输入输出接口</b></p><p>虽然lucene没有定义一个确定的输入文档格式，但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口，然后其他数据，比如PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主，类似实现已经不下4，5个：</p><pre>数据源: WORD       PDF     HTML    DB       other<br />         \          |       |      |         /<br />                       XML中间格式<br />                            |<br />                     Lucene INDEX</pre><p>目前还没有针对MSWord文档的解析器，因为Word文档和基于ASCII的RTF文档不同，需要使用COM对象机制解析。这个是我在Google上查的相关资料：<a href="http://www.intrinsyc.com/products/enterprise_applications.asp">http://www.intrinsyc.com/products/enterprise_applications.asp</a><br />另外一个办法就是把Word文档转换成text：<a href="http://www.winfield.demon.nl/index.html">http://www.winfield.demon.nl/index.html</a><br /></p><p><br /><b>索引过程优化</b></p><p>索引一般分2种情况，一种是小批量的索引扩展，一种是大批量的索引重建。在索引过程中，并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作（文件I/O是一件非常消耗资源的事情）。</p><p>Lucene先在内存中进行索引操作，并根据一定的批量进行文件的写入。这个批次的间隔越大，文件的写入次数越少，但占用内存会很多。反之占用内存少，但文件IO操作频繁，索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件的操作。根据我的使用经验：缺省Indexer是每20条记录索引后写入一次，每将MERGE_FACTOR增加50倍，索引速度可以提高1倍左右。<br /></p><p><span style="FONT-WEIGHT: bold">搜索过程优化<br /></span></p><p><span style="FONT-WEIGHT: bold"></span>lucene支持内存索引：这样的搜索比基于文件的I/O有数量级的速度提升。<br /><a href="http://www.onjava.com/lpt/a/3273">http://www.onjava.com/lpt/a/3273</a><br />而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。<br /><span style="FONT-WEIGHT: bold"></span></p><p><b></b></p><p>Lucene面向全文检索的优化在于首次索引检索后，并不把所有的记录（Document）具体内容读取出来，而起只将所有结果中匹配度最高的头100条结果（TopDocs）的ID放到结果集缓存中并返回，这里可以比较一下数据库检索：如果是一个10,000条的数据库检索结果集，数据库是一定要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多，Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是用不到这么多的结果的，头100条已经可以满足90%以上的检索需求。<br /></p><p>如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存，并再重新向后抓取。所以如果构造一个Searcher去查1－120条结果，Searcher其实是进行了2次搜索过程：头100条取完后，缓存结果用完，Searcher重新检索再构造一个200条的结果缓存，依此类推，400条缓存，800条缓存。由于每次Searcher对象消失后，这些缓存也访问那不到了，你有可能想将结果记录缓存下来，缓存数尽量保证在100以下以充分利用首次的结果缓存，不让Lucene浪费多次检索，而且可以分级进行结果缓存。<br /></p><p>Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。</p><p><a href="http://sourceforge.net/projects/weblucene/">我的一些尝试</a>：</p><ul><li>支持中文的Tokenizer：这里有2个版本，一个是通过JavaCC生成的，对CJK部分按一个字符一个TOKEN索引，另外一个是从SimpleTokenizer改写的，对英文支持数字和字母TOKEN，对中文按迭代索引。 
</li><li>基于XML数据源的索引器：XMLIndexer，因此所有数据源只要能够按照DTD转换成指定的XML，就可以用XMLIndxer进行索引了。 
</li><li>根据某个字段排序：按记录索引顺序排序结果的搜索器：IndexOrderSearcher，因此如果需要让搜索结果根据某个字段排序，可以让数据源先按某个字段排好序（比如：PriceField），这样索引后，然后在利用这个按记录的ID顺序检索的搜索器，结果就是相当于是那个字段排序的结果了。 </li></ul><p><a name="learn"><b>从Lucene学到更多</b></a></p><p>Luene的确是一个面对对象设计的典范</p><ul><li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要； 
</li><li>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务； 
</li><li>所有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修改其他模块。  
</li><li>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。 </li></ul><p>这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。</p><p>此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</p><ul><li>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。 
</li><li>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的， 
</li><li>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。 
</li><li>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的操作。<br /></li></ul><p>参考资料：</p><p>Apache: Lucene Project<br /><a href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/<br /></a>Lucene开发/用户邮件列表归档<br /><a href="http://www.mail-archive.com/lucene-dev@jakarta.apache.org/">Lucene-dev@jakarta.apache.org</a><br /><a href="http://www.mail-archive.com/lucene-user@jakarta.apache.org/">Lucene-user@jakarta.apache.org</a></p><p>The Lucene search engine: Powerful, flexible, and free<br /><a href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html">http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html</a></p><p>Lucene Tutorial<br /><a href="http://www.darksleep.com/puff/lucene/lucene.html">http://www.darksleep.com/puff/lucene/lucene.html</a></p><p>Notes on distributed searching with Lucene<br /><a href="http://home.clara.net/markharwood/lucene/">http://home.clara.net/markharwood/lucene/</a></p><p>中文语言的切分词<br /><a href="http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment">http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment</a></p><p>搜索引擎工具介绍<a href="http://searchtools.com/"><br />http://searchtools.com/</a></p><p>Lucene作者Cutting的几篇论文和专利<br /><a href="http://lucene.sourceforge.net/publications.html">http://lucene.sourceforge.net/publications.html</a> </p><p>Lucene的.NET实现：dotLucene<br /><a href="http://sourceforge.net/projects/dotlucene/">http://sourceforge.net/projects/dotlucene/<br /></a></p><p>Lucene作者Cutting的另外一个项目：基于Java的搜索引擎Nutch<br /><a href="http://www.nutch.org/">http://www.nutch.org/</a>   <a href="http://sourceforge.net/projects/nutch/">http://sourceforge.net/projects/nutch/<br /></a></p><p>关于基于词表和N-Gram的切分词比较<br /><a href="http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html">http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html</a><br /><br />2005-01-08 <a href="http://lucene.sourceforge.net/talks/pisa/">Cutting在Pisa大学做的关于Lucene的讲座：非常详细的Lucene架构解说</a></p><img src ="http://www.blogjava.net/kingpub/aggbug/63614.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-08-15 10:05 <a href="http://www.blogjava.net/kingpub/articles/63614.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lucene基本使用介绍 </title><link>http://www.blogjava.net/kingpub/articles/63613.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Tue, 15 Aug 2006 02:03:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/63613.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/63613.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/63613.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/63613.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/63613.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 今天用了下Lucene，发现网上虽然也有不少介绍它的文档，不过很多都偏向介绍概念呀、设计或者是一些更为深入的东西，对于其入门使用的介绍性的文档并不多，就写了这么一篇。																		Lucene																				基本使用介绍																																											...&nbsp;&nbsp;<a href='http://www.blogjava.net/kingpub/articles/63613.html'>阅读全文</a><img src ="http://www.blogjava.net/kingpub/aggbug/63613.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-08-15 10:03 <a href="http://www.blogjava.net/kingpub/articles/63613.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>lucene简单例子</title><link>http://www.blogjava.net/kingpub/articles/59681.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Sun, 23 Jul 2006 11:01:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/59681.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/59681.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/59681.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/59681.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/59681.html</trackback:ping><description><![CDATA[
		<p>lucene的组成结构：对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口</p>
		<p>
		</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td width="27%">org.apache.Lucene.search/</td>
								<td width="73%">搜索入口</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.index/</td>
								<td width="73%">索引入口</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.analysis/</td>
								<td width="73%">语言分析器</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.queryParser/</td>
								<td width="73%">查询分析器</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.document/</td>
								<td width="73%">存储结构</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.store/ </td>
								<td width="73%">底层IO/存储结构</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.util/</td>
								<td width="73%">一些公用的数据结构</td>
						</tr>
				</tbody>
		</table>
		<p>简单的例子演示一下Lucene的使用方法：</p>
		<p>索引过程：从命令行读取文件名（多个），将文件分路径(path字段)和内容(body字段)2个字段进行存储，并对内容进行全文索引：索引的单位是Document对象，每个Document对象包含多个字段Field对象，针对不同的字段属性和数据输出的需求，对字段还可以选择不同的索引/存储字段规则，列表如下： </p>
		<table border="1">
				<tbody>
						<tr>
								<th>方法</th>
								<th>切词</th>
								<th>索引</th>
								<th>存储</th>
								<th>用途</th>
						</tr>
						<tr>
								<td>Field.Text(String name, String value)</td>
								<td>Yes</td>
								<td>Yes</td>
								<td>Yes</td>
								<td valign="top">切分词索引并存储，比如：标题，内容字段</td>
						</tr>
						<tr>
								<td>Field.Text(String name, Reader value)</td>
								<td>Yes</td>
								<td>Yes</td>
								<td>No</td>
								<td valign="top">切分词索引不存储，比如：META信息，<br />不用于返回显示，但需要进行检索内容</td>
						</tr>
						<tr>
								<td>Field.Keyword(String name, String value)</td>
								<td>No</td>
								<td>Yes</td>
								<td>Yes</td>
								<td valign="top">不切分索引并存储，比如：日期字段</td>
						</tr>
						<tr>
								<td>Field.UnIndexed(String name, String value)</td>
								<td>No</td>
								<td>No</td>
								<td>Yes</td>
								<td valign="top">不索引，只存储，比如：文件路径</td>
						</tr>
						<tr>
								<td>Field.UnStored(String name, String value)</td>
								<td>Yes</td>
								<td>Yes</td>
								<td>No</td>
								<td valign="top">只全文索引，不存储</td>
						</tr>
				</tbody>
		</table>
		<pre>public class IndexFiles { <br />  //使用方法：: IndexFiles [索引输出目录] [索引的文件列表] ... <br />  public static void main(String[] args) throws Exception {<br />    String indexPath = args[0];<br />    IndexWriter writer;<br />    //用指定的语言分析器构造一个新的写索引器（第3个参数表示是否为追加索引）<br />    writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<br /><br />    for (int i=1; i      System.out.println("Indexing file " + args[i]);<br />      InputStream is = new FileInputStream(args[i]);<br /><br />      //构造包含2个字段Field的Document对象<br />      //一个是路径path字段，不索引，只存储<br />      //一个是内容body字段，进行全文索引，并存储<br />      Document doc = new Document();<br />      doc.add(Field.UnIndexed("path", args[i]));<br />      doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<br />      //将文档写入索引<br />      writer.addDocument(doc);<br />      is.close();<br />    };<br />    //关闭写索引器<br />    writer.close();<br />  }<br />}<br />具体的子类只需要提供一个返回getName()方法的实现，当在Spring上下文中定义MessageProcessor接口时，该方法返回接口特定实现的bean名字。子类也可以提供自己的MessageConverter，使用不同的策略填充MessageData。以下是MessageProcessor的一个简单实现：<pre class="code">public class SimpleMessageProcessor implements MessageProcessor {  private Log log = LogFactory.getLog(getClass());  public Object process( MessageData messageData) {    log.info(messageData);    return null;  }}</pre><p>　　最后，具体的MDB看起来如下所示。注意，我们使用Xdoclet注释来声明部署描述符的元数据：</p><pre class="code">/** * SimpleMdb *  * .bean  *   name="org.javatx.mdb.SimpleMdb"  *   type="MDB" *   destination-type="javax.jms.Queue"  *   transaction-type="Bean" *  * .pool  *   initial-beans-in-free-pool= *                ""  *   max-beans-in-free-pool= *                "" *  * .message-driven  *   connection-factory-jndi-name= *                  "-e" *   destination-jndi-name= *                  "-e" *   jms-polling-interval-seconds= *                  "" *    * .env-entry *   name="BeanFactoryPath"  *   value="applicationContext.xml" */public class SimpleMdb extends MessageDataDrivenBean {  protected String getName() {    return "simpleProcessor";  }  }</pre><p>　　在上述代码中，BeanFactoryPath的env-entry被Spring的EJB类用来定位应用程序上下文。应用程序上下文中应该有simpleProcessor bean的声明，这个bean会处理所有的处理逻辑，以及非功能性的需求，比如：事务、防止消息的重复处理以及可选的跟踪和性能监控。</p><p>　　显然，将所有非功能方面移到通知中，并利用包装了MessageProcessor实际实现的ProxyFactoryBean来定义拦截器链是很有意义的。定义可能如下所示：</p><pre class="code"><bean class="org.springframework.aop.framework.ProxyFactoryBean" id="simpleProcessor"></bean><property name="target"><bean class="SimpleMessageProcessor"></bean></property><property name="proxyInterfaces" value="org.javatx.mdb.MessageProcessor"></property><property name="interceptorNames"><list></list><idref local="mdbTransactionAdvisor"></idref><idref local="mdbDuplicateHandlingAdvisor"></idref><!-- optional monitoring and tracing --><idref local="messageProcessorPerformanceMonitorAdvisor"></idref><idref local="messageProcessorTraceAdvisor"></idref></property></pre><p>　　图1中的顺序图说明了消息处理过程以及支持该服务质量模型所需的advisor堆栈：</p><p align="center"><a href="http://java.chinaitlab.com/UploadFiles_8734/200603/20060331105109617.gif" target="_blank"></a></p><p align="center"><br />图 1.处理传入消息的advisor堆栈(单击图像查看大图)</p><h2><font size="2">　　实现消息拦截器</font></h2><p>　　现在我们来仔细看一下mdbTransactionInterceptor和mdbDuplicateHandlingAdvisor，它们使用上述方法提供了保证服务质量所需的功能。</p><p>　　mdbTransactionAdvisor是利用标准的Spring TransactionInterceptor以及process()方法的PROPAGATION_REQUIRES_NEW事务属性定义的。</p><pre class="code"><bean class="org.springframework.transaction.interceptor.TransactionInterceptor" id="mdbTransactionAdvisor"></bean><property name="transactionManager" ref="transactionManager"></property><property name="transactionAttributes"><props></props><!-- PROPAGATION_NAME,ISOLATION_NAME,readOnly,                        timeout_NNNN,+Exception1,-Exception2 --><prop key="process"></prop>PROPAGATION_REQUIRES_NEW,timeout_300      </property></pre><p>　　在WebLogic Server中，可以将Spring包装器用作服务器JNDI中javax.transaction.UserTransaction所公开的平台事务管理器，并定义应用程序上下文如下：</p><pre class="code"><bean class="org.springframework.transaction.jta.WebLogicJtaTransactionManager" id="transactionManager"></bean><property name="userTransactionName" value="javax.transaction.UserTransaction"></property><property name="autodetectTransactionManager" value="false"></property></pre><p>　　链中的下一个通知是mdbDuplicateHandlingAdvisor。因为它还要将一些独有键保存到数据库表中，所以需要一个数据源：</p><p><strong>更通用的输入输出接口</strong></p><p>虽然lucene没有定义一个确定的输入文档格式，但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口，然后其他数据，比如PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主，类似实现已经不下4，5个：</p><pre>数据源: WORD       PDF     HTML    DB       other<br />         \          |       |      |         /<br />                       XML中间格式<br />                            |<br />                     Lucene INDEX</pre><p>目前还没有针对MSWord文档的解析器，因为Word文档和基于ASCII的RTF文档不同，需要使用COM对象机制解析。这个是我在Google上查的相关资料：<a href="http://www.intrinsyc.com/products/enterprise_applications.asp">http://www.intrinsyc.com/products/enterprise_applications.asp</a><br />另外一个办法就是把Word文档转换成text：<a href="http://www.winfield.demon.nl/index.html">http://www.winfield.demon.nl/index.html</a><br /></p><p><br /><strong>索引过程优化</strong></p><p>索引一般分2种情况，一种是小批量的索引扩展，一种是大批量的索引重建。在索引过程中，并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作（文件I/O是一件非常消耗资源的事情）。</p><p>Lucene先在内存中进行索引操作，并根据一定的批量进行文件的写入。这个批次的间隔越大，文件的写入次数越少，但占用内存会很多。反之占用内存少，但文件IO操作频繁，索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件的操作。根据我的使用经验：缺省Indexer是每20条记录索引后写入一次，每将MERGE_FACTOR增加50倍，索引速度可以提高1倍左右。<br /></p><p><span style="FONT-WEIGHT: bold">搜索过程优化<br /></span></p><p><span style="FONT-WEIGHT: bold"></span>lucene支持内存索引：这样的搜索比基于文件的I/O有数量级的速度提升。<br /><a href="http://www.onjava.com/lpt/a/3273">http://www.onjava.com/lpt/a/3273</a><br />而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。<br /><span style="FONT-WEIGHT: bold"></span></p><p><strong></strong></p><p>Lucene面向全文检索的优化在于首次索引检索后，并不把所有的记录（Document）具体内容读取出来，而起只将所有结果中匹配度最高的头100条结果（TopDocs）的ID放到结果集缓存中并返回，这里可以比较一下数据库检索：如果是一个10,000条的数据库检索结果集，数据库是一定要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多，Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是用不到这么多的结果的，头100条已经可以满足90%以上的检索需求。<br /></p><p>如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存，并再重新向后抓取。所以如果构造一个Searcher去查1－120条结果，Searcher其实是进行了2次搜索过程：头100条取完后，缓存结果用完，Searcher重新检索再构造一个200条的结果缓存，依此类推，400条缓存，800条缓存。由于每次Searcher对象消失后，这些缓存也访问那不到了，你有可能想将结果记录缓存下来，缓存数尽量保证在100以下以充分利用首次的结果缓存，不让Lucene浪费多次检索，而且可以分级进行结果缓存。<br /></p><p>Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。</p><p><a href="http://sourceforge.net/projects/weblucene/">我的一些尝试</a>：</p><ul><li>支持中文的Tokenizer：这里有2个版本，一个是通过JavaCC生成的，对CJK部分按一个字符一个TOKEN索引，另外一个是从SimpleTokenizer改写的，对英文支持数字和字母TOKEN，对中文按迭代索引。
    
    </li><li>基于XML数据源的索引器：XMLIndexer，因此所有数据源只要能够按照DTD转换成指定的XML，就可以用XMLIndxer进行索引了。
    
    </li><li>根据某个字段排序：按记录索引顺序排序结果的搜索器：IndexOrderSearcher，因此如果需要让搜索结果根据某个字段排序，可以让数据源先按某个字段排好序（比如：PriceField），这样索引后，然后在利用这个按记录的ID顺序检索的搜索器，结果就是相当于是那个字段排序的结果了。
    
</li></ul><p><a name="learn"><strong>从Lucene学到更多</strong></a></p><p>Luene的确是一个面对对象设计的典范</p><ul><li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要；
    
    </li><li>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务；
    
    </li><li>所有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修改其他模块。 
    
    </li><li>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。
    
</li></ul><p>这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。</p><p>此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</p><ul><li>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。
    
    </li><li>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的，
    
    </li><li>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。
    
    </li><li>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的</li></ul></pre>
<img src ="http://www.blogjava.net/kingpub/aggbug/59681.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-07-23 19:01 <a href="http://www.blogjava.net/kingpub/articles/59681.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入 Lucene 索引机制</title><link>http://www.blogjava.net/kingpub/articles/59678.html</link><dc:creator>xiaofeng</dc:creator><author>xiaofeng</author><pubDate>Sun, 23 Jul 2006 10:32:00 GMT</pubDate><guid>http://www.blogjava.net/kingpub/articles/59678.html</guid><wfw:comment>http://www.blogjava.net/kingpub/comments/59678.html</wfw:comment><comments>http://www.blogjava.net/kingpub/articles/59678.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kingpub/comments/commentRss/59678.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kingpub/services/trackbacks/59678.html</trackback:ping><description><![CDATA[
		<p>
				<a name="N10047">
						<span class="atitle">
								<font size="4">架构概览</font>
						</span>
				</a>
		</p>
		<p>图一显示了 Lucene 的索引机制的架构。Lucene 使用各种解析器对各种不同类型的文档进行解析。比如对于 HTML 文档，HTML 解析器会做一些预处理的工作，比如过滤文档中的 HTML 标签等等。HTML 解析器的输出的是文本内容，接着 Lucene 的分词器(Analyzer)从文本内容中提取出索引项以及相关信息，比如索引项的出现频率。接着 Lucene 的分词器把这些信息写到索引文件中。</p>
		<p>
				<br />
				<a name="N10052">
						<strong>图一：Lucene 索引机制架构</strong>
				</a>
				<br />
				<img height="345" alt="图一：Lucene 索引机制架构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/image002.jpg" width="438" border="0" />
		</p>
		<p>
				<a name="N10062">
						<span class="atitle">
								<font size="4">用Lucene索引文档</font>
						</span>
				</a>
		</p>
		<p>接下来我将一步一步的来演示如何利用 Lucene 为你的文档创建索引。只要你能将要索引的文件转化成文本格式，Lucene 就能为你的文档建立索引。比如，如果你想为 HTML 文档或者 PDF 文档建立索引，那么首先你就需要从这些文档中提取出文本信息，然后把文本信息交给 Lucene 建立索引。我们接下来的例子用来演示如何利用 Lucene 为后缀名为 txt 的文件建立索引。</p>
		<p>1． 准备文本文件</p>
		<p>首先把一些以 txt 为后缀名的文本文件放到一个目录中，比如在 Windows 平台上，你可以放到 C:\\files_to_index 下面。</p>
		<p>2． 创建索引</p>
		<p>清单1是为我们所准备的文档创建索引的代码。</p>
		<p>
				<br />
				<a name="N10077">
						<strong>清单1：用 Lucene 索引你的文档</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">package lucene.index;

import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.Date;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;

/**
 * This class demonstrates the process of creating an index with Lucene 
 * for text files in a directory.
 */
public class TextFileIndexer {
 public static void main(String[] args) throws Exception{
   //fileDir is the directory that contains the text files to be indexed
   File   fileDir  = new File("C:\\files_to_index ");

   //indexDir is the directory that hosts Lucene's index files
   File   indexDir = new File("C:\\luceneIndex");
   Analyzer luceneAnalyzer = new StandardAnalyzer();
   IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
   File[] textFiles  = fileDir.listFiles();
   long startTime = new Date().getTime();

   //Add documents to the index
   for(int i = 0; i &lt; textFiles.length; i++){
     if(textFiles[i].isFile() &gt;&gt; textFiles[i].getName().endsWith(".txt")){
       System.out.println("File " + textFiles[i].getCanonicalPath() 
              + " is being indexed");
       Reader textReader = new FileReader(textFiles[i]);
       Document document = new Document();
       document.add(Field.Text("content",textReader));
       document.add(Field.Text("path",textFiles[i].getPath()));
       indexWriter.addDocument(document);
     }
   }

   indexWriter.optimize();
   indexWriter.close();
   long endTime = new Date().getTime();

   System.out.println("It took " + (endTime - startTime) 
              + " milliseconds to create an index for the files in the directory "
              + fileDir.getPath());
  }
}
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>正如清单1所示，你可以利用 Lucene 非常方便的为文档创建索引。接下来我们分析一下清单1中的比较关键的代码，我们先从下面的一条语句开始看起。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">Analyzer luceneAnalyzer = new StandardAnalyzer();
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这条语句创建了类 StandardAnalyzer 的一个实例，这个类是用来从文本中提取出索引项的。它只是抽象类 Analyzer 的其中一个实现。Analyzer 也有一些其它的子类，比如 SimpleAnalyzer 等。</p>
		<p>我们接着看另外一条语句：</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这条语句创建了类 IndexWriter 的一个实例，该类也是 Lucene 索引机制里面的一个关键类。这个类能创建一个新的索引或者打开一个已存在的索引并为该所引添加文档。我们注意到该类的构造函数接受三个参数，第一个参数指定了存储索引文件的路径。第二个参数指定了在索引过程中使用什么样的分词器。最后一个参数是个布尔变量，如果值为真，那么就表示要创建一个新的索引，如果值为假，就表示打开一个已经存在的索引。</p>
		<p>接下来的代码演示了如何添加一个文档到索引文件中。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">Document document = new Document();
document.add(Field.Text("content",textReader));
document.add(Field.Text("path",textFiles[i].getPath()));
indexWriter.addDocument(document);
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>首先第一行创建了类 Document 的一个实例，它由一个或者多个的域(Field)组成。你可以把这个类想象成代表了一个实际的文档，比如一个 HTML 页面，一个 PDF 文档，或者一个文本文件。而类 Document 中的域一般就是实际文档的一些属性。比如对于一个 HTML 页面，它的域可能包括标题，内容，URL 等。我们可以用不同类型的 Field 来控制文档的哪些内容应该索引，哪些内容应该存储。如果想获取更多的关于 Lucene 的域的信息，可以参考 Lucene 的帮助文档。代码的第二行和第三行为文档添加了两个域，每个域包含两个属性，分别是域的名字和域的内容。在我们的例子中两个域的名字分别是"content"和"path"。分别存储了我们需要索引的文本文件的内容和路径。最后一行把准备好的文档添加到了索引当中。</p>
		<p>当我们把文档添加到索引中后，不要忘记关闭索引，这样才保证 Lucene 把添加的文档写回到硬盘上。下面的一句代码演示了如何关闭索引。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">indexWriter.close();
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>利用清单1中的代码，你就可以成功的将文本文档添加到索引中去。接下来我们看看对索引进行的另外一种重要的操作，从索引中删除文档。</p>
		<p>
				<a name="N100BD">
						<span class="atitle">
								<font size="4">从索引中删除文档</font>
						</span>
				</a>
		</p>
		<p>类IndexReader负责从一个已经存在的索引中删除文档，如清单2所示。</p>
		<p>
				<br />
				<a name="N100C6">
						<strong>清单2：从索引中删除文档</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">File   indexDir = new File("C:\\luceneIndex");
IndexReader ir = IndexReader.open(indexDir);
ir.delete(1);
ir.delete(new Term("path","C:\\file_to_index\lucene.txt"));
ir.close();
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在清单2中，第二行用静态方法 IndexReader.open(indexDir) 初始化了类 IndexReader 的一个实例，这个方法的参数指定了索引的存储路径。类 IndexReader 提供了两种方法去删除一个文档，如程序中的第三行和第四行所示。第三行利用文档的编号来删除文档。每个文档都有一个系统自动生成的编号。第四行删除了路径为"C:\\file_to_index\lucene.txt"的文档。你可以通过指定文件路径来方便的删除一个文档。值得注意的是虽然利用上述代码删除文档使得该文档不能被检索到，但是并没有物理上删除该文档。Lucene 只是通过一个后缀名为 .delete 的文件来标记哪些文档已经被删除。既然没有物理上删除，我们可以方便的把这些标记为删除的文档恢复过来，如清单 3 所示，首先打开一个索引，然后调用方法 ir.undeleteAll() 来完成恢复工作。</p>
		<p>
				<br />
				<a name="N100D3">
						<strong>清单3：恢复已删除文档</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">File   indexDir = new File("C:\\luceneIndex");
IndexReader ir = IndexReader.open(indexDir);
ir.undeleteAll();
ir.close();
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>你现在也许想知道如何物理上删除索引中的文档，方法也非常简单。清单 4 演示了这个过程。</p>
		<p>
				<br />
				<a name="N100E0">
						<strong>清单4：如何物理上删除文档</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">File   indexDir = new File("C:\\luceneIndex");
Analyzer luceneAnalyzer = new StandardAnalyzer();
IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,false);
indexWriter.optimize();
indexWriter.close();
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>在清单 4 中，第三行创建了类 IndexWriter 的一个实例，并且打开了一个已经存在的索引。第 4 行对索引进行清理，清理过程中将把所有标记为删除的文档物理删除。</p>
		<p>Lucene 没有直接提供方法对文档进行更新，如果你需要更新一个文档，那么你首先需要把这个文档从索引中删除，然后把新版本的文档加入到索引中去。</p>
		<p>
				<a name="N100F0">
						<span class="atitle">
								<font size="4">提高索引性能</font>
						</span>
				</a>
		</p>
		<p>利用 Lucene，在创建索引的工程中你可以充分利用机器的硬件资源来提高索引的效率。当你需要索引大量的文件时，你会注意到索引过程的瓶颈是在往磁盘上写索引文件的过程中。为了解决这个问题, Lucene 在内存中持有一块缓冲区。但我们如何控制 Lucene 的缓冲区呢？幸运的是，Lucene 的类 IndexWriter 提供了三个参数用来调整缓冲区的大小以及往磁盘上写索引文件的频率。</p>
		<p>1．合并因子（mergeFactor）</p>
		<p>这个参数决定了在 Lucene 的一个索引块中可以存放多少文档以及把磁盘上的索引块合并成一个大的索引块的频率。比如，如果合并因子的值是 10，那么当内存中的文档数达到 10 的时候所有的文档都必须写到磁盘上的一个新的索引块中。并且，如果磁盘上的索引块的隔数达到 10 的话，这 10 个索引块会被合并成一个新的索引块。这个参数的默认值是 10，如果需要索引的文档数非常多的话这个值将是非常不合适的。对批处理的索引来讲，为这个参数赋一个比较大的值会得到比较好的索引效果。</p>
		<p>2．最小合并文档数</p>
		<p>这个参数也会影响索引的性能。它决定了内存中的文档数至少达到多少才能将它们写回磁盘。这个参数的默认值是10，如果你有足够的内存，那么将这个值尽量设的比较大一些将会显著的提高索引性能。</p>
		<p>3．最大合并文档数</p>
		<p>这个参数决定了一个索引块中的最大的文档数。它的默认值是 Integer.MAX_VALUE，将这个参数设置为比较大的值可以提高索引效率和检索速度，由于该参数的默认值是整型的最大值，所以我们一般不需要改动这个参数。</p>
		<p>清单 5 列出了这个三个参数用法，清单 5 和清单 1 非常相似，除了清单 5 中会设置刚才提到的三个参数。</p>
		<p>
				<br />
				<a name="N1010E">
						<strong>清单5：提高索引性能</strong>
				</a>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">/**
 * This class demonstrates how to improve the indexing performance 
 * by adjusting the parameters provided by IndexWriter.
 */
public class AdvancedTextFileIndexer  {
  public static void main(String[] args) throws Exception{
    //fileDir is the directory that contains the text files to be indexed
    File   fileDir  = new File("C:\\files_to_index");

    //indexDir is the directory that hosts Lucene's index files
    File   indexDir = new File("C:\\luceneIndex");
    Analyzer luceneAnalyzer = new StandardAnalyzer();
    File[] textFiles  = fileDir.listFiles();
    long startTime = new Date().getTime();

    int mergeFactor = 10;
    int minMergeDocs = 10;
    int maxMergeDocs = Integer.MAX_VALUE;
    IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);        
    indexWriter.mergeFactor = mergeFactor;
    indexWriter.minMergeDocs = minMergeDocs;
    indexWriter.maxMergeDocs = maxMergeDocs;

    //Add documents to the index
    for(int i = 0; i &lt; textFiles.length; i++){
      if(textFiles[i].isFile() &gt;&gt; textFiles[i].getName().endsWith(".txt")){
        Reader textReader = new FileReader(textFiles[i]);
        Document document = new Document();
        document.add(Field.Text("content",textReader));
        document.add(Field.Keyword("path",textFiles[i].getPath()));
        indexWriter.addDocument(document);
      }
    }

    indexWriter.optimize();
    indexWriter.close();
    long endTime = new Date().getTime();

    System.out.println("MergeFactor: " + indexWriter.mergeFactor);
    System.out.println("MinMergeDocs: " + indexWriter.minMergeDocs);
    System.out.println("MaxMergeDocs: " + indexWriter.maxMergeDocs);
    System.out.println("Document number: " + textFiles.length);
    System.out.println("Time consumed: " + (endTime - startTime) + " milliseconds");
  }
}
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>通过这个例子，我们注意到在调整缓冲区的大小以及写磁盘的频率上面 Lucene 给我们提供了非常大的灵活性。现在我们来看一下代码中的关键语句。如下的代码首先创建了类 IndexWriter 的一个实例，然后对它的三个参数进行赋值。</p>
		<p>
				<br />
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">int mergeFactor = 10;
int minMergeDocs = 10;
int maxMergeDocs = Integer.MAX_VALUE;
IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);        
indexWriter.mergeFactor = mergeFactor;
indexWriter.minMergeDocs = minMergeDocs;
indexWriter.maxMergeDocs = maxMergeDocs;
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>下面我们来看一下这三个参数取不同的值对索引时间的影响，注意参数值的不同和索引之间的关系。我们为这个实验准备了 10000 个测试文档。表 1 显示了测试结果。</p>
		<p>
				<br />
				<a name="N10129">
						<strong>表1：测试结果</strong>
				</a>
				<br />
				<img height="199" alt="表1：测试结果" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table1.gif" width="502" border="0" />
				<br />
		</p>
		<p>通过表 1，你可以清楚地看到三个参数对索引时间的影响。在实践中，你会经常的改变合并因子和最小合并文档数的值来提高索引性能。只要你有足够大的内存，你可以为合并因子和最小合并文档数这两个参数赋尽量大的值以提高索引效率，另外我们一般无需更改最大合并文档数这个参数的值，因为系统已经默认将它设置成了最大。</p>
		<p>
				<a name="N1013C">
						<span class="atitle">
								<font size="4">Lucene 索引文件结构分析</font>
						</span>
				</a>
		</p>
		<p>在分析 Lucene 的索引文件结构之前，我们先要理解反向索引（Inverted index）这个概念，反向索引是一种以索引项为中心来组织文档的方式，每个索引项指向一个文档序列，这个序列中的文档都包含该索引项。相反，在正向索引中，文档占据了中心的位置，每个文档指向了一个它所包含的索引项的序列。你可以利用反向索引轻松的找到那些文档包含了特定的索引项。Lucene正是使用了反向索引作为其基本的索引结构。</p>
		<p>
				<a name="N10145">
						<span class="atitle">
								<font size="4">索引文件的逻辑视图</font>
						</span>
				</a>
		</p>
		<p>在Lucene 中有索引块的概念，每个索引块包含了一定数目的文档。我们能够对单独的索引块进行检索。图 2 显示了 Lucene 索引结构的逻辑视图。索引块的个数由索引的文档的总数以及每个索引块所能包含的最大文档数来决定。</p>
		<p>
				<br />
				<a name="N10150">
						<strong>图2：索引文件的逻辑视图</strong>
				</a>
				<br />
				<img height="415" alt="图2：索引文件的逻辑视图" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/image004.jpg" width="467" border="0" /> </p>
		<p>
				<a name="N10160">
						<span class="atitle">
								<font size="4">Lucene 中的关键索引文件</font>
						</span>
				</a>
		</p>
		<p>下面的部分将会分析Lucene中的主要的索引文件，可能分析有些索引文件的时候没有包含文件的所有的字段，但不会影响到对索引文件的理解。</p>
		<p>1．索引块文件</p>
		<p>这个文件包含了索引中的索引块信息，这个文件包含了每个索引块的名字以及大小等信息。表 2 显示了这个文件的结构信息。</p>
		<p>
				<br />
				<a name="N10171">
						<strong>表2：索引块文件结构</strong>
				</a>
				<br />
				<img height="198" alt="表2：索引块文件结构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table2.gif" width="568" border="0" />
				<br />
		</p>
		<p>2．域信息文件</p>
		<p>我们知道，索引中的文档由一个或者多个域组成，这个文件包含了每个索引块中的域的信息。表 3 显示了这个文件的结构。</p>
		<p>
				<br />
				<a name="N10189">
						<strong>表3：域信息文件结构</strong>
				</a>
				<br />
				<img height="179" alt="表3：域信息文件结构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table3.gif" width="567" border="0" />
				<br />
		</p>
		<p>3．索引项信息文件</p>
		<p>这是索引文件里面最核心的一个文件，它存储了所有的索引项的值以及相关信息，并且以索引项来排序。表 4 显示了这个文件的结构。</p>
		<p>
				<br />
				<a name="N101A1">
						<strong>表4：索引项信息文件结构</strong>
				</a>
				<br />
				<img height="252" alt="表4：索引项信息文件结构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table4.gif" width="568" border="0" />
				<br />
		</p>
		<p>4．频率文件</p>
		<p>这个文件包含了包含索引项的文档的列表，以及索引项在每个文档中出现的频率信息。如果Lucene在索引项信息文件中发现有索引项和搜索词相匹配。那么 Lucene 就会在频率文件中找有哪些文件包含了该索引项。表5显示了这个文件的一个大致的结构，并没有包含这个文件的所有字段。</p>
		<p>
				<br />
				<a name="N101B9">
						<strong>表5：频率文件的结构</strong>
				</a>
				<br />
				<img height="124" alt="表5：频率文件的结构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table5.gif" width="567" border="0" />
				<br />
		</p>
		<p>5．位置文件</p>
		<p>这个文件包含了索引项在每个文档中出现的位置信息，你可以利用这些信息来参与对索引结果的排序。表 6 显示了这个文件的结构</p>
		<p>
				<br />
				<a name="N101D1">
						<strong>表6：位置文件的结构</strong>
				</a>
				<br />
				<img height="69" alt="表6：位置文件的结构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table6.gif" width="568" border="0" />
				<br />
		</p>
		<p>到目前为止我们介绍了 Lucene 中的主要的索引文件结构，希望能对你理解 Lucene 的物理的存储结构有所帮助。</p>
		<p>
				<a name="N101E4">
						<span class="atitle">
								<font size="4">总结</font>
						</span>
				</a>
		</p>
		<p>目前已经有非常多的知名的组织正在使用 Lucene，比如，Lucene 为 Eclipse 的帮助系统，麻省理工学院的 OpenCourseWare 提供了搜索功能。通过阅读这篇文章，希望你能对 Lucene 的索引机制有所了解，并且你会发现利用 Lucene 创建索引是非常简单的事情。</p>
<img src ="http://www.blogjava.net/kingpub/aggbug/59678.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kingpub/" target="_blank">xiaofeng</a> 2006-07-23 18:32 <a href="http://www.blogjava.net/kingpub/articles/59678.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>