﻿<?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-szhswl-文章分类-搜索引擎</title><link>http://www.blogjava.net/szhswl/category/27889.html</link><description>宋针还的个人空间</description><language>zh-cn</language><lastBuildDate>Thu, 20 Dec 2007 16:42:34 GMT</lastBuildDate><pubDate>Thu, 20 Dec 2007 16:42:34 GMT</pubDate><ttl>60</ttl><item><title>LUCENE学习笔记3(转载)</title><link>http://www.blogjava.net/szhswl/articles/168305.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Mon, 17 Dec 2007 11:36:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/168305.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/168305.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/168305.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/168305.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/168305.html</trackback:ping><description><![CDATA[1, 有时对于一个Document来说，有一些Field会被频繁地操作，而另一些Field则不会。这时可以将频繁操作的Field和其他Field分开存放，而在搜索时同时检索这两部分Field而提取出一个完整的Document。 &nbsp; 这要求两个索引包含的Document的数量必须相同。 <br />
在创建索引的时候，可以同时创建多个IndexWriter，将一个Document根据需要拆分成多个包含部分Field的Document，并将这些Document分别添加到不同的索引。 <br />
而在搜索时，则必须借助ParallelReader类来整合。 <br />
Directory dir1=FSDirectory.getDirectory(new File(INDEX_DIR1),false); <br />
Directory dir2=FSDirectory.getDirectory(new File(INDEX_DIR2),false); <br />
<strong>ParallelReader preader=new ParallelReader(); <br />
preader.add(IndexReader.open(dir1)); <br />
preader.add(IndexReader.open(dir2));</strong> <br />
IndexSearcher searcher=new IndexSearcher(preader); <br />
之后的操作和一般的搜索相同。 <br />
<br />
2, Query的子类. 下面的几个搜索在各种不同要求的场合,都会用到. 需要大家仔细研读! <br />
<br />
Query query1 = new TermQuery(new Term(FieldValue, "name1")); // 词语搜索 <br />
Query query2 = new WildcardQuery(new Term(FieldName, "name*")); // 通配符 <br />
Query query3 = new PrefixQuery(new Term(FieldName, "name1")); // 字段搜索 Field:Keyword，自动在结尾添加 * <br />
Query query4 = new RangeQuery(new Term(FieldNumber, NumberTools.LongToString(11L)), new Term(FieldNumber, NumberTools.LongToString(13L)), true); // 范围搜索 <br />
Query query5 = new FilteredQuery(query, filter); // 带过滤条件的搜索 <br />
Query query6 =new MatchAllDocsQuery(... // 用来匹配所有文档 <br />
Query query7 = new FuzzyQuery (...模糊搜索 <br />
Query query8 = new RegexQuery (.. &nbsp; 正则搜索 <br />
Query query9 = new SpanRegexQuery(...)。 同上, 正则表达式的查询： <br />
Query query9 = new SpanQuery 的子类嵌套其他SpanQuery 增加了 rewrite方法 <br />
Query query10 =new DisjunctionMaxQuery () ..类，提供了针对某个短语的最大score。这一点对多字段的搜索非常有用 <br />
Query query11 = new ConstantScoreQuery 类它包装了一个 filter produces a score <br />
equal to the query boost for every matching document. <br />
<br />
BooleanQuery query12= new BooleanQuery(); <br />
booleanQuery.add(termQuery 1, BooleanClause.Occur.SHOULD); <br />
booleanQuery.add(termQuery 2, BooleanClause.Occur.SHOULD); <br />
&nbsp; //这个是为了联合多个查询而做的Query类. BooleanQuery增加了最小的匹配短语。见：BooleanQuery.setMinimumNumberShouldMatch(). <br />
<br />
<br />
PhraseQuery <br />
你可能对中日关系比较感兴趣，想查找&#8216;中&#8217;和&#8216;日&#8217;挨得比较近（5个字的距离内）的文章，超过这个距离的不予考虑，你可以： <br />
<br />
PhraseQuery query 13= new PhraseQuery(); <br />
query.setSlop(5); <br />
query.add(new Term("content ", &#8220;中&#8221;)); <br />
query.add(new Term(&#8220;content&#8221;, &#8220;日&#8221;)); <br />
<br />
PhraseQuery对于短语的顺序是不管的,这点在查询时除了提高命中率外,也会对性能产生很大的影响, 利用SpanNearQuery可以对短语的顺序进行控制,提高性能<br />
<br />
BooleanQuery query12= &nbsp; new SpanNearQuery 可以对短语的顺序进行控制,提高性能<br />
<br />
3, 索引文本文件 <br />
如果你想把纯文本文件索引起来，而不想自己将它们读入字符串创建field，你可以用下面的代码创建field： <br />
<br />
Field field = new Field("content", new FileReader(file)); <br />
<br />
这里的file就是该文本文件。该构造函数实际上是读去文件内容，并对其进行索引，但不存储 <br />
<br />
<br />
4, 如何删除索引 <br />
lucene提供了两种从索引中删除document的方法，一种是 <br />
<br />
void deleteDocument(int docNum) <br />
<br />
这种方法是根据document在索引中的编号来删除，每个document加进索引后都会有个唯一编号，所以根据编号删除是一种精确删除，但是这个编号是索引的内部结构，一般我们不会知道某个文件的编号到底是几，所以用处不大。另一种是 <br />
<br />
void deleteDocuments(Term term) <br />
<br />
这种方法实际上是首先根据参数term执行一个搜索操作，然后把搜索到的结果批量删除了。我们可以通过这个方法提供一个严格的查询条件，达到删除指定document的目的。 <br />
下面给出一个例子： <br />
<br />
Directory dir = FSDirectory.getDirectory(PATH, false); <br />
IndexReader reader = IndexReader.open(dir); <br />
Term term = new Term(field, key); <br />
reader.deleteDocuments(term); <br />
reader.close(); <br />
<br />
<br />
<br />
5, 如何更新索引 <br />
lucene并没有提供专门的索引更新方法，我们需要先将相应的document删除，然后再将新的document加入索引。例如： <br />
<br />
Directory dir = FSDirectory.getDirectory(PATH, false); <br />
IndexReader reader = IndexReader.open(dir); <br />
Term term = new Term(&#8220;title&#8221;, &#8220;lucene introduction&#8221;); <br />
reader.deleteDocuments(term); <br />
reader.close(); <br />
<br />
IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true); <br />
Document doc = new Document(); <br />
doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED)); <br />
doc.add(new Field("content", "lucene is funny", Field.Store.YES, Field.Index.TOKENIZED)); <br />
writer.addDocument(doc); <br />
writer.optimize(); <br />
writer.close(); <br />
<br />
<br />
但是在1.9RC1中说明: <br />
新增类： org.apache.lucene.index.IndexModifier ，它合并了 &nbsp; IndexWriter 和 IndexReader，好处是我们可以增加和删除文档的时候不同担心 synchronisation/locking 的问题了。 &nbsp; <br />
<br />
6, filer类.使用 Filter 对搜索结果进行过滤，可以获得更小范围内更精确的结果。 有人说: 注意它执行的是预处理，而不是对查询结果进行过滤，所以使用filter的代价是很大的，它可能会使一次查询耗时提高一百倍 <br />
<br />
<br />
ISOLatin1AccentFilter ,用 ISO Latin 1 字符集中的unaccented类字符替代 accented 类字符 <br />
DateFilter &nbsp; 日期过滤器 <br />
RangeFileter ,比 DateFilter 更加通用，实用 <br />
LengthFilter 类, 已经从 contrib 放到了 core 代码里。从 stream 中去掉太长和太短的单词 &nbsp; StopFilter 类, 增加了对处理stop words 的忽略大小写处理 <br />
<br />
<br />
7,本条是一个使用过滤的说明: <br />
<br />
过滤 <br />
<br />
使用 Filter 对搜索结果进行过滤，可以获得更小范围内更精确的结果。 <br />
<br />
举个例子，我们搜索上架时间在 2005-10-1 到 2005-10-30 之间的商品。 <br />
对于日期时间，我们需要转换一下才能添加到索引库，同时还必须是索引字段。 <br />
// index <br />
document.Add(FieldDate, DateField.DateToString(date), Field.Store.YES, Field.Index.UN_TOKENIZED); <br />
<br />
//... <br />
<br />
// search <br />
Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-1"), DateTime.Parse("2005-10-30")); <br />
Hits hits = searcher.Search(query, filter); <br />
<br />
除了日期时间，还可以使用整数。比如搜索价格在 100 ~ 200 之间的商品。 <br />
Lucene.Net NumberTools 对于数字进行了补位处理，如果需要使用浮点数可以自己参考源码进行。 <br />
// index <br />
document.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED)); <br />
<br />
//... <br />
<br />
// search <br />
Filter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true); <br />
Hits hits = searcher.Search(query, filter); <br />
<br />
使用 Query 作为过滤条件。 <br />
QueryFilter filter = new QueryFilter(QueryParser.Parse("name2", FieldValue, analyzer)); <br />
<br />
我们还可以使用 FilteredQuery 进行多条件过滤。 <br />
<br />
Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-10"), DateTime.Parse("2005-10-15")); <br />
Filter filter2 = new RangeFilter(FieldNumber, NumberTools.LongToString(11L), NumberTools.LongToString(13L), true, true); <br />
<br />
Query query = QueryParser.Parse("name*", FieldName, analyzer); <br />
query = new FilteredQuery(query, filter); <br />
query = new FilteredQuery(query, filter2); <br />
<br />
IndexSearcher searcher = new IndexSearcher(reader); <br />
Hits hits = searcher.Search(query); <br />
<br />
<br />
<br />
8, Sort <br />
有时你想要一个排好序的结果集，就像SQL语句的&#8220;order by&#8221;，lucene能做到：通过Sort。 <br />
Sort sort = new Sort(&#8220;time&#8221;); //相当于SQL的&#8220;order by time&#8221; <br />
Sort sort = new Sort(&#8220;time&#8221;, true); // 相当于SQL的&#8220;order by time desc&#8221; <br />
下面是一个完整的例子： <br />
<br />
Directory dir = FSDirectory.getDirectory(PATH, false); <br />
IndexSearcher is = new IndexSearcher(dir); <br />
QueryParser parser = new QueryParser("content", new StandardAnalyzer()); <br />
Query query = parser.parse("title:lucene content:lucene"; <br />
RangeFilter filter = new RangeFilter("time", "20060101", "20060230", true, true); <br />
Sort sort = new Sort(&#8220;time&#8221;); <br />
Hits hits = is.search(query, filter, sort); <br />
for (int i = 0; i &lt; hits.length(); i++) <br />
{ <br />
Document doc = hits.doc(i); <br />
System.out.println(doc.get("title"); <br />
} <br />
is.close(); <br />
<br />
9, &nbsp; 性能优化 <br />
一直到这里，我们还是在讨论怎么样使lucene跑起来，完成指定任务。利用前面说的也确实能完成大部分功能。但是测试表明lucene的性能并不是很好，在大数据量大并发的条件下甚至会有半分钟返回的情况。另外大数据量的数据初始化建立索引也是一个十分耗时的过程。那么如何提高lucene的性能呢？下面从优化创建索引性能和优化搜索性能两方面介绍。 <br />
<br />
9.1 优化创建索引性能 <br />
这方面的优化途径比较有限，IndexWriter提供了一些接口可以控制建立索引的操作，另外我们可以先将索引写入RAMDirectory，再批量写入FSDirectory，不管怎样，目的都是尽量少的文件IO，因为创建索引的最大瓶颈在于磁盘IO。另外选择一个较好的分析器也能提高一些性能。 <br />
<br />
9.1.1 通过设置IndexWriter的参数优化索引建立 <br />
setMaxBufferedDocs(int maxBufferedDocs) <br />
控制写入一个新的segment前内存中保存的document的数目，设置较大的数目可以加快建索引速度，默认为10。 <br />
setMaxMergeDocs(int maxMergeDocs) <br />
控制一个segment中可以保存的最大document数目，值较小有利于追加索引的速度，默认Integer.MAX_VALUE，无需修改。 <br />
setMergeFactor(int mergeFactor) <br />
控制多个segment合并的频率，值较大时建立索引速度较快，默认是10，可以在建立索引时设置为100。 <br />
<br />
9.1.2 通过RAMDirectory缓写提高性能 <br />
我们可以先把索引写入RAMDirectory，达到一定数量时再批量写进FSDirectory，减少磁盘IO次数。 <br />
<br />
FSDirectory fsDir = FSDirectory.getDirectory("/data/index", true); <br />
RAMDirectory ramDir = new RAMDirectory(); <br />
IndexWriter fsWriter = new IndexWriter(fsDir, new StandardAnalyzer(), true); <br />
IndexWriter ramWriter = new IndexWriter(ramDir, new StandardAnalyzer(), true); <br />
while (there are documents to index) <br />
{ <br />
... create Document ... <br />
ramWriter.addDocument(doc); <br />
if (condition for flushing memory to disk has been met) <br />
{ <br />
fsWriter.addIndexes(new Directory[] { ramDir }); <br />
ramWriter.close(); <br />
ramWriter = new IndexWriter(ramDir, new StandardAnalyzer(), true); <br />
} <br />
} <br />
<br />
9.1.3 选择较好的分析器 <br />
这个优化主要是对磁盘空间的优化，可以将索引文件减小将近一半，相同测试数据下由600M减少到380M。但是对时间并没有什么帮助，甚至会需要更长时间，因为较好的分析器需要匹配词库，会消耗更多cpu，测试数据用StandardAnalyzer耗时133分钟；用MMAnalyzer耗时150分钟。 <br />
<br />
9.2 优化搜索性能 <br />
虽然建立索引的操作非常耗时，但是那毕竟只在最初创建时才需要，平时只是少量的维护操作，更何况这些可以放到一个后台进程处理，并不影响用户搜索。我们创建索引的目的就是给用户搜索，所以搜索的性能才是我们最关心的。下面就来探讨一下如何提高搜索性能。 <br />
<br />
9.2.1 将索引放入内存 <br />
这是一个最直观的想法，因为内存比磁盘快很多。Lucene提供了RAMDirectory可以在内存中容纳索引： <br />
<br />
Directory fsDir = FSDirectory.getDirectory(&#8220;/data/index/&#8221;, false); <br />
Directory ramDir = new RAMDirectory(fsDir); <br />
Searcher searcher = new IndexSearcher(ramDir); <br />
<br />
但是实践证明RAMDirectory和FSDirectory速度差不多，当数据量很小时两者都非常快，当数据量较大时（索引文件400M）RAMDirectory甚至比FSDirectory还要慢一点，这确实让人出乎意料。 <br />
而且lucene的搜索非常耗内存，即使将400M的索引文件载入内存，在运行一段时间后都会out of memory，所以个人认为载入内存的作用并不大。 <br />
<br />
9.2.2 优化时间范围限制 <br />
既然载入内存并不能提高效率，一定有其它瓶颈，经过测试发现最大的瓶颈居然是时间范围限制，那么我们可以怎样使时间范围限制的代价最小呢？ <br />
当需要搜索指定时间范围内的结果时，可以： <br />
1、用RangeQuery，设置范围，但是RangeQuery的实现实际上是将时间范围内的时间点展开，组成一个个BooleanClause加入到BooleanQuery中查询， 因此时间范围不可能设置太大，经测试，范围超过一个月就会抛BooleanQuery.TooManyClauses，可以通过设置BooleanQuery.setMaxClauseCount(int maxClauseCount)扩大，但是扩大也是有限的，并且随着maxClauseCount扩大，占用内存也扩大 <br />
2、用RangeFilter代替RangeQuery，经测试速度不会比RangeQuery慢，但是仍然有性能瓶颈，查询的90%以上时间耗费在RangeFilter，研究其源码发现RangeFilter实际上是首先遍历所有索引，生成一个BitSet，标记每个document，在时间范围内的标记为true，不在的标记为false，然后将结果传递给Searcher查找，这是十分耗时的。 <br />
3、进一步提高性能，这个又有两个思路： <br />
a、缓存Filter结果。既然RangeFilter的执行是在搜索之前，那么它的输入都是一定的，就是IndexReader，而IndexReader是由Directory决定的，所以可以认为RangeFilter的结果是由范围的上下限决定的，也就是由具体的RangeFilter对象决定，所以我们只要以RangeFilter对象为键，将filter结果BitSet缓存起来即可。lucene API已经提供了一个CachingWrapperFilter类封装了Filter及其结果，所以具体实施起来我们可以cache CachingWrapperFilter对象，需要注意的是，不要被CachingWrapperFilter的名字及其说明误导，CachingWrapperFilter看起来是有缓存功能，但的缓存是针对同一个filter的，也就是在你用同一个filter过滤不同IndexReader时，它可以帮你缓存不同IndexReader的结果，而我们的需求恰恰相反，我们是用不同filter过滤同一个IndexReader，所以只能把它作为一个封装类。 <br />
b、降低时间精度。研究Filter的工作原理可以看出，它每次工作都是遍历整个索引的，所以时间粒度越大，对比越快，搜索时间越短，在不影响功能的情况下，时间精度越低越好，有时甚至牺牲一点精度也值得，当然最好的情况是根本不作时间限制。 <br />
下面针对上面的两个思路演示一下优化结果（都采用800线程随机关键词随即时间范围）： <br />
第一组，时间精度为秒： <br />
方式 直接用RangeFilter 使用cache 不用filter <br />
平均每个线程耗时 10s 1s 300ms <br />
<br />
第二组，时间精度为天 <br />
方式 直接用RangeFilter 使用cache 不用filter <br />
平均每个线程耗时 900ms 360ms 300ms <br />
<br />
由以上数据可以得出结论： <br />
1、 尽量降低时间精度，将精度由秒换成天带来的性能提高甚至比使用cache还好，最好不使用filter。 <br />
2、 在不能降低时间精度的情况下，使用cache能带了10倍左右的性能提高。 <br />
<br />
9.2.3 使用更好的分析器 <br />
这个跟创建索引优化道理差不多，索引文件小了搜索自然会加快。当然这个提高也是有限的。较好的分析器相对于最差的分析器对性能的提升在20%以下。 <br />
<br />
10 一些经验 <br />
<br />
10.1关键词区分大小写 <br />
<strong>or AND TO等关键词是区分大小写的，lucene只认大写的，小写的当做普通单词。</strong> <br />
<br />
10.2 读写互斥性 <br />
同一时刻只能有一个对索引的写操作，在写的同时可以进行搜索 <br />
<br />
10.3 文件锁 <br />
在写索引的过程中强行退出将在tmp目录留下一个lock文件，使以后的写操作无法进行，可以将其手工删除 <br />
<br />
10.4 时间格式 <br />
<strong>lucene只支持一种时间格式yyMMddHHmmss，所以你传一个yy-MM-dd HH:mm:ss的时间给lucene它是不会当作时间来处理的</strong> <br />
<br />
10.5 设置boost <br />
有些时候在搜索时某个字段的权重需要大一些，例如你可能认为标题中出现关键词的文章比正文中出现关键词的文章更有价值，你可以把标题的boost设置的更大，那么搜索结果会优先显示标题中出现关键词的文章（没有使用排序的前题下）。使用方法： <br />
Field. setBoost(float boost);默认值是1.0，也就是说要增加权重的需要设置得比1大。 <br />
<br />
上面这篇关于性能的讲解是很深刻. 请学习. <br />
<br />
本文转自：http://zhangxinzhou.blog.ccidnet.com/blog-htm-do-showone-uid-36421-type-blog-itemid-213713.html<br />
<img src ="http://www.blogjava.net/szhswl/aggbug/168305.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-17 19:36 <a href="http://www.blogjava.net/szhswl/articles/168305.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Lucene加速Web搜索应用程序的开发</title><link>http://www.blogjava.net/szhswl/articles/168301.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Mon, 17 Dec 2007 11:17:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/168301.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/168301.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/168301.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/168301.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/168301.html</trackback:ping><description><![CDATA[<blockquote>Lucene 是基于 Java 的全文信息检索包，它目前是 Apache Jakarta 家族下面的一个开源项目。在这篇文章中，我们首先来看如何利用Lucene 实现高级搜索功能，然后学习如何利用 Lucene 来创建一个健壮的 Web 搜索应用程序。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>在本篇文章中，你会学习到如何利用 Lucene 实现高级搜索功能以及如何利用 Lucene 来创建 Web 搜索应用程序。通过这些学习，你就可以利用 Lucene 来创建自己的搜索应用程序。</p>
<p><a name="N10064"><span class="atitle">架构概览</span></a></p>
<p>通常一个 Web 搜索引擎的架构分为前端和后端两部分，就像下图中所示。在前端流程中，用户在搜索引擎提供的界面中输入要搜索的关键词，这里提到的用户界面一般是一个带有输入框的 Web 页面，然后应用程序将搜索的关键词解析成搜索引擎可以理解的形式，并在索引文件上进行搜索操作。在排序后，搜索引擎返回搜索结果给用户。在后端流程中，网络爬虫或者机器人从因特网上获取 Web 页面，然后索引子系统解析这些 Web 页面并存入索引文件中。如果你想利用 Lucene 来创建一个 Web 搜索应用程序，那么它的架构也和上面所描述的类似，就如下图中所示。</p>
<br />
<a name="figure1"><strong>Figure 1. Web 搜索引擎架构</strong></a><br />
<img height="380" alt="Web搜索引擎架构" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure1.gif" width="449" /> <br />
<p><a name="N10085"><span class="atitle">利用 Lucene 实现高级搜索</span></a></p>
<p>Lucene 支持多种形式的高级搜索，我们在这一部分中会进行探讨，然后我会使用 Lucene 的 API 来演示如何实现这些高级搜索功能。</p>
<p><a name="N1008E"><span class="smalltitle">布尔操作符</span></a></p>
<p>大多数的搜索引擎都会提供布尔操作符让用户可以组合查询，典型的布尔操作符有 AND, OR, NOT。Lucene 支持 5 种布尔操作符，分别是 AND, OR, NOT, 加(+), 减(-)。接下来我会讲述每个操作符的用法。 </p>
<ul>
    <li><strong>OR</strong>: 如果你要搜索含有字符 A 或者 B 的文档，那么就需要使用 OR 操作符。需要记住的是，如果你只是简单的用空格将两个关键词分割开，其实在搜索的时候搜索引擎会自动在两个关键词之间加上 OR 操作符。例如，&#8220;Java OR Lucene&#8221; 和 &#8220;Java Lucene&#8221; 都是搜索含有 Java 或者含有 Lucene 的文档。
    <li><strong>AND</strong>: 如果你需要搜索包含一个以上关键词的文档，那么就需要使用 AND 操作符。例如，&#8220;Java AND Lucene&#8221; 返回所有既包含 Java 又包含 Lucene 的文档。
    <li><strong>NOT</strong>: Not 操作符使得包含紧跟在 NOT 后面的关键词的文档不会被返回。例如，如果你想搜索所有含有 Java 但不含有 Lucene 的文档，你可以使用查询语句 &#8220;Java NOT Lucene&#8221;。但是你不能只对一个搜索词使用这个操作符，比如，查询语句 &#8220;NOT Java&#8221; 不会返回任何结果。
    <li><strong>加号（+）</strong>: 这个操作符的作用和 AND 差不多，但它只对紧跟着它的一个搜索词起作用。例如，如果你想搜索一定包含 Java，但不一定包含 Lucene 的文档，就可以使用查询语句&#8220;+Java Lucene&#8221;。
    <li><strong>减号（-）</strong>: 这个操作符的功能和 NOT 一样，查询语句 &#8220;Java -Lucene&#8221; 返回所有包含 Java 但不包含 Lucene 的文档。 </li>
</ul>
<p>接下来我们看一下如何利用 Lucene 提供的 API 来实现布尔查询。下面代码 显示了如果利用布尔操作符进行查询的过程。</p>
<br />
<a name="Listing1"><strong>清单1：使用布尔操作符</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">  //Test boolean operator
            public 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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N100C4"><span class="smalltitle">域搜索(Field Search)</span></a></p>
<p>Lucene 支持域搜索，你可以指定一次查询是在哪些域(Field)上进行。例如，如果索引的文档包含两个域，<code>Title</code> 和 <code>Content</code>，你就可以使用查询 &#8220;Title: Lucene AND Content: Java&#8221; 来返回所有在 Title 域上包含 Lucene 并且在 Content 域上包含 Java 的文档。下面代码 显示了如何利用 Lucene 的 API 来实现域搜索。 </p>
<br />
<a name="Listing2"><strong>清单2：实现域搜索</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">//Test field search
            public 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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N100E3"><span class="smalltitle">通配符搜索(Wildcard Search)</span></a></p>
<p>Lucene 支持两种通配符：问号（？）和星号（*）。你可以使用问号（？）来进行单字符的通配符查询，或者利用星号（*）进行多字符的通配符查询。例如，如果你想搜索 tiny 或者 tony，你就可以使用查询语句 &#8220;t?ny&#8221;；如果你想查询 Teach, Teacher 和 Teaching，你就可以使用查询语句 &#8220;Teach*&#8221;。下面代码 显示了通配符查询的过程。 </p>
<br />
<a name="Listing3"><strong>清单3：进行通配符查询</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">//Test wildcard search
            public 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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N100FA"><span class="smalltitle">模糊查询</span></a></p>
<p>Lucene 提供的模糊查询基于编辑距离算法(Edit distance algorithm)。你可以在搜索词的尾部加上字符 ~ 来进行模糊查询。例如，查询语句 &#8220;think~&#8221; 返回所有包含和 think 类似的关键词的文档。下面代码显示了如果利用 Lucene 的 API 进行模糊查询的代码。 </p>
<br />
<a name="Listing4"><strong>清单4：实现模糊查询</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">//Test fuzzy search
            public 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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N10111"><span class="smalltitle">范围搜索(Range Search)</span></a></p>
<p>范围搜索匹配某个域上的值在一定范围的文档。例如，查询 &#8220;age:[18 TO 35]&#8221; 返回所有 age 域上的值在 18 到 35 之间的文档。下面代码显示了利用 Lucene 的 API 进行返回搜索的过程。 </p>
<br />
<a name="Listing5"><strong>清单5：测试范围搜索</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">//Test range search
            public 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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N10128"><span class="atitle">在 Web 应用程序中集成 Lucene</span></a></p>
<p>接下来我们开发一个 Web 应用程序利用 Lucene 来检索存放在文件服务器上的 HTML 文档。在开始之前，需要准备如下环境：</p>
<ol>
    <li>Eclipse 集成开发环境
    <li>Tomcat 5.0
    <li>Lucene Library
    <li>JDK 1.5 </li>
</ol>
<p>这个例子使用 Eclipse 进行 Web 应用程序的开发，最终这个 Web 应用程序跑在 Tomcat 5.0 上面。在准备好开发所必需的环境之后，我们接下来进行 Web 应用程序的开发。 </p>
<p><a name="N10143"><span class="smalltitle">1、创建一个动态 Web 项目</span></a></p>
<ol>
    <li>在 Eclipse 里面，选择 <strong>File &gt; New &gt; Project</strong>，然后再弹出的窗口中选择<strong>动态 Web 项目</strong>，如下图所示。 </li>
</ol>
<br />
<a name="figure2"><strong>图二：创建动态Web项目</strong></a><br />
<img height="473" alt="创建动态Web项目" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure2.jpg" width="496" /> <br />
<ol start="2">
    <li>在创建好动态 Web 项目之后，你会看到创建好的项目的结构，如下图所示，项目的名称为 sample.dw.paper.lucene。 </li>
</ol>
<br />
<a name="figure3"><strong>图三：动态 Web 项目的结构</strong></a><br />
<img height="338" alt="动态 Web 项目的结构" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure3.jpg" width="329" /> <br />
<p><a name="N10182"><span class="smalltitle">2. 设计 Web 项目的架构</span></a></p>
<p>在我们的设计中，把该系统分成如下四个子系统：</p>
<ol>
    <li><strong>用户接口</strong>: 这个子系统提供用户界面使用户可以向 Web 应用程序服务器提交搜索请求，然后搜索结果通过用户接口来显示出来。我们用一个名为 search.jsp 的页面来实现该子系统。
    <li><strong>请求管理器</strong>: 这个子系统管理从客户端发送过来的搜索请求并把搜索请求分发到搜索子系统中。最后搜索结果从搜索子系统返回并最终发送到用户接口子系统。我们使用一个 Servlet 来实现这个子系统。
    <li><strong>搜索子系统</strong>: 这个子系统负责在索引文件上进行搜索并把搜索结构传递给请求管理器。我们使用 Lucene 提供的 API 来实现该子系统。
    <li><strong>索引子系统</strong>: 这个子系统用来为 HTML 页面来创建索引。我们使用 Lucene 的 API 以及 Lucene 提供的一个 HTML 解析器来创建该子系统。 </li>
</ol>
<p>下图显示了我们设计的详细信息，我们将用户接口子系统放到 webContent 目录下面。你会看到一个名为 search.jsp 的页面在这个文件夹里面。请求管理子系统在包 <code>sample.dw.paper.lucene.servlet</code> 下面，类 <code>SearchController</code> 负责功能的实现。搜索子系统放在包 <code>sample.dw.paper.lucene.search</code> 当中，它包含了两个类，<code>SearchManager</code> 和 <code>SearchResultBean</code>，第一个类用来实现搜索功能，第二个类用来描述搜索结果的结构。索引子系统放在包 <code>sample.dw.paper.lucene.index</code> 当中。类 <code>IndexManager</code> 负责为 HTML 文件创建索引。该子系统利用包 <code>sample.dw.paper.lucene.util</code> 里面的类 <code>HTMLDocParser</code> 提供的方法 <code>getTitle</code> 和 <code>getContent</code> 来对 HTML 页面进行解析。 </p>
<br />
<a name="figure4"><strong>图四：项目的架构设计</strong></a><br />
<img height="324" alt="项目的架构设计" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure4.jpg" width="384" /> <br />
<p><a name="N101E4"><span class="smalltitle">3. 子系统的实现</span></a></p>
<p>在分析了系统的架构设计之后，我们接下来看系统实现的详细信息。 </p>
<ol>
    <li><strong>用户接口</strong>: 这个子系统有一个名为 search.jsp 的 JSP 文件来实现，这个 JSP 页面包含两个部分。第一部分提供了一个用户接口去向 Web 应用程序服务器提交搜索请求，如下图所示。注意到这里的搜索请求发送到了一个名为 SearchController 的 Servlet 上面。Servlet 的名字和具体实现的类的对应关系在 web.xml 里面指定。 </li>
</ol>
<br />
<a name="figure5"><strong>图5：向Web服务器提交搜索请求</strong></a><br />
<img height="207" alt="向Web服务器提交搜索请求" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure5.jpg" width="532" /> <br />
<p>这个JSP的第二部分负责显示搜索结果给用户，如图下图所示： </p>
<br />
<a name="figure6"><strong>图6：显示搜索结果</strong></a><br />
<img height="360" alt="显示搜索结果" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure6.jpg" width="572" /> <br />
<ol start="2">
    <li><strong>请求管理器</strong>: 一个名为 <code>SearchController</code> 的 servlet 用来实现该子系统。下面代码给出了这个类的源代码。 </li>
</ol>
<br />
<a name="Listing6"><strong>清单６：请求管理器的实现</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在代码中，<code>doPost</code> 方法从客户端获取搜索词并创建类 <code>SearchManager</code> 的一个实例，其中类 <code>SearchManager</code> 在搜索子系统中进行了定义。然后，<code>SearchManager</code> 的方法 search 会被调用。最后搜索结果被返回到客户端。 </p>
<ol start="3">
    <li><strong>搜索子系统</strong>: 在这个子系统中，我们定义了两个类：<code>SearchManager</code> 和 <code>SearchResultBean</code>。第一个类用来实现搜索功能，第二个类是个JavaBean，用来描述搜索结果的结构。下面代码给出了类 <code>SearchManager</code> 的源代码。 </li>
</ol>
<br />
<a name="Listing7"><strong>清单7：搜索功能的实现</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在上面代码，注意到在这个类里面有三个私有属性。第一个是 <code>searchWord</code>，代表了来自客户端的搜索词。第二个是 <code>indexManager</code>，代表了在索引子系统中定义的类 <code>IndexManager</code> 的一个实例。第三个是 <code>analyzer</code>，代表了用来解析搜索词的解析器。现在我们把注意力放在方法 <code>search</code> 上面。这个方法首先检查索引文件是否已经存在，如果已经存在，那么就在已经存在的索引上进行检索，如果不存在，那么首先调用类 <code>IndexManager</code> 提供的方法来创建索引，然后在新创建的索引上进行检索。搜索结果返回后，这个方法从搜索结果中提取出需要的属性并为每个搜索结果生成类 <code>SearchResultBean</code> 的一个实例。最后这些 <code>SearchResultBean</code> 的实例被放到一个列表里面并返回给请求管理器。</p>
<p>在类 <code>SearchResultBean</code> 中，含有两个属性，分别是 <code>htmlPath</code> 和 <code>htmlTitle</code>，以及这个两个属性的 get 和 set 方法。这也意味着我们的搜索结果包含两个属性：<code>htmlPath</code> 和 <code>htmlTitle</code>，其中 <code>htmlPath</code> 代表了 HTML 文件的路径，<code>htmlTitle</code> 代表了 HTML 文件的标题。 </p>
<ol start="4">
    <li><strong>索引子系统</strong>: 类 <code>IndexManager</code> 用来实现这个子系统。 下面代码给出了这个类的源代码。 </li>
</ol>
<br />
<a name="Listing8"><strong>清单8：索引子系统的实现</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这个类包含两个私有属性，分别是 <code>dataDir</code> 和 <code>indexDir</code>。<code>dataDir</code> 代表存放等待进行索引的 HTML 页面的路径，<code>indexDir</code> 代表了存放 Lucene 索引文件的路径。类 <code>IndexManager</code> 提供了三个方法，分别是 <code>createIndex</code>, <code>addDocument</code> 和 <code>ifIndexExist</code>。如果索引不存在的话，你可以使用方法 <code>createIndex</code> 去创建一个新的索引，用方法 <code>addDocument</code> 去向一个索引上添加文档。在我们的场景中，一个文档就是一个 HTML 页面。方法 <code>addDocument</code> 会调用由类 <code>HTMLDocParser</code> 提供的方法对 HTML 文档进行解析。你可以使用最后一个方法 <code>ifIndexExist</code> 来判断 Lucene 的索引是否已经存在。 </p>
<p>现在我们来看一下放在包 <code>sample.dw.paper.lucene.util</code> 里面的类 <code>HTMLDocParser</code>。这个类用来从 HTML 文件中提取出文本信息。这个类包含三个方法，分别是 <code>getContent</code>，<code>getTitle</code> 和 <code>getPath</code>。第一个方法返回去除了 HTML 标记的文本内容，第二个方法返回 HTML 文件的标题，最后一个方法返回 HTML 文件的路径。下面代码给出了这个类的源代码。 </p>
<br />
<a name="Listing9"><strong>清单9：HTML 解析器</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">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>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N10330"><span class="smalltitle">5．在 Tomcat 5.0 上运行应用程序</span></a></p>
<p>现在我们可以在 Tomcat 5.0 上运行开发好的应用程序。 </p>
<ol>
    <li>右键单击 <strong>search.jsp</strong>，然后选择 <strong>Run as &gt; Run on Server</strong>，如下图所示。 </li>
</ol>
<br />
<a name="figure7"><strong>图7：配置 Tomcat 5.0</strong></a><br />
<img height="434" alt="配置 Tomcat 5.0" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure7.jpg" width="478" /> <br />
<ol start="2">
    <li>在弹出的窗口中，选择 <strong>Tomcat v5.0 Server</strong> 作为目标 Web 应用程序服务器，然后点击 <strong>Next</strong>，如下图所示： </li>
</ol>
<br />
<a name="figure8"><strong>图8：选择 Tomcat 5.0</strong></a><br />
<img height="433" alt="选择 Tomcat 5.0" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure8.jpg" width="467" /> <br />
<ol start="3">
    <li>现在需要指定用来运行 Web 应用程序的 Apache Tomcat 5.0 以及 JRE 的路径。这里你所选择的 JRE 的版本必须和你用来编译 Java 文件的 JRE 的版本一致。配置好之后，点击 <strong>Finish</strong>。如下图 所示。 </li>
</ol>
<br />
<a name="figure9"><strong>图9：完成Tomcat 5.0的配置</strong></a><br />
<img height="436" alt="完成Tomcat 5.0的配置" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure9.jpg" width="468" /> <br />
<ol start="4">
    <li>配置好之后，Tomcat 会自动运行，并且会对 search.jsp 进行编译并显示给用户。如下图 所示。 </li>
</ol>
<br />
<a name="figure10"><strong>图10：用户界面</strong></a><br />
<img height="403" alt="用户界面" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure10.jpg" width="521" /> <br />
<ol start="5">
    <li>在输入框中输入关键词 &#8220;information&#8221; 然后单击 <strong>Search</strong> 按钮。然后这个页面上会显示出搜索结果来，如下图所示。 </li>
</ol>
<br />
<a name="figure11"><strong>图11：搜索结果</strong></a><br />
<img height="416" alt="搜索结果" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure11.jpg" width="526" /> <br />
<ol start="6">
    <li>单击搜索结果的第一个链接，页面上就会显示出所链接到的页面的内容。如下图所示. </li>
</ol>
<br />
<a name="figure12"><strong>图12：详细信息</strong></a><br />
<img height="419" alt="详细信息" src="http://www.ibm.com/developerworks/cn/web/wa-lucene2/figure12.jpg" width="525" /> <br />
<p>现在我们已经成功的完成了示例项目的开发，并成功的用Lucene实现了搜索和索引功能。你可以下载这个项目的源代码（<a title="下载" href="http://www.blogjava.net/Files/szhswl/wa-lucene2_source_code.zip">下载</a>）。 </p>
<br />
<p><a name="N103ED"><span class="atitle">总结</span></a></p>
<p>Lucene 提供了灵活的接口使我们更加方便的设计我们的 Web 搜索应用程序。如果你想在你的应用程序中加入搜索功能，那么 Lucene 是一个很好的选择。在设计你的下一个带有搜索功能的应用程序的时候可以考虑使用 Lucene 来提供搜索功能。<br />
<br />
本文摘自：http://www.ibm.com/developerworks/cn/web/wa-lucene2/</p>
<img src ="http://www.blogjava.net/szhswl/aggbug/168301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-17 19:17 <a href="http://www.blogjava.net/szhswl/articles/168301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给Compass搜索添加高亮(highlight)</title><link>http://www.blogjava.net/szhswl/articles/167326.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Wed, 12 Dec 2007 12:39:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/167326.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/167326.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/167326.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/167326.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/167326.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;参考了springside的代码实现了高亮，其实也是很简单的。在原来的searchController里面用的是默认的CompassSearchHelper来搜索的。<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(searchHelper&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">)&nbsp;{<br />
</span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;searchHelper&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;CompassSearchHelper(getCompass(),&nbsp;getPageSize());<br />
</span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CompassSearchCommand&nbsp;searchCommand&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;CompassSearchCommand();<br />
</span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;searchCommand.setPage(</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(currentPage</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">));<br />
</span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;searchCommand.setQuery(query);<br />
</span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ModelAndView&nbsp;mv&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;ModelAndView();<br />
</span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv.addObject(</span><span style="color: #000000">"</span><span style="color: #000000">query</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;query);<br />
</span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv.addObject(</span><span style="color: #000000">"</span><span style="color: #000000">p</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;currentPage);<br />
</span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000">&lt;</span><span style="color: #000000">String</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;errors&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;ArrayList</span><span style="color: #000000">&lt;</span><span style="color: #000000">String</span><span style="color: #000000">&gt;</span><span style="color: #000000">();<br />
</span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">try</span><span style="color: #000000">{<br />
</span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CompassSearchResults&nbsp;searchResults&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;searchHelper.search(searchCommand);<br />
</span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv.addObject(getSearchResultsName(),&nbsp;searchResults);<br />
</span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="color: #0000ff">catch</span><span style="color: #000000">&nbsp;(SearchEngineQueryParseException&nbsp;ex){<br />
</span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errors.add(TextUtil.escapeHTML(ex.getMessage()));<br />
</span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mv.addObject(</span><span style="color: #000000">"</span><span style="color: #000000">errors</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;errors);<br />
</span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
</span><span style="color: #008080">18</span>&nbsp;</div>
本来打算按照springside里面的封装方法。<br />
<a href="http://svn.javascud.org/svn/springside/springside2/trunk/core/src/java/org/springside/components/compass/">http://svn.javascud.org/svn/springside/springside2/trunk/core/src/java/org/springside/components/compass/</a><br />
这里有两个类AdvancedSearchCommand.java,CompassSearchService.java，CompassSearchService里面实现了高亮和排序，后来发现CompassSearchService和CompassSearchHelper长得很像，而且CompassSearchHelper也提供了两个抽象的方法给用户提供一个添加功能的机会。<br />
<code><br />
</code>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">/**</span><span style="color: #008000"><br />
</span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;An&nbsp;option&nbsp;to&nbsp;perform&nbsp;any&nbsp;type&nbsp;of&nbsp;processing&nbsp;before&nbsp;the&nbsp;hits&nbsp;are&nbsp;detached.<br />
</span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;doProcessBeforeDetach(CompassSearchCommand&nbsp;searchCommand,&nbsp;CompassSession&nbsp;session,&nbsp;CompassHits&nbsp;hits,<br />
</span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;from,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;size)&nbsp;{<br />
</span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">/**</span><span style="color: #008000"><br />
</span><span style="color: #008080">10</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;An&nbsp;option&nbsp;to&nbsp;perform&nbsp;any&nbsp;type&nbsp;of&nbsp;processing&nbsp;after&nbsp;the&nbsp;hits&nbsp;are&nbsp;detached.<br />
</span><span style="color: #008080">11</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000"><br />
</span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;doProcessAfterDetach(CompassSearchCommand&nbsp;searchCommand,&nbsp;CompassSession&nbsp;session,<br />
</span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CompassDetachedHits&nbsp;hits)&nbsp;{<br />
</span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
</span><span style="color: #008080">16</span>&nbsp;</div>
所以我们只要实现一个自己的CompassSearchHelper覆盖这两个方法就可以达到实现高亮的目的了。<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #008000">/**</span><span style="color: #008000"><br />
</span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #008000">&nbsp;*&nbsp;</span><span style="color: #808080">@author</span><span style="color: #008000">&nbsp;&lt;a&nbsp;href="mailto:rory.cn@gmail.com"&gt;somebody&lt;/a&gt;<br />
</span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #008000">&nbsp;*&nbsp;</span><span style="color: #808080">@since</span><span style="color: #008000">&nbsp;Aug&nbsp;23,&nbsp;2007&nbsp;2:04:19&nbsp;PM<br />
</span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #008000">&nbsp;*&nbsp;</span><span style="color: #808080">@version</span><span style="color: #008000">&nbsp;$Id&nbsp;AdvanceCompassSearchHelper.java$<br />
</span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #008000">&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;AdvanceCompassSearchHelper&nbsp;</span><span style="color: #0000ff">extends</span><span style="color: #000000">&nbsp;CompassSearchHelper&nbsp;{<br />
</span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;String[]&nbsp;highlightFields;<br />
</span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;String[]&nbsp;getHighlightFields()&nbsp;{<br />
</span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;highlightFields;<br />
</span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;setHighlightFields(String[]&nbsp;highlightFields)&nbsp;{<br />
</span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">this</span><span style="color: #000000">.highlightFields&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;highlightFields;<br />
</span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">18</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">/**</span><span style="color: #008000"><br />
</span><span style="color: #008080">19</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080">@param</span><span style="color: #008000">&nbsp;compass<br />
</span><span style="color: #008080">20</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000"><br />
</span><span style="color: #008080">21</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;AdvanceCompassSearchHelper(Compass&nbsp;compass)&nbsp;{<br />
</span><span style="color: #008080">22</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">super</span><span style="color: #000000">(compass);<br />
</span><span style="color: #008080">23</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">24</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">25</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">26</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">/*</span><span style="color: #008000">&nbsp;(non-Javadoc)<br />
</span><span style="color: #008080">27</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;org.compass.core.support.search.CompassSearchHelper#doProcessBeforeDetach(org.compass.core.support.search.CompassSearchCommand,&nbsp;org.compass.core.CompassSession,&nbsp;org.compass.core.CompassHits,&nbsp;int,&nbsp;int)<br />
</span><span style="color: #008080">28</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000"><br />
</span><span style="color: #008080">29</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
</span><span style="color: #008080">30</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;doProcessBeforeDetach(CompassSearchCommand&nbsp;searchCommand,<br />
</span><span style="color: #008080">31</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CompassSession&nbsp;session,&nbsp;CompassHits&nbsp;hits,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;from,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;size)&nbsp;{<br />
</span><span style="color: #008080">32</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(from&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">)&nbsp;{<br />
</span><span style="color: #008080">33</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br />
</span><span style="color: #008080">34</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;hits.getLength();<br />
</span><span style="color: #008080">35</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">36</span>&nbsp;<span style="color: #000000"><br />
</span><span style="color: #008080">37</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(highlightFields&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">null</span><span style="color: #000000">)&nbsp;{<br />
</span><span style="color: #008080">38</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">;<br />
</span><span style="color: #008080">39</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">40</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;highlight&nbsp;fields</span><span style="color: #008000"><br />
</span><span style="color: #008080">41</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">&nbsp;(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;from;&nbsp;i&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;size;&nbsp;i</span><span style="color: #000000">++</span><span style="color: #000000">)&nbsp;{<br />
</span><span style="color: #008080">42</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">&nbsp;(String&nbsp;highlightField&nbsp;:&nbsp;highlightFields)&nbsp;{<br />
</span><span style="color: #008080">43</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hits.highlighter(i).fragment(highlightField);<br />
</span><span style="color: #008080">44</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">45</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span><span style="color: #008080">46</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
</span><span style="color: #008080">47</span>&nbsp;</div>
ok，这样就可以实现高亮了。修改一下SearchController的配置。<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean&nbsp;</span><span style="color: #ff0000">id</span><span style="color: #0000ff">="advanceCompassSearchHelper"</span><span style="color: #ff0000">&nbsp;class</span><span style="color: #0000ff">="com.jdkcn.compass.AdvanceCompassSearchHelper"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="highlightFields"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">list</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">content</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">list</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="pageSize"</span><span style="color: #0000ff">&gt;&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">10</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">constructor-arg&nbsp;</span><span style="color: #ff0000">ref</span><span style="color: #0000ff">="compass"</span><span style="color: #0000ff">/&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean&nbsp;</span><span style="color: #ff0000">id</span><span style="color: #0000ff">="searchController"</span><span style="color: #ff0000">&nbsp;class</span><span style="color: #0000ff">="com.jdkcn.web.SearchController"</span><span style="color: #ff0000">&nbsp;parent</span><span style="color: #0000ff">="baseController"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="compass"</span><span style="color: #0000ff">&gt;&lt;</span><span style="color: #800000">ref&nbsp;</span><span style="color: #ff0000">bean</span><span style="color: #0000ff">="compass"</span><span style="color: #0000ff">/&gt;&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="searchView"</span><span style="color: #0000ff">&gt;&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">search</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="searchResultsView"</span><span style="color: #0000ff">&gt;&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">search</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="pageSize"</span><span style="color: #0000ff">&gt;&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">10</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="searchHelper"</span><span style="color: #0000ff">&gt;&lt;</span><span style="color: #800000">ref&nbsp;</span><span style="color: #ff0000">local</span><span style="color: #0000ff">="advanceCompassSearchHelper"</span><span style="color: #0000ff">/&gt;&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">&nbsp;<br />
</span><span style="color: #008080">18</span>&nbsp;</div>
还不要忘了修改一下compass settings 修改一下高亮的样式，修改一下高亮的颜色。<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean&nbsp;</span><span style="color: #ff0000">id</span><span style="color: #0000ff">="compass"</span><span style="color: #ff0000">&nbsp;class</span><span style="color: #0000ff">="org.compass.spring.LocalCompassBean"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="resourceDirectoryLocations"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">list</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">classpath:com/jdkcn/compass</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">list</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="connection"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">/lucene/indexes</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="compassSettings"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">props</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">prop&nbsp;</span><span style="color: #ff0000">key</span><span style="color: #0000ff">="compass.transaction.factory"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.compass.spring.transaction.SpringSyncTransactionFactory<br />
</span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">prop</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">prop&nbsp;</span><span style="color: #ff0000">key</span><span style="color: #0000ff">="compass.engine.highlighter.default.formatter.simple.pre"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;![CDATA[</span><span style="color: #808080">&lt;font&nbsp;color="red"&gt;&lt;b&gt;</span><span style="color: #0000ff">]]&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">prop</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">18</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">prop&nbsp;</span><span style="color: #ff0000">key</span><span style="color: #0000ff">="compass.engine.highlighter.default.formatter.simple.post"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">19</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;![CDATA[</span><span style="color: #808080">&lt;/b&gt;&lt;/font&gt;</span><span style="color: #0000ff">]]&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">20</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">prop</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">21</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">props</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">22</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">23</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property&nbsp;</span><span style="color: #ff0000">name</span><span style="color: #0000ff">="transactionManager"</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">24</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">ref&nbsp;</span><span style="color: #ff0000">bean</span><span style="color: #0000ff">="transactionManager"</span><span style="color: #ff0000">&nbsp;</span><span style="color: #0000ff">/&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">25</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">property</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br />
</span><span style="color: #008080">26</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">&nbsp;<br />
</span><span style="color: #008080">27</span>&nbsp;</div>
ok了，页面上输出一下就能看到最终效果了<code>${hit.highlightedText['content']?if_exists}<br />
<br />
本文转自:http://jdkcn.com/entry/howto-add-highlight-in-compass.html</code><br />
  <img src ="http://www.blogjava.net/szhswl/aggbug/167326.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-12 20:39 <a href="http://www.blogjava.net/szhswl/articles/167326.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Compass: 在你的应用中集成搜索功能</title><link>http://www.blogjava.net/szhswl/articles/167242.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Wed, 12 Dec 2007 07:20:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/167242.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/167242.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/167242.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/167242.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/167242.html</trackback:ping><description><![CDATA[在许多应用程序中，用户总会提出搜索和查询领域实例的需求。他们或者希望构建一个进入应用程序的入口或者希望填充表单的机制。非常典型的解决方案是用浏览的方式（把领域的继承关系表现出来，这样用户可以定位和选择一个自己需要的）或者一个检索表单的方式（展现一个多个输入域的表单，用户可以检索他们需要的信息）。<br />
<br />
现实中，对于可用性的角度来说，这两种方案都不是最佳的。浏览的方式会在有许多分支的时候变得缓慢而笨重。而且，用户通常精确地知道他们要用到那个应用，然而却不情愿要浏览整个系统来找到他要的应用。检索表单的方式同样被检索条件个数的多少限制住了。这就要在设置足够的检索域还是检索表单的复杂性上作出权衡。<br />
<br />
从可用性的角度来说，解决这个问题的答案就是提供一个单一的、Google样式的检索框，用户可以输入任何符合实例字段的内容。他们可以检索和表示符合这些内容的结果。表单中的这个检索框可以自动填充、Google建议模式的输入框，或者是返回表格式结果的正则表达式搜索。不管怎样，这种解决方案的精髓就是UI是简单的，用户可以输入任何他们选择的条件，然后由搜索引擎去做这些复杂的工作。现在唯一的问题时：如何实现这样的搜索机能。<br />
<br />
当面对实现传统的多输入域的表单的时候，大部分应用程序都选择了SQL。典型的情况是，检索的字段都与列名相匹配，并且使用SQL的LIKE语句。然而，因为复杂的SQL要去匹配太多的字段，并且很多情况下由于这些字段的文本长度问题，造成实现的性能经常是非常差的。第二个问题是，对检索结果没有排名并且返回的提示并没有反应出与查询的内容有多大相关性，只是简单地返回结果罢了。第三，对检索结果相关联的关键字没有高亮表示。<br />
<br />
很快，大家意识到大部分应用程序需要搜索引擎。所有实体的字段可以像只有一个文件那样被索引，并且是正则文本搜索可以匹配的实体。现在非常流行的搜索引擎之一是Luence。Lucene是相当不错的搜索引擎，在很多项目中应用成功。它提供了底层的搜索引擎API，能够使用Lucene数据结构（Document/Field）去索引数据，能供使用查询API或搜索引擎在索引上检索。它已经在多种编程语言上实现了全部功能，包括Java、C#和 C++等。<br />
<br />
如果我们分析一个典型的Web应用程序，一般都有个一个共通的架构和特点。通常，应用与后端的关系数据库一起工作。这个应用使用领域模型表示这个系统中的实体，并使用ORM框架把领域模型映射到数据库上。一般情况下，使用一个服务层框架去管理事务、协作，有时也包括业务逻辑和Web框架。问题就在于怎么把Lucene集成到这样的应用程序中去。<br />
<br />
当你试图去集成Lucene的时候，刚刚把第一个简单的程序跑起来的时候，马上就会遇到一连串的挑战。第一个问题就是索引应用数据。在之前很长一段时间，相当多的样板式代码热衷于把领域模型映射到Lucene数据模型上去。Lucene文档，是Lucene主要的数据结构，它是一个扁平的类似Map 的，只包含字符串的数据结构——所以许多无意义的代码热衷于&#8220;植入&#8221;和&#8220;植出&#8221;领域模型。另外一个问题是缺少对Lucene的事务控制，把领域模型数据存储到数据库和搜索引擎是有问题的。而且还有几个其他的很有名的实践和模式要在Lucene中实现，比如缓存、隐式的搜索、为支持Google样式的搜索而创建聚集的属性和为合适的语义保持可识别的Document对象，等等。<br />
Compass简介<br />
<br />
Compass的设计目标是简化企业在集成搜索功能时的花费。Compass是在Lucene之上，使用了设计很好的搜索引擎的抽象。Compass扩展了核心Lucene，增加了事务控制功能和快速更新，也包括在数据库存储索引的功能。当然，它没有去隐藏Lucene的特性——所有Lucene的功能都能通过Compass实现。<br />
Compass核心API<br />
<br />
Compass提供给了我们简单的并且熟悉的API。说Compass提供了让人熟悉的API是因为它模仿了当前流行的ORM框架的API来降低学习曲线。Compass以下面一些主要的接口作为主要内容：<br />
<br />
&nbsp; &nbsp; * CompassConfiguration：用来在一些设置参数、配置文件和映射定义上配置Compass。通常用来创建Compass接口。<br />
&nbsp; &nbsp; * Compass：为单线程使用，创建线程安全的实例来打开Compass Seesion。同样还提供了一些搜索引擎索引级别的操作。<br />
&nbsp; &nbsp; * CompassSesssion：用来执行像保存、删除、查找、装载这样的搜索操作。很轻量但是并不是线程安全的。<br />
&nbsp; &nbsp; * CompassTransaction：管理Compass事务的接口。使用它并不需要事务管理环境（像Spring、JTA）。<br />
<br />
下面是使用这些API的一个简单的例子：<br />
<br />
// 在程序中配置和创建Compass<br />
CompassConfiguration conf =<br />
&nbsp; &nbsp;&nbsp;&nbsp;new CompassConfiguration().setConnection("/tmp/index").addClass(Author.class);<br />
Compass compass = conf.buildCompass();<br />
<br />
&nbsp;&nbsp;// 一个请求操作<br />
CompassSession session = compass.openSession();<br />
CompassTransaction tx = null;<br />
try {<br />
&nbsp; &nbsp;&nbsp;&nbsp;tx = session.beginTransaction();<br />
&nbsp; &nbsp;&nbsp;&nbsp;...<br />
&nbsp; &nbsp;&nbsp;&nbsp;session.save(author);<br />
&nbsp; &nbsp;&nbsp;&nbsp;CompassHits hits = session.find("jack london");<br />
&nbsp; &nbsp;&nbsp;&nbsp;Author a = (Author) hits.data(0);<br />
&nbsp; &nbsp;&nbsp;&nbsp;Resource r = hits.resource(0);<br />
&nbsp; &nbsp;&nbsp;&nbsp;...<br />
&nbsp; &nbsp;&nbsp;&nbsp;tx.commit();<br />
} catch (CompassException ce) {<br />
&nbsp; &nbsp;&nbsp;&nbsp;if (tx != null) tx.rollback();<br />
} finally {<br />
&nbsp; &nbsp;&nbsp;&nbsp;session.close();<br />
}<br />
<br />
为了简化事务管理代码，Compass提供了好几种选择，首先是使用CompassTemplate，它使用流行的设计模式来抽象事务管理。第二个选择是在和事务管理环境下，这样，Compass与JTA与Spring这样的事务管理器集成并在一个已经存在的事务中执行。这个情况下，当一个 Session执行的时候，CompassSession 可被用做一个自动加入事务处理的代理。这个代理的创建可以是编程式的，也可使用Spring IOC（Spring 2 中支持@CompassContext）。<br />
<br />
Compass支持原子性的事务运算，与不同的事务管理策略集成，包括本地事务管理、JTA同步、XA for JTA的集成，Spring同步的集成。<br />
<br />
Compass的配置基于&#8220;键—值&#8221;的一一对应的设置。Compass可以使用编程式的配置，基于XML DTD的配置（定义映射和设置），基于XML Schema的配置。基于XML Schema的配置得到了Spring2新的基于Schema配置文件的支持。<br />
搜索引擎映射<br />
<br />
Compass的主要功能之一就是从应用程序模型到搜索引擎的声明式映射。Compass搜索引擎的领域模型由资源（Lucene Document）和属性（一个Lucene Field）组成。这是用来索引可搜索内容的抽象数据对象。<br />
RSEM<br />
<br />
第一个映射是RSEM（Resource/SearchEngine Mapping）。这是一个低级别从Compass资源和属性到搜索引擎抽象到搜索引擎的映射。下面是个对作者资源的RSEM的示例：<br />
<br />
&lt;resource alias="author"&gt;<br />
&nbsp; &nbsp; &lt;resource-id name="id"/&gt;<br />
&nbsp; &nbsp; &lt;resource-property name="firstName"/&gt;<br />
&nbsp; &nbsp; &lt;resource-property name="lastName" store="yes" index="tokenized"/&gt;<br />
&nbsp; &nbsp; &lt;resource-property name="birthdate" converter="mydate"/&gt;<br />
&nbsp;&nbsp;&lt;/resource&gt;<br />
&nbsp;&nbsp;<br />
<br />
上面的例子中，我们定义了一个映射了作者别名的资源。这个资源的映射包括标识资源的ID和几个附加的属性。定义属性是可选的，尽管他们允许声明式的控制不同属性的特征，包括和一个转换器关联。下面的示例代码填充了一个资源并索引它。<br />
<br />
Resource r = session.createResource("author");<br />
&nbsp;&nbsp;r.addProperty("id", "1")<br />
&nbsp; &nbsp; .addProperty("firstName", "jack")<br />
&nbsp; &nbsp; .addProperty("lastName", "london")<br />
&nbsp; &nbsp; .addProperty("birthdate", new Date());<br />
&nbsp;&nbsp;session.save(r);<br />
&nbsp;&nbsp;<br />
<br />
上面的代码显示了一些Compass的特性。第一，由于一个资源是可识别的，Compass在这个资源已经存在的情况下更新它。第二，可以声明式的分配一个转换器给这个资源，可以使用Compass内置的许多转换器。下面是上面示例代码的Compass配置（包括对mydate转换器的配置）：<br />
<br />
&lt;compass-core-config xmlns="http://www.opensymphony.com/compass/schema/core-config" <br />
&nbsp; &nbsp;&nbsp; &nbsp;xsi:schemaLocation="http://www.opensymphony.com/compass/schema/core-config <br />
&nbsp; &nbsp;&nbsp; &nbsp;<a href="http://www.opensymphony.com/compass/schema/compass-core-config.xsd" target="_blank">http://www.opensymphony.com/comp ... ass-core-config.xsd</a>"&gt; <br />
&nbsp; &nbsp; &lt;compass name="default"&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;connection&gt; <br />
&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;&lt;file path="index" /&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/connection&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;converters&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;converter name="mydate" type="org.compass.core.converter.basic.DateConverter"&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;setting name="format" value="yyyy-MM-dd" /&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;/converter&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/converters&gt; <br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;mappings&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;resource location="sample/author.cpm.xml" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;/mappings&gt;<br />
&nbsp; &nbsp; &lt;/compass&gt; <br />
&nbsp;&nbsp;&lt;/compass-core-config&gt;<br />
&nbsp;&nbsp;<br />
<br />
OSEM<br />
<br />
OSEM(Object/Search Engine Mapping)是第二个支持的映射方案。它允许把应用对象的领域模型映射到搜索引擎。下面是Author类，使用注释对它进行了OSEM定义：<br />
<br />
@Searchable<br />
public class Author {<br />
<br />
&nbsp; &nbsp; @SearchableId<br />
&nbsp; &nbsp;private Long id;<br />
<br />
&nbsp; &nbsp; @SearchableComponent<br />
&nbsp; &nbsp;private String Name;<br />
<br />
&nbsp; &nbsp; @SearchableReference<br />
&nbsp; &nbsp;private List books;<br />
<br />
&nbsp; &nbsp; @SearchableProperty(format = "yyyy-MM-dd")<br />
&nbsp; &nbsp;private Date birthdate;<br />
}<br />
<br />
&nbsp;&nbsp;// ...<br />
<br />
@Searchable<br />
public class Name {<br />
<br />
&nbsp; &nbsp; @SearchableProperty<br />
&nbsp; &nbsp;private String firstName;<br />
<br />
&nbsp; &nbsp; @SearchableProperty<br />
&nbsp; &nbsp;private String lastName;<br />
} <br />
<br />
OSEM支持&#8220;植入&#8221;和&#8220;植出&#8221;一个对象的分层结构进入一个资源。当存储一个Author对象，Compass就会&#8220;植入&#8221;进一个资源，Name类也会&#8220;植入&#8221;进相同的资源来表示这个作者（由于组件的映射），也包括一个这个作者书籍列表里的每一本书（存储在其他的资源里）的引用。这个最后得到的资源会存储或者索引在搜索引擎中。<br />
<br />
Compass提供了非常灵活的机制来把领域模型映射到搜索引擎中。上面的例子只是一个很简单的例子。OSEM允许制定不同的转换器，一个类属性对应多个元数据（从资源到属性的映射）、分析器和所有参与的字段，等等。<br />
<br />
下面是author类怎样使用的例子：<br />
<br />
// ...<br />
Author author = new Author(1, new Name("jack", "london"), new Date());<br />
session.save(author);<br />
// ...<br />
author = (Author) session.load(Author.class, 1); <br />
<br />
XSEM<br />
<br />
最后，Compass支持的搜索引擎映射是XSEM(Xml/Search Engine Mapping)。这种映射允许基于XML映射的定义（用XPath实现），把XML数据结构直接映射到搜索引擎。XSEM的处理同样的通过对资源&#8220;植入&#8221;和&#8220;植出&#8221;的处理。Compass提供了一个XML包装对象叫做XmlObject，它定义了不同的实现(dom4j, W3C Document)，这些实现允许XPath表达式来求值。如果我们给出下面的XML数据结构：<br />
<br />
&lt;xml-fragment&gt;<br />
&nbsp; &nbsp; &lt;author id="1"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;firstName&gt;Jack&lt;/firstName&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;lastName&gt;London&lt;/lastName&gt;<br />
&nbsp; &nbsp; &lt;/author&gt;<br />
&nbsp;&nbsp;&lt;/xml-fragment&gt;<br />
&nbsp;&nbsp;<br />
<br />
下面是个XSEM的实现：<br />
<br />
&lt;compass-core-mapping&gt;<br />
&nbsp; &nbsp; &lt;xml-object alias="author" xpath="/xml-fragment/author"&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;xml-id name="id" xpath="@id" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;xml-property xpath="firstName" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;xml-property xpath="lastName" /&gt;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&lt;xml-content name="content" /&gt;<br />
&nbsp; &nbsp; &lt;/xml-object&gt;<br />
&nbsp;&nbsp;&lt;/compass-core-mapping&gt;<br />
&nbsp;&nbsp;<br />
<br />
从XML数据结构到搜索引擎的映射是使用XPath表达式来完成。XML内容映射可以在搜索引擎中存储为XML结构，这样就可以加载和搜索数据。 Compass支持多种XML DOM框架（为XML内容作映射），包括JSE5， dom4j（SAX 和XPP），当然定制的实现也很好做。下面是个不错的例子：<br />
<br />
Reader reader = // construct an xml reader over raw xml content<br />
AliasedXmlObject xmlObj = RawAliasedXmlObject("author", reader);<br />
session.save(xmlObj);<br />
// ...<br />
Resource resource = session.loadResource("author", 1);<br />
// since we have xml-content, we can do the following as well<br />
XmlObject xmlObj = session.load("author", 1); <br />
<br />
Compass Gps<br />
<br />
Compass Gps 是Compass的一个组件，用来把不同的数据源与Compass集成。大部分常用的数据源是Compass与ORM工具的集成。Compass支持JPA、Hibernate、OJB、JDO和iBatis。<br />
<br />
拿Hibernate作为例子，Compass给出了两个主要的操作：索引与镜像。拥有这两个映射的对象可以通过使用Hibernate API注册时间监听，进行自动的镜像操作到搜索引擎。下面的例子给出了怎样使用Compass Gps集成Hibernate：<br />
<br />
SessionFactory sessionFactory = // Hibernate Session Factory<br />
Compass compass = // set up a Compass instance<br />
CompassGps gps = new SingleCompassGps(compass);<br />
CompassGpsDevice device = new Hibernate3GpsDevice("hibernate", sessionFactory);<br />
gps.addDevice(device);<br />
// start the gps, mirroring any changes made through Hibernate API<br />
// to be mirrored to the search engine<br />
gps.start();<br />
<br />
&nbsp;&nbsp;// ....<br />
<br />
&nbsp;&nbsp;// this will cause the database to be indexed<br />
gps.index();<br />
&nbsp;&nbsp;// this will cause Hibernate to store the author in the database<br />
&nbsp;&nbsp;// and also index the author object through Compass<br />
hibernateSess.save(new Author(1, new Name("jack", "london"), new Date()));<br />
<br />
<br />
总结<br />
<br />
这篇文章对Compass的主要功能的做了介绍，但只是覆盖了怎样使用Compass的基本功能（尤其，Compass还有个与Spring集成的扩展组件，这个并没介绍）。在使用搜索引擎的时候，Compass同样也有很多现在流行功能和有一些细微的差别功能，还有配置扩展功能。Compass的主要目标，像刚才提到的，是简化集成搜索到任何类型的应用程序中，这篇文章只是介绍了怎么使用的基本信息。<br />
<br />
个人收藏资料，本文严重抄袭：<a href="http://bbs.51cto.com/thread-442091-1-1.html">http://bbs.51cto.com/thread-442091-1-1.html</a>
  <img src ="http://www.blogjava.net/szhswl/aggbug/167242.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-12 15:20 <a href="http://www.blogjava.net/szhswl/articles/167242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>lunece查询</title><link>http://www.blogjava.net/szhswl/articles/166776.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Mon, 10 Dec 2007 11:51:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/166776.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/166776.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/166776.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/166776.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/166776.html</trackback:ping><description><![CDATA[//初始化IndexSearcher<br />
IndexSearcher search = new IndexSearcher(path);<br />
//构建Term<br />
Term term = new Term(fieldName,value);<br />
//构建Query对象<br />
Query q = new TermQuery(term);<br />
//检索<br />
Hits hits = search.search(q);<br />
//显示查询结果<br />
for(int i=0; i&lt;hits.length(); i++)<br />
{<br />
&nbsp;System.out.println(hits.doc(i));<br />
}
<p>IndexSearcher的常用方法有<br />
search(Query q);<br />
search(Query q,Filter filter);<br />
search(Query q,Sort sort);<br />
search(Query q,Filter filter,Sort sort);</p>
<p>Hits的常用方法有<br />
doc(i); //得到第i个Document<br />
id(i);&nbsp; //得到第i个Document在lucene文件中的id号<br />
length(); //结果集的数量<br />
score(i); //第i个Doucment的文档得分，默认显示方式为score值越高，排得越前。score取值0-1之间<br />
如果想提高score的值。可以有建立索引时设置，用Field.setBoost(Float f)方法<br />
Field f = new Field(fieldname,value,store,tokenized);<br />
f.setBoost(5f);</p>
<p>在lucene中，document ID 越小，查询时所需时间越短，因为Hits的内部缓存机制。</p>
<p>Lunece的常用搜索</p>
<p>一、TermQuery 词条搜索<br />
Query query = new TermQuery(new Term(fieldname,value));</p>
<p>二、BooleanQuery 布尔搜索<br />
建立二个TermQuery<br />
Query q1 = new TermQuery(new Term(fieldname1,value1));<br />
Query q2 = new TermQuery(new Term(fieldname2,value2));<br />
建立BooleanQuery对象<br />
BooleanQuery query = new BooleanQuery();<br />
query.add(q1,BooleanClause.Occue.MUST);<br />
query.add(q2,BooleanClause.Occue.MUST);<br />
BooleanClause.Occue 有三个静态值<br />
MUST，MUST_NOT，SHOULD<br />
must&amp;&amp;must = (AnB)<br />
must&amp;&amp;must_not = (A-(AnB))<br />
should&amp;&amp;should = (AuB)</p>
<p>三、RangeQuery 范围搜索<br />
RangeQuery query = new RangeQuery(begin,end,false);<br />
begin = new Term(fieldname,value);<br />
end = new Term(fieldname,value);<br />
false 表示开区间 不包括 (begin,end) true 表示闭区间 包括 [begin,end]</p>
<p>四、PrefixQuery 前缀搜索<br />
PrefixQuery query = new PrefixQuery(new Term(fieldname,value));</p>
<p>五、PhraseQuery 短语搜索<br />
PhraseQuery query = new PhraseQuery();<br />
query.add(new Term(fieldname,value));<br />
query.add(new Term(fieldname,value));<br />
还可以设置坡度，query.setSlop(int n),默认为0如查询&#8220;钢铁&#8221;，可以用<br />
query.add(new Term(fieldname,&#8221;钢&#8221;));<br />
query.add(new Term(fieldname,&#8221;铁&#8221;));<br />
如想把&#8220;钢和铁&#8221;，&#8220;钢与铁&#8221;也查询出来。可以加上query.setSlop(1);</p>
<p>六、MultiPhraseQuery 多短语搜索<br />
MultiPhraseQuery query = new MultiPharseQuery();<br />
//加入短语的前缀<br />
query.add(new Term(fieldname,value));<br />
//加入短语的后缀<br />
query.add(new Term[] {new Term(fieldname,value), new Term(fieldname,value)});</p>
<p>七、FuzzyQuery 模糊搜索<br />
FuzzyQuery query = new FuzzyQuery(new Term(filed,value));<br />
它的三个构造函数<br />
FuzzyQuery(Term t);<br />
FuzzyQuery(Term t,float 0.5f); 相似度。0-1之间<br />
FuzzyQuery(Term t,float 0.5f,int prefixLength);前缀必须相同的长度<br />
<br />
本文转自:http://job5156.xicp.net/?p=72</p>
<img src ="http://www.blogjava.net/szhswl/aggbug/166776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-10 19:51 <a href="http://www.blogjava.net/szhswl/articles/166776.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lucene的工作原理(转载)</title><link>http://www.blogjava.net/szhswl/articles/165817.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Thu, 06 Dec 2007 07:57:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/165817.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/165817.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/165817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/165817.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/165817.html</trackback:ping><description><![CDATA[<div class="textbox-label"><strong>Lucene的概述：</strong> </div>
<div class="textbox-content" id="textboxContent">
<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.文章中的&#8221;in&#8221;, &#8220;once&#8221; &#8220;too&#8221;等词没有什么实际意义，中文中的&#8220;的&#8221;&#8220;是&#8221;等字通常也无具体含义，这些不代表概念的词可以过滤掉 <br />
　　c.用户通常希望查&#8220;He&#8221;时能把含&#8220;he&#8221;，&#8220;HE&#8221;的文章也找出来，所以所有单词需要统一大小写。 <br />
　　d.用户通常希望查&#8220;live&#8221;时能把含&#8220;lives&#8221;，&#8220;lived&#8221;的文章也找出来，所以需要把&#8220;lives&#8221;，&#8220;lived&#8221;还原成&#8220;live&#8221; <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>：有了关键词后，我们就可以建立倒排索引了。上面的对应关系是：&#8220;文章号&#8221;对&#8220;文章中所有关键词&#8221;。倒排索引把这个关系倒过来，变成<font color="#008080">：&#8220;关键词&#8221;对&#8220;拥有该关键词的所有文章号&#8221;。</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>加上&#8220;出现频率&#8221;和&#8220;出现位置&#8221;信息后，我们的索引结构变为：&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<table height="226" cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
    <tbody>
        <tr>
            <td>&nbsp;关键词</td>
            <td>&nbsp;文章号</td>
            <td>&nbsp;[出现频率]</td>
            <td>&nbsp;出现位置</td>
        </tr>
        <tr>
            <td>&nbsp;guangzhou</td>
            <td>&nbsp;1</td>
            <td>&nbsp;[2]</td>
            <td>&nbsp;3，6</td>
        </tr>
        <tr>
            <td>&nbsp;he</td>
            <td>&nbsp;2</td>
            <td>&nbsp;[1] </td>
            <td>&nbsp;1</td>
        </tr>
        <tr>
            <td>&nbsp;i </td>
            <td>&nbsp;1</td>
            <td>&nbsp;[1]</td>
            <td>&nbsp;4</td>
        </tr>
        <tr>
            <td>&nbsp;live </td>
            <td>&nbsp;1</td>
            <td>&nbsp;[2]</td>
            <td>&nbsp;2，5</td>
        </tr>
        <tr>
            <td>&nbsp;</td>
            <td>&nbsp;2</td>
            <td>&nbsp;[1]</td>
            <td>&nbsp;2</td>
        </tr>
        <tr>
            <td>&nbsp;shanghai</td>
            <td>&nbsp;2</td>
            <td>&nbsp;[1] </td>
            <td>&nbsp;3</td>
        </tr>
        <tr>
            <td>&nbsp;tom </td>
            <td>&nbsp;1</td>
            <td>&nbsp;[1]</td>
            <td>&nbsp;1</td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>　　以live 这行为例我们说明一下该结构：live在文章1中出现了2次，文章2中出现了一次，它的出现位置为&#8220;2,5,2&#8221;这表示什么呢？我们需要结合文章号和出现频率来分析，文章1中出现了2次，那么&#8220;2,5&#8221;就表示live在文章1中出现的两个位置，文章2中出现了一次，剩下的&#8220;2&#8221;就表示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;，例如：当前词为&#8220;阿拉伯语&#8221;，上一个词为&#8220;阿拉伯&#8221;，那么&#8220;阿拉伯语&#8221;压缩为&lt;3，语&gt;。其次大量用到的是对数字的压缩，数字只保存与上一个值的差值（这样可以减小数字的长度，进而减少保存该数字需要的字节数）。例如当前文章号是16389（不压缩要用3个字节保存），上一文章号是16382，压缩后保存7（只用一个字节）。 <font color="#008080">注意是&#8220;上一个词&#8221;。由于词典是按顺序排列的，这种压缩方法的效果会非常显著。</font></p>
<p>　　下面我们可以通过对该索引的查询来解释一下为什么要建立索引。 <br />
假设要查询单词 &#8220;live&#8221;，lucene先对词典二元查找、找到该词，通过指向频率文件的指针读出所有文章号，然后返回结果。词典通常非常小，因而，整个过程的时间是毫秒级的。 <br />
而用普通的顺序匹配算法，不建索引，而是对所有文章的内容进行字符串匹配，这个过程将会相当缓慢，当文章数目很大时，时间往往是无法忍受的。</p>
<p><strong>全文检索框架的实现机制：</strong> </p>
<p><strong></strong>　　Lucene的API接口设计的比较通用，输入输出结构都很像数据库的表==&gt;记录==&gt;字段，所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看：可以先把Lucene当成一个支持全文索引的数据库系统。</p>
<p>比较一下Lucene和数据库：</p>
<p>&nbsp;</p>
<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
    <tbody>
        <tr>
            <td>&nbsp;Lucene</td>
            <td>&nbsp;数据库</td>
        </tr>
        <tr>
            <td>
            <p>&nbsp;索引数据源：doc(field1,field2...) doc(field1,field2...)&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;\&nbsp; indexer /<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;_____________<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| Lucene Index |<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/ searcher \</p>
            <p>结果输出：Hits(doc(field1,field2) doc(field1...))</p>
            </td>
            <td>
            <p>&nbsp;索引数据源：record(field1,field2...) record(field1..)&nbsp;&nbsp;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp; SQL: insert/&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_____________<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; DB&nbsp; Index&nbsp;&nbsp; |<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -------------&nbsp; <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / SQL: select \</p>
            <p>结果输出：results(record(field1,field2..) record(field1...))</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>&nbsp;Document：一个需要进行索引的&#8220;单元,一个Document由多个字段组成 </p>
            </td>
            <td>
            <p>&nbsp;Record：记录，包含多个字段<br />
            </p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Field：字段</p>
            </td>
            <td>Field：字段</td>
        </tr>
        <tr>
            <td>
            <p>Hits：查询结果集，由匹配的Document组成</p>
            </td>
            <td>&nbsp;RecordSet：查询结果集，由多个Record组成</td>
        </tr>
    </tbody>
</table>
<p><strong></strong></p>
<p><strong>全文检索 &#8800; like "%keyword%"</strong> </p>
<p>　　由于数据库索引不是为全文索引设计的，因此，使用like "%keyword%"时，数据库索引是不起作用的，在使用like查询时，搜索过程又变成类似于一页页翻书的遍历过程了，所以对于含有模糊查询的数据库服务来说，LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配：like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。</p>
<p>　　通常比较厚的书籍后面常常附关键词索引表（比如：北京：12, 34页，上海：3,77页&#8230;&#8230;），它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样，想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍&#8230;&#8230;而索引之所以效率高，另外一个原因是它是排好序的。对于检索系统来说核心是一个排序问题。</p>
<p>　　所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制，将数据源（比如多篇文章）排序顺序存储的同时，有另外一个排好序的关键词列表，用于存储关键词==&gt;文章映射关系，利用这样的映射关系索引：[关键词==&gt;出现关键词的文章编号，出现次数（甚至包括位置：起始偏移量，结束偏移量），出现频率]，检索过程就是把模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程。从而大大提高了多关键词查询的效率，所以，全文检索问题归结到最后是一个排序问题。</p>
<p>　　由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题，这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制，并提供了扩展接口，以方便针对不同应用的定制。</p>
<p>　　可以通过一下表格对比一下数据库的模糊查询：</p>
<p>&nbsp;</p>
<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
    <tbody>
        <tr>
            <td>&nbsp;</td>
            <td>&nbsp;Lucene全文索引引擎</td>
            <td>&nbsp;数据库</td>
        </tr>
        <tr>
            <td>&nbsp;索引</td>
            <td>&nbsp;将数据源中的数据都通过全文索引一一建立反向索引</td>
            <td>&nbsp;对于LIKE查询来说，数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配，比有索引的搜索速度要有多个数量级的下降。</td>
        </tr>
        <tr>
            <td>&nbsp;匹配效果</td>
            <td>&nbsp;通过词元(term)进行匹配，通过语言分析接口的实现，可以实现对中文等非英语的支持。</td>
            <td>&nbsp;使用：like "%net%" 会把netherlands也匹配出来，<br />
            多个关键词的模糊匹配：使用like "%com%net%"：就不能匹配词序颠倒的xxx.net..xxx.com</td>
        </tr>
        <tr>
            <td>&nbsp;匹配度</td>
            <td>&nbsp;有匹配度算法，将匹配程度（相似度）比较高的结果排在前面。</td>
            <td>&nbsp;没有匹配程度的控制：比如有记录中net出现5词和出现1次的，结果是一样的</td>
        </tr>
        <tr>
            <td>&nbsp;结果输出</td>
            <td>&nbsp;通过特别的算法，将最匹配度最高的头100条结果输出，结果集是缓冲式的小批量读取的。</td>
            <td>&nbsp;返回所有的结果集，在匹配条目非常多的时候（比如上万条）需要大量的内存存放这些临时结果集。</td>
        </tr>
        <tr>
            <td>&nbsp;可定制性</td>
            <td>&nbsp;通过不同的语言分析接口实现，可以方便的定制出符合应用需要的索引规则（包括对中文的支持）</td>
            <td>&nbsp;没有接口或接口复杂，无法定制</td>
        </tr>
        <tr>
            <td>&nbsp;结论</td>
            <td>&nbsp;高负载的模糊查询应用，需要负责的模糊查询的规则，索引的资料量比较大</td>
            <td>&nbsp;使用率低，模糊匹配规则简单或者需要模糊查询的资料量少</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>&nbsp;</p>
<table cellspacing="1" cellpadding="1" width="100%" align="center" border="1">
    <tbody>
        <tr>
            <td>&nbsp;</td>
            <td>&nbsp;Lucene</td>
            <td>&nbsp;其他开源全文检索系统</td>
        </tr>
        <tr>
            <td>&nbsp;增量索引和批量索引</td>
            <td>&nbsp;可以进行增量的索引(Append)，可以对于大量数据进行批量索引，并且接口设计用于优化批量索引和小批量的增量索引。</td>
            <td>&nbsp;很多系统只支持批量的索引，有时数据源有一点增加也需要重建索引。</td>
        </tr>
        <tr>
            <td>&nbsp;数据源</td>
            <td>&nbsp;Lucene没有定义具体的数据源，而是一个文档的结构，因此可以非常灵活的适应各种应用（只要前端有合适的转换器把数据源转换成相应结构）。</td>
            <td>&nbsp;很多系统只针对网页，缺乏其他格式文档的灵活性。</td>
        </tr>
        <tr>
            <td>&nbsp;索引内容抓取</td>
            <td>&nbsp;Lucene的文档是由多个字段组成的，甚至可以控制那些字段需要进行索引，那些字段不需要索引，近一步索引的字段也分为需要分词和不需要分词的类型：<br />
            &nbsp;&nbsp; 需要进行分词的索引，比如：标题，文章内容字段<br />
            &nbsp;&nbsp; 不需要进行分词的索引，比如：作者/日期字段</td>
            <td>&nbsp;缺乏通用性，往往将文档整个索引了</td>
        </tr>
        <tr>
            <td>&nbsp;语言分析</td>
            <td>&nbsp;通过语言分析器的不同扩展实现：<br />
            可以过滤掉不需要的词：an the of 等，<br />
            西文语法分析：将jumps jumped jumper都归结成jump进行索引/检索<br />
            非英文支持：对亚洲语言，阿拉伯语言的索引支持</td>
            <td>&nbsp;缺乏通用接口实现</td>
        </tr>
        <tr>
            <td>&nbsp;查询分析</td>
            <td>&nbsp;通过查询分析接口的实现，可以定制自己的查询语法规则：<br />
            比如： 多个关键词之间的 + - and or关系等</td>
            <td>&nbsp;功能较强大</td>
        </tr>
        <tr>
            <td>&nbsp;并发访问</td>
            <td>&nbsp;能够支持多用户的使用</td>
            <td>&nbsp;功能较强大</td>
        </tr>
    </tbody>
</table>
<p><strong>关于亚洲语言的的切分词问题(Word Segment)</strong> <br />
　　对于中文来说，全文索引首先还要解决一个语言分析的问题，对于英文来说，语句中单词之间是天然通过空格分开的，但亚洲语言的中日韩文语句中的字是一个字挨一个，所有，首先要把语句中按&#8220;词&#8221;进行索引的话，这个词如何切分出来就是一个很大的问题。<br />
　　首先，肯定不能用单个字符作(si-gram)为索引单元，否则查&#8220;上海&#8221;时，不能让含有&#8220;海上&#8221;也匹配。<br />
但一句话：&#8220;北京天安门&#8221;，计算机如何按照中文的语言习惯进行切分呢？<br />
　　&#8220;北京 天安门&#8221; 还是&#8220;北 京 天安门&#8221;？让计算机能够按照语言习惯进行切分，往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。<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>&nbsp;</td>
            <td>&nbsp;自动切分</td>
            <td>&nbsp;词表切分</td>
        </tr>
        <tr>
            <td>&nbsp;实现</td>
            <td>&nbsp;实现非常简单</td>
            <td>&nbsp;实现复杂</td>
        </tr>
        <tr>
            <td>&nbsp;查询</td>
            <td>&nbsp;增加了查询分析的复杂程度</td>
            <td>&nbsp;适于实现比较复杂的查询语法规则</td>
        </tr>
        <tr>
            <td>&nbsp;存储效率</td>
            <td>&nbsp;索引冗余大，索引几乎和原文一样大</td>
            <td>&nbsp;索引效率高，为原文大小的30％左右</td>
        </tr>
        <tr>
            <td>&nbsp;维护成本</td>
            <td>&nbsp;无词表维护成本</td>
            <td>&nbsp;词表维护成本非常高：中日韩等语言需要分别维护。<br />
            还需要包括词频统计等内容</td>
        </tr>
        <tr>
            <td>&nbsp;适用领域</td>
            <td>&nbsp;嵌入式系统：运行环境资源有限<br />
            分布式系统：无词表同步问题<br />
            多语言环境：无词表维护成本</td>
            <td>&nbsp;对查询和存储效率要求高的专业搜索引擎<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>&nbsp;org.apache.Lucene.search/</td>
            <td>&nbsp;搜索入口</td>
        </tr>
        <tr>
            <td>&nbsp;org.apache.Lucene.index/</td>
            <td>&nbsp;索引入口</td>
        </tr>
        <tr>
            <td>&nbsp;org.apache.Lucene.analysis/</td>
            <td>&nbsp;语言分析器</td>
        </tr>
        <tr>
            <td>&nbsp;org.apache.Lucene.queryParser/</td>
            <td>查询分析器</td>
        </tr>
        <tr>
            <td>&nbsp;org.apache.Lucene.document/</td>
            <td>&nbsp;存储结构</td>
        </tr>
        <tr>
            <td>&nbsp;org.apache.Lucene.store/&nbsp;</td>
            <td>&nbsp;底层IO/存储结构</td>
        </tr>
        <tr>
            <td>&nbsp;org.apache.Lucene.util/</td>
            <td>&nbsp;一些公用的数据结构</td>
        </tr>
    </tbody>
</table>
<p><strong>从Lucene学到更多：</strong> <br />
　　Luene的确是一个面对对象设计的典范。</p>
<ol>
    <li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要；
    <li>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务；
    <li>所有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修改其他模块。&nbsp;
    <li>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。 </li>
</ol>
<p><br />
这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。<br />
　　此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</p>
<ol>
    <li>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。
    <li>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的。
    <li>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。
    <li>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的操作。 </li>
</ol>
<p>本文转自：<a href="http://www.chedong.com/tech/lucene.html">http://www.chedong.com/tech/lucene.html</a></p>
</div>
<img src ="http://www.blogjava.net/szhswl/aggbug/165817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-06 15:57 <a href="http://www.blogjava.net/szhswl/articles/165817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>lucene全文检索应用示例及代码简析</title><link>http://www.blogjava.net/szhswl/articles/165592.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Wed, 05 Dec 2007 09:08:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/165592.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/165592.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/165592.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/165592.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/165592.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 使用Lucene实现全文检索，主要有下面三个步骤：　　1、建立索引库：根据网站新闻信息库中的已有的数据资料建立Lucene索引文件。　　2、通过索引库搜索：有了索引后，即可使用标准的词法分析器或直接的词法分析器实现进行全文检索。　　3、维护索引库：网站新闻信息库中的信息会不断的变动，包括新增、修改及删除等，这些信息的变动都需要进一步反映到Lucene索引文件中。&nbsp;&nbs...&nbsp;&nbsp;<a href='http://www.blogjava.net/szhswl/articles/165592.html'>阅读全文</a><img src ="http://www.blogjava.net/szhswl/aggbug/165592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-05 17:08 <a href="http://www.blogjava.net/szhswl/articles/165592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LUCENE简单实例</title><link>http://www.blogjava.net/szhswl/articles/165589.html</link><dc:creator>宋针还</dc:creator><author>宋针还</author><pubDate>Wed, 05 Dec 2007 09:04:00 GMT</pubDate><guid>http://www.blogjava.net/szhswl/articles/165589.html</guid><wfw:comment>http://www.blogjava.net/szhswl/comments/165589.html</wfw:comment><comments>http://www.blogjava.net/szhswl/articles/165589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/szhswl/comments/commentRss/165589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/szhswl/services/trackbacks/165589.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: lucene的简单实例&lt;一&gt;关键字:&nbsp;&nbsp; lucene&nbsp;&nbsp;&nbsp;&nbsp; 说明一下,这一篇文章的用到的lucene,是用2.0版本的,主要在查询的时候2.0版本的lucene与以前的版本有了一些区别. 其实这一些代码都是早几个月写的,自己很懒,所以到今天才写到自己的博客上,高深的文章自己写不了，只能记录下一些简单的记录与...&nbsp;&nbsp;<a href='http://www.blogjava.net/szhswl/articles/165589.html'>阅读全文</a><img src ="http://www.blogjava.net/szhswl/aggbug/165589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/szhswl/" target="_blank">宋针还</a> 2007-12-05 17:04 <a href="http://www.blogjava.net/szhswl/articles/165589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>