﻿<?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-自然-文章分类-search</title><link>http://www.blogjava.net/masen/category/22936.html</link><description>从现在开始</description><language>zh-cn</language><lastBuildDate>Thu, 31 May 2007 13:42:10 GMT</lastBuildDate><pubDate>Thu, 31 May 2007 13:42:10 GMT</pubDate><ttl>60</ttl><item><title> Lucene 学习（转）</title><link>http://www.blogjava.net/masen/articles/121128.html</link><dc:creator>Masen</dc:creator><author>Masen</author><pubDate>Thu, 31 May 2007 04:56:00 GMT</pubDate><guid>http://www.blogjava.net/masen/articles/121128.html</guid><wfw:comment>http://www.blogjava.net/masen/comments/121128.html</wfw:comment><comments>http://www.blogjava.net/masen/articles/121128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/masen/comments/commentRss/121128.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/masen/services/trackbacks/121128.html</trackback:ping><description><![CDATA[通过这几天的看书和学习，对 Lucene 有了更进一步的认识，所以总结一下这些天的学习成果把 Lucene 的学习心得也学出来。 <o:p></o:p>
<h1>1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lucene 的认识 <o:p></o:p></h1>
<p style="FONT-SIZE: 14pt">提到 Lucene 很多人都知道这个开源的搜索工具，其魅力也是很大的。它让我们对搜索引擎的认识不在那么神秘，也不会在觉得百度和 google 的技术多么的高深没测，其实其原理都是一样的，只是他们要做的更好，走的更远罢了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Lucene 可以对任何的数据做索引和搜索，说这样的话其实不过分，真的就是这样，只要你能处理好这些数据，交给 Lucene 去建立索引它都可以帮你把这些数据给检索出来，是不是很好玩了。真正好玩的地方还在后面呢。 <o:p></o:p></p>
<h1>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lucene 的学习 <o:p></o:p></h1>
<p style="FONT-SIZE: 14pt">前面已经对 Lucene 有了一些了解，现在我们想象它怎么去搜索这些数据呢，如果知道倒排索引，你就知道了，其实 lucene 检索的是它自己建立的索引，从索引中的到数据的指针，从而得到数据。其实就这么简单。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">提到索引，现在的索引技术中有：倒排索引、后缀数组和签名文件这三种，其中后缀数组这种技术虽然检索速度也很快，但是它的数据结构构造和维护都是相当麻烦的所以不可取了。我也懒得去看了。至于签名文件嘛，那是 80 年代的玩意了，现在已经过时了。现在可是倒排索引的天下啊！相信百度和 google 都是这种技术。 <o:p></o:p></p>
<h1>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 索引的建立 <o:p></o:p></h1>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">我们从索引的建立入手： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">我们建立一个 lucene 的索引时必须先建立该索引文件存放的位置，看一下代码： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">IndexWriter writer = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">writer = new IndexWriter("c:\\index", new CJKAnalyzer(), true);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">这段代码就时建立一个索引前所必须的操作，先声明这个 IndexWriter ，实例化它你必须传入三个参数。他们分别代表：你要建立索引文件的存放位置、你要使用索引建立的分词方法、是否重新建立索引。这样你就告诉 lucene 我要在 c 盘的 index 目录下建立索引文件，我要使用<st1:personname w:st="on" productid="车东">车东</st1:personname>老师的二分词算法做分析器、我要在这个目录下删除以前的索引或任何文件创立我的索引文件。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">索引的建立有三种方式，让我一一道来： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">1 、 new IndexWriter(new RAMDirectory(), new StandardAnalyzer(), true);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">在内存中建立索引，速度最快但是耗资源，而且重启就没了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">2 、 new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">在文件系统中建立索引，这里有两个参数，分别是：建立索引的路径、是否要删除当前目录下的文件重新建立索引。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">3 、 new IndexWriter("c:\\index", new CJKAnalyzer(), true);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">最常见的一种，在制定目录下建立索引，看了源码你就知道这种方法也是用的第二种方式。 Lucene 的源码： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">public IndexWriter(String path, Analyzer a, boolean create)<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IOException {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;this(FSDirectory.getDirectory(path, create), a, create, true);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp; }<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">我想的没错。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Indexwriter 性能调整参数： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第一个优化的参数 ： mergeFactor 这个参数用于控制 lucene 在把索引从内存写入到磁盘上的文件系统时内存最大的 Document 对象的数量。这个数要根据你的计算机设置，默认情况下是 10 。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 第二个优化的参数 ： maxMergeFactor 这个参数用来设置当有多少个 Segment 时进行合并操作。当然我们知道当索引文件太多的话其检索的速度就会很慢，所以我们要当文件数量一定时让它进行索引的合并。这样就可以加快索引速度，但是这个值要根据你的情况而定。当文档数量较多时我们将值设大些，当文档数量较少时我们将值设小些。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第三个优化的参数 ： minMergeDocs 这个参数用于控制内存中文档的数量。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">这样我们建立索引已经完成，接下来我们要建立 Document 对象，因为你必须告诉我要搜索什么吧！好了，看看源码： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">File file = new File("1.txt"); <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Document doc = new Document();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">doc.add(Field.UnIndexed("filename", file.getName()));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">FileInputStream fis = new FileInputStream(file);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">byte[] b = new byte[fis.available()];<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">fis.read(b);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">String content = new String(b);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">doc.add(Field.Text("content", content));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">fis.close();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">以上我们就完成了将 1.txt 文件放到我们的 Document 对象了。这里我们用了 Field.Text(); 这样的操作和 doc.add(); 这样的方法建立的。这也是建立索引的必须。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">稍微介绍一下 Field ，它就是你要建立索引的字段。它分别有 <o:p></o:p></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td style="FONT-SIZE: 14pt" width=208>
            <p style="FONT-SIZE: 14pt" align=center>类型 / 方法 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=52>
            <p style="FONT-SIZE: 14pt" align=center>是否分词 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=48>
            <p style="FONT-SIZE: 14pt" align=center>是否索引 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=48>
            <p style="FONT-SIZE: 14pt" align=center>是否存储 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=213>
            <p style="FONT-SIZE: 14pt" align=center>常用实例 <o:p></o:p></p>
            </td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 14pt" width=208>
            <p style="FONT-SIZE: 14pt" align=center>Keyword(String,String)<o:p></o:p> </p>
            <p style="FONT-SIZE: 14pt" align=center>Keyword(String,Date)<o:p></o:p> </p>
            </td>
            <td style="FONT-SIZE: 14pt" width=52>
            <p style="FONT-SIZE: 14pt" align=center>否 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" width=213>
            <p style="FONT-SIZE: 14pt" align=center>电话号码，身份证，人名，地名，日期 <o:p></o:p></p>
            </td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 14pt" vAlign=top width=208>
            <p style="FONT-SIZE: 14pt" align=center>Unindexed(String,String)<o:p></o:p> </p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=52>
            <p style="FONT-SIZE: 14pt" align=center>否 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>否 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=213>
            <p style="FONT-SIZE: 14pt" align=center>文档类型，文档名称 <o:p></o:p></p>
            </td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 14pt" vAlign=top width=208>
            <p style="FONT-SIZE: 14pt" align=center>UnStored(String,String)<o:p></o:p> </p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=52>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>否 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=213>
            <p style="FONT-SIZE: 14pt" align=center>文档的标题和内容 <o:p></o:p></p>
            </td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 14pt" vAlign=top width=208>
            <p style="FONT-SIZE: 14pt" align=center>Text(String,String)<o:p></o:p> </p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=52>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=213>
            <p style="FONT-SIZE: 14pt" align=center>文档的标题和内容 <o:p></o:p></p>
            </td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 14pt" vAlign=top width=208>
            <p style="FONT-SIZE: 14pt" align=center>Text(String,Reader)<o:p></o:p> </p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=52>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>是 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=48>
            <p style="FONT-SIZE: 14pt" align=center>否 <o:p></o:p></p>
            </td>
            <td style="FONT-SIZE: 14pt" vAlign=top width=213>
            <p style="FONT-SIZE: 14pt" align=center>文档的标题和内容 <o:p></o:p></p>
            </td>
        </tr>
    </tbody>
</table>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 这样我们要建什么样的索引就对号入座吧，只要最后我们使用 doc.add(Field.Text("content", content)); 把它添加到 Document 中就可以了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 这时我们的文档已经建立好了，现在就开始向索引中添加文档吧！这里我们使用 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">writer.addDocument(doc); 来向 Indexwriter 索引中添加构造好的文档。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">这样我们是不是就可以说我们已经建立完了索引呢，其实不然，我们还要优化优化，这样才快嘛！对不对？ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; writer.optimize(); 这样一句话就可以实现索引优化了，具体的优化过程我就不说了，是不是很简单。但是一定不要忘了哦。调用这个方法时最好建立一个合适的周期。定期进行优化。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 好了，这样我们就完成了索引的建立了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 下面我们看看缩影的合并吧！ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">当我们在很多地方建立了很多的索引后，想要合并这些索引我们怎么办呢？ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 使用 IndexWriter.assIndexs(New Directory[]{path});<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">就可以对 path 路径下的索引合并到当前的索引中了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 下面再看看索引的删除吧！ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 有一些过时的索引我们需要删除，怎么办呢？ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">IndexReader reader = IndexReader.open("c:\\index");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; reader.delete(0);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">这样我们就可以按照文档的顺序删除对应的文档了，但是这样不太现实，不对吗？我们怎么会知道文档的顺序呢？ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">下面我们看看第二中方法： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">IndexReader reader = IndexReader.open("c:\\index");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">reader.delete(new Term("name","word1"));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">reader.close();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">按照字段来删除对应的文档，这样合理多了。以后要删除时就按照词条的方式去删除吧 !<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">索引锁： write.lock , commit.lock.<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">write.lock 是为了避免几个线程同时修改一个索引文档而设置。当实例一个 indexwrite 时建立和使用 indexReader 删除文档时建立。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Commit.lock 该锁主要在 segment 在建立，合并或读取时生成。 <o:p></o:p></p>
<h1>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lucene 的搜索 <o:p></o:p></h1>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">以上完成了索引的建立和一些关于索引的知识，但是光有索引是不行的，我们真正要做的检索，这才是我们的关键。现在我们看看 lucene 的检索吧。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">认识检索从检索的工具开始吧！ IndexSearcher 类是 lucene 用于检索的工具类，我们在检索之前要得到这个类的实例。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第一步我们看以下代码： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">IndexSearcher searcher = new IndexSearcher("c:\\index");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">创建 IndexSearcher 实例需要告诉 lucene 索引的位置，就是你 IndexWrite 的文件路径。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Query query = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">Hits hits = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">query = QueryParser.parse(key1, "name", new StandardAnalyzer());<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">if (hits != null) {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (hits.length() == 0) {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(" 没有找到任何结果 ");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(" 找到 ");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; hits.length(); i++) {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Document d = hits.doc(i);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String dname = d.get("title");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(dname + "&nbsp;&nbsp; " );<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">}<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">以上就是一个完整的检索过程，这里我们看见了个 Query 和 Hits ，这两个类就是比较关键的了，我们先从检索结果的 Hits 类说起。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">我们使用 Hits 经常使用的几个方法有： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">length() :&nbsp; 返回搜索结果的总数量。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Doc(int n) : 放回第 n 个文档。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Id(int n) : 返回第 n 个文档的内部编号。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Sorce(int n) : 返回第 n 个文档的得分。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">看见这个 Sorce(int n) 这个方法，是不是就可以联想到搜索引擎的排序问题呢，像百度的推广是怎么做出来的呢 , 可想而知吧，那就说明必定存在一中方法可以动态的改变某片文档的得分。对了， lucene 中可以使用 Document 的 setBoost 方法可以改变当前文档的 boost 因子。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">下面我们看看： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Document doc1 = new Document();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">doc1.add(Field.Text("contents", "word1 word"));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">doc1.add(Field.Keyword("path", "path\\document1.txt"));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;doc1.setBoost(<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="F">1.0f</st1:chmetcnv>);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp; 这样我们就在改变了篇文档的评分了，当 boost 的值越大它的分值就越高，其出现的位置就越靠前。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">让我们再来看看 lucene 为我们提供的各种 Query 吧。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第一、&nbsp;&nbsp; 按词条搜索 － TermQuery <br>query = new TermQuery(new Term("name","word1"));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">这样就可以把 field 为 name 的所有包含 word1 的文档检索出来了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第二、&nbsp; &#8220;与或&#8221;搜索 － BooleanQuery<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">它实际是一个组合 query 看看下面的代码： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">query1 = new TermQuery(new Term("name","word1"));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">query2 = new TermQuery(new Term("name","word2"));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">query = new BooleanQuery();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">query.add(query1, false, false);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">query.add(query2, false, false);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">看看 booleanQuery 的用法吧： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">true &amp; true : 表明当前加入的字句是必须要满足的。相当于逻辑与。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">false &amp; true : 表明当前加入的字句是不可一被满足的， 相当于逻辑非。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">false &amp; false : 表明当前加入的字句是可选的，相当于逻辑或。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">true &amp; true : 错误的情况。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Lucene 可以最多支持连续 1024 的 query 的组合。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第三、&nbsp; 在某一范围内搜索 － RangeQuery<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">IndexSearcher searcher = new IndexSearcher("c:\\index");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp; Term beginTime = new Term("time","200001");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp; Term endTime = new Term("time","200005");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp; Hits hits = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp; RangeQuery query = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp; query = new RangeQuery(beginTime, endTime, false);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">RangeQuery 的构造函数的参数分别代表起始、结束、是否包括边界。这样我们就可以按照要求检索了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第四、&nbsp; 使用前缀检索 － PrefixQuery<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">这个检索的机制有点类似于 indexOf() 从前缀查找。这个常在英文中使用，中文中就很少使用了。代码如下： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">IndexSearcher searcher = new IndexSearcher("c:\\index");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term pre1 = new Term("name", "Da");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = new PrefixQuery(pre1);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">第五、&nbsp; 多关键字的搜索 － PhraseQuery<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">可以多个关键字同时查询。使用如下： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">query = new PhraseQuery();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.add(word1);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.add(word2);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.setSlop(0);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, "'david' 与 'mary' 紧紧相隔的 Document");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.setSlop(2);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, "'david' 与 'mary' 中相隔两个词的短语 ");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 这里我们要注意 query.setSlop(); 这个方法的含义。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">query.setSlop(0);&nbsp; 紧紧相连 （这个的条件比较苛刻） <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">query.setSlop(2);&nbsp; 相隔 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">第六、&nbsp; 使用短语缀搜索 － PharsePrefixQuery <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">使用 PharsePrefixQuery 可以很容易的实现相关短语的检索功能。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">实例： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">query = new PhrasePrefixQuery();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 加入可能的所有不确定的词 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Term word1 = new Term("content", "david");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word2 = new Term("content", "mary");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word3 = new Term("content", "smith");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word4 = new Term("content", "robert");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.add(new Term[]{word1, word2});<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 加入确定的词 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.add(word4);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query.setSlop(2);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, " 存在短语 'david robert' 或 'mary robert' 的文档 ");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">第七、&nbsp; 相近词语的搜索 － fuzzyQuery<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">可以通俗的说它是一种模糊查询。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">实例： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Term word1 = new Term("content", "david");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hits hits = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FuzzyQuery query = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = new FuzzyQuery(word1);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits," 与 'david' 相似的词 ");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">第八、&nbsp; 使用通配符搜索 － WildcardQuery<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">实例： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">IndexSearcher searcher = new IndexSearcher("c:\\index");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word1 = new Term("content", "*ever");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word2 = new Term("content", "wh?ever");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word3 = new Term("content", "h??ever");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Term word4 = new Term("content", "ever*");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WildcardQuery query = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hits hits = null;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = new WildcardQuery(word1);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, "*ever");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = new WildcardQuery(word2);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, "wh?ever");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = new WildcardQuery(word3);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, "h??ever");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = new WildcardQuery(word4);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printResult(hits, "ever*");<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; 由上可以看出通配符？代便 1 个字符， * 代表 0 到多个字符。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Lucene 现在支持以上八中的搜索方式，我们可以根据需要选择适合自己的搜索方式。当然上面提供的一些可能对英文还是比较有效，中文就不可取了，所以我们开始想想百度，我们只在一个输入框中搜索结果。有了这个疑问我们揭开下一章的讨论吧！ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">查询字符串的解析：这个就是我们经常在一个输入框中输入我们要检索的文字，交给搜索引擎去帮我们分词。 </p>
<p style="FONT-SIZE: 14pt">QueryParser 类就是对查询字符串的解析类。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">看看它的用法： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">query = QueryParser.parse(key1, "name", new StandardAnalyzer());<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">它直接返回一个 Query 对象。需要传入的参数分别是： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">用户需要查询的字符串、需要检索的对应字段名称、采用的分词类。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Analyzer analyzer = new CJKAnalyzer();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">String[] fields = {"filename", "content"};<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">Query query = MultiFieldQueryParser.parse(searchword, fields, analyzer);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">Hits hits = searcher.search(query);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">QueryParser 的&#8220;与&#8221; 和 &#8220;或&#8221;： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">QueryParser 之间默认是或，我们想改变为与的话加入以下代码： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">QueryParser.setOperator(QueryParser.DEFAULT_OPERATOR_AND);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">就可以了。 <o:p></o:p></p>
<h1>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 高级搜索技巧 <o:p></o:p></h1>
<p style="FONT-SIZE: 14pt">前面我们已经介绍了一般情况下 lucene 的使用技巧，现在我们探讨一下高级搜索的技巧吧！ <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">1、 对搜索结果进行排序： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">1） 使用 sort 类排序： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; Sort sort = new Sort();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; hits = searcher.search(query,sort);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">这种方式是使用默认的 sort 排序方式进行排序。默认的 sort 排序是按照相关度进行排序。即通过 luence 的评分机制进行排序。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">2) 对某一字段进行排序 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sort sort = new Sort( &#8220; content &#8221; );<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp; &nbsp;&nbsp;&nbsp; hits = searcher.search(query,sort);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">3) 对多个字段进行排序 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Sort sort = new Sort(new SortField[]{new SortField("title"),new SortField("contents")});<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">hits = searcher.search(query,sort);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">2、 多域搜索和多索引搜索： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">在使用 luecene 时，如果查询的只是某些 terms ，而不关心这些词条到时来自那个字段中时。这时可以使用 MultiFieldQueryParser 类。这个用于用户搜索含有某个关键字是否存在在字段中，他们之间的关系使用 OR 连接。即不管存在在哪一个字段都会显示显示出来。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">使用 MultiSearcher 可以满足同时多索引的搜索需求。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Searcher[] searchers = new Searcher[2]; <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">searchers[0] = new IndexSearcher(indexStoreB);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">searchers[1] = new IndexSearcher(indexStoreA);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 创建一个多索引检索器 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Searcher mSearcher = new MultiSearcher(searchers);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">3、 &nbsp;&nbsp;&nbsp; 对搜索结果进行过滤： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">1)&nbsp;&nbsp;&nbsp; 对时间进行过滤 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通常情况下我们对搜索结果要进行过滤显示，即只显示过滤后的结果。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">doc.add(Field.Keyword("datefield", DateField.timeToString(now - 1000)));<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">DateFilter df1 = DateFilter.Before("datefield", now);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">2)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 查询过滤器 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">通过查询过滤器可以过滤一部分的信息。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">Filter filter = new Filter() <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; public BitSet bits (IndexReader reader) throws IOException <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BitSet bitset = new BitSet(5);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitset.set (1);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitset.set (3);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return bitset;<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }; <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 生成带有过滤器的查询对象 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Query filteredquery = new FilteredQuery (query, filter);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 返回检索结果 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Hits hits = searcher.search (filteredquery);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt"><o:p>&nbsp;</o:p> </p>
<p style="FONT-SIZE: 14pt">这样我们就可以使用自己定义的过滤方式去过滤信息了。 <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">3)&nbsp;&nbsp;&nbsp; 带缓存的过滤器： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">使用待缓存的过滤器我们可以重用过滤功能，如下： <o:p></o:p></p>
<p style="FONT-SIZE: 14pt">MockFilter filter = new MockFilter();<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;CachingWrapperFilter cacher = new CachingWrapperFilter(filter);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; cacher.bits(reader);<o:p></o:p> </p>
<p style="FONT-SIZE: 14pt">以上介绍完了现在学习 luence ，没有太详细的介绍它的实现，因为它对于我们来说是一个工具，既然是工具我们就要会用就可以了。 </p>
<img src ="http://www.blogjava.net/masen/aggbug/121128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/masen/" target="_blank">Masen</a> 2007-05-31 12:56 <a href="http://www.blogjava.net/masen/articles/121128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Lucene 索引机制 </title><link>http://www.blogjava.net/masen/articles/121115.html</link><dc:creator>Masen</dc:creator><author>Masen</author><pubDate>Thu, 31 May 2007 03:52:00 GMT</pubDate><guid>http://www.blogjava.net/masen/articles/121115.html</guid><wfw:comment>http://www.blogjava.net/masen/comments/121115.html</wfw:comment><comments>http://www.blogjava.net/masen/articles/121115.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/masen/comments/commentRss/121115.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/masen/services/trackbacks/121115.html</trackback:ping><description><![CDATA[<div class=postTitle><a class=postTitle2 id=viewpost1_TitleUrl href="http://www.blogjava.net/buaacaptain/archive/2006/08/01/61256.html"><u><font color=#0000ff>深入 Lucene 索引机制</font></u></a> </div>
<blockquote>
<p>Lucene 是一个基于 Java 的全文检索工具包，你可以利用它来为你的应用程序加入索引和检索功能。Lucene 目前是著名的 Apache Jakarta 家族中的一个开源项目，下面我们即将学习 Lucene 的索引机制以及它的索引文件的结构。</p>
<p>在这篇文章中，我们首先演示如何使用 Lucene 来索引文档，接着讨论如何提高索引的性能。最后我们来分析 Lucene 的索引文件结构。需要记住的是，Lucene 不是一个完整的应用程序，而是一个信息检索包，它方便你为你的应用程序添加索引和搜索功能。</p>
</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><a name=N10047><span class=atitle><font face=Arial size=4>架构概览</font> </span></a></p>
<p>图一显示了 Lucene 的索引机制的架构。Lucene 使用各种解析器对各种不同类型的文档进行解析。比如对于 HTML 文档，HTML 解析器会做一些预处理的工作，比如过滤文档中的 HTML 标签等等。HTML 解析器的输出的是文本内容，接着 Lucene 的分词器(Analyzer)从文本内容中提取出索引项以及相关信息，比如索引项的出现频率。接着 Lucene 的分词器把这些信息写到索引文件中。</p>
<br><a name=N10052><strong>图一：Lucene 索引机制架构</strong> </a><br><img height=345 alt="图一：Lucene 索引机制架构" src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/image002.jpg" width=438 border=0> <br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10062><span class=atitle><font face=Arial size=4>用Lucene索引文档</font> </span></a></p>
<p>接下来我将一步一步的来演示如何利用 Lucene 为你的文档创建索引。只要你能将要索引的文件转化成文本格式，Lucene 就能为你的文档建立索引。比如，如果你想为 HTML 文档或者 PDF 文档建立索引，那么首先你就需要从这些文档中提取出文本信息，然后把文本信息交给 Lucene 建立索引。我们接下来的例子用来演示如何利用 Lucene 为后缀名为 txt 的文件建立索引。</p>
<p>1． 准备文本文件</p>
<p>首先把一些以 txt 为后缀名的文本文件放到一个目录中，比如在 Windows 平台上，你可以放到 C:\\files_to_index 下面。</p>
<p>2． 创建索引</p>
<p>清单1是为我们所准备的文档创建索引的代码。</p>
<br><a name=N10077><strong>清单1：用 Lucene 索引你的文档</strong> </a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>package lucene.index;
            import java.io.File;
            import java.io.FileReader;
            import java.io.Reader;
            import java.util.Date;
            import org.apache.lucene.analysis.Analyzer;
            import org.apache.lucene.analysis.standard.StandardAnalyzer;
            import org.apache.lucene.document.Document;
            import org.apache.lucene.document.Field;
            import org.apache.lucene.index.IndexWriter;
            /**
            * This class demonstrates the process of creating an index with Lucene
            * for text files in a directory.
            */
            public class TextFileIndexer {
            public static void main(String[] args) throws Exception{
            //fileDir is the directory that contains the text files to be indexed
            File   fileDir  = new File("C:\\files_to_index ");
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            File[] textFiles  = fileDir.listFiles();
            long startTime = new Date().getTime();
            //Add documents to the index
            for(int i = 0; i &lt; textFiles.length; i++){
            if(textFiles[i].isFile() &gt;&gt; textFiles[i].getName().endsWith(".txt")){
            System.out.println("File " + textFiles[i].getCanonicalPath()
            + " is being indexed");
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Text("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("It took " + (endTime - startTime)
            + " milliseconds to create an index for the files in the directory "
            + fileDir.getPath());
            }
            }
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>正如清单1所示，你可以利用 Lucene 非常方便的为文档创建索引。接下来我们分析一下清单1中的比较关键的代码，我们先从下面的一条语句开始看起。</p>
<br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>Analyzer luceneAnalyzer = new StandardAnalyzer();
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>这条语句创建了类 StandardAnalyzer 的一个实例，这个类是用来从文本中提取出索引项的。它只是抽象类 Analyzer 的其中一个实现。Analyzer 也有一些其它的子类，比如 SimpleAnalyzer 等。</p>
<p>我们接着看另外一条语句：</p>
<br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>这条语句创建了类 IndexWriter 的一个实例，该类也是 Lucene 索引机制里面的一个关键类。这个类能创建一个新的索引或者打开一个已存在的索引并为该所引添加文档。我们注意到该类的构造函数接受三个参数，第一个参数指定了存储索引文件的路径。第二个参数指定了在索引过程中使用什么样的分词器。最后一个参数是个布尔变量，如果值为真，那么就表示要创建一个新的索引，如果值为假，就表示打开一个已经存在的索引。</p>
<p>接下来的代码演示了如何添加一个文档到索引文件中。</p>
<br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Text("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>首先第一行创建了类 Document 的一个实例，它由一个或者多个的域(Field)组成。你可以把这个类想象成代表了一个实际的文档，比如一个 HTML 页面，一个 PDF 文档，或者一个文本文件。而类 Document 中的域一般就是实际文档的一些属性。比如对于一个 HTML 页面，它的域可能包括标题，内容，URL 等。我们可以用不同类型的 Field 来控制文档的哪些内容应该索引，哪些内容应该存储。如果想获取更多的关于 Lucene 的域的信息，可以参考 Lucene 的帮助文档。代码的第二行和第三行为文档添加了两个域，每个域包含两个属性，分别是域的名字和域的内容。在我们的例子中两个域的名字分别是"content"和"path"。分别存储了我们需要索引的文本文件的内容和路径。最后一行把准备好的文档添加到了索引当中。</p>
<p>当我们把文档添加到索引中后，不要忘记关闭索引，这样才保证 Lucene 把添加的文档写回到硬盘上。下面的一句代码演示了如何关闭索引。</p>
<br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>indexWriter.close();
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>利用清单1中的代码，你就可以成功的将文本文档添加到索引中去。接下来我们看看对索引进行的另外一种重要的操作，从索引中删除文档。</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N100BD><span class=atitle><font face=Arial size=4>从索引中删除文档</font> </span></a></p>
<p>类IndexReader负责从一个已经存在的索引中删除文档，如清单2所示。</p>
<br><a name=N100C6><strong>清单2：从索引中删除文档</strong> </a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>File   indexDir = new File("C:\\luceneIndex");
            IndexReader ir = IndexReader.open(indexDir);
            ir.delete(1);
            ir.delete(new Term("path","C:\\file_to_index\lucene.txt"));
            ir.close();
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>在清单2中，第二行用静态方法 IndexReader.open(indexDir) 初始化了类 IndexReader 的一个实例，这个方法的参数指定了索引的存储路径。类 IndexReader 提供了两种方法去删除一个文档，如程序中的第三行和第四行所示。第三行利用文档的编号来删除文档。每个文档都有一个系统自动生成的编号。第四行删除了路径为"C:\\file_to_index\lucene.txt"的文档。你可以通过指定文件路径来方便的删除一个文档。值得注意的是虽然利用上述代码删除文档使得该文档不能被检索到，但是并没有物理上删除该文档。Lucene 只是通过一个后缀名为 .delete 的文件来标记哪些文档已经被删除。既然没有物理上删除，我们可以方便的把这些标记为删除的文档恢复过来，如清单 3 所示，首先打开一个索引，然后调用方法 ir.undeleteAll() 来完成恢复工作。</p>
<br><a name=N100D3><strong>清单3：恢复已删除文档</strong> </a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>File   indexDir = new File("C:\\luceneIndex");
            IndexReader ir = IndexReader.open(indexDir);
            ir.undeleteAll();
            ir.close();
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>你现在也许想知道如何物理上删除索引中的文档，方法也非常简单。清单 4 演示了这个过程。</p>
<br><a name=N100E0><strong>清单4：如何物理上删除文档</strong> </a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,false);
            indexWriter.optimize();
            indexWriter.close();
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>在清单 4 中，第三行创建了类 IndexWriter 的一个实例，并且打开了一个已经存在的索引。第 4 行对索引进行清理，清理过程中将把所有标记为删除的文档物理删除。</p>
<p>Lucene 没有直接提供方法对文档进行更新，如果你需要更新一个文档，那么你首先需要把这个文档从索引中删除，然后把新版本的文档加入到索引中去。</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N100F0><span class=atitle><font face=Arial size=4>提高索引性能</font> </span></a></p>
<p>利用 Lucene，在创建索引的工程中你可以充分利用机器的硬件资源来提高索引的效率。当你需要索引大量的文件时，你会注意到索引过程的瓶颈是在往磁盘上写索引文件的过程中。为了解决这个问题, Lucene 在内存中持有一块缓冲区。但我们如何控制 Lucene 的缓冲区呢？幸运的是，Lucene 的类 IndexWriter 提供了三个参数用来调整缓冲区的大小以及往磁盘上写索引文件的频率。</p>
<p>1．合并因子（mergeFactor）</p>
<p>这个参数决定了在 Lucene 的一个索引块中可以存放多少文档以及把磁盘上的索引块合并成一个大的索引块的频率。比如，如果合并因子的值是 10，那么当内存中的文档数达到 10 的时候所有的文档都必须写到磁盘上的一个新的索引块中。并且，如果磁盘上的索引块的隔数达到 10 的话，这 10 个索引块会被合并成一个新的索引块。这个参数的默认值是 10，如果需要索引的文档数非常多的话这个值将是非常不合适的。对批处理的索引来讲，为这个参数赋一个比较大的值会得到比较好的索引效果。</p>
<p>2．最小合并文档数</p>
<p>这个参数也会影响索引的性能。它决定了内存中的文档数至少达到多少才能将它们写回磁盘。这个参数的默认值是10，如果你有足够的内存，那么将这个值尽量设的比较大一些将会显著的提高索引性能。</p>
<p>3．最大合并文档数</p>
<p>这个参数决定了一个索引块中的最大的文档数。它的默认值是 Integer.MAX_VALUE，将这个参数设置为比较大的值可以提高索引效率和检索速度，由于该参数的默认值是整型的最大值，所以我们一般不需要改动这个参数。</p>
<p>清单 5 列出了这个三个参数用法，清单 5 和清单 1 非常相似，除了清单 5 中会设置刚才提到的三个参数。</p>
<br><a name=N1010E><strong>清单5：提高索引性能</strong> </a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>/**
            * This class demonstrates how to improve the indexing performance
            * by adjusting the parameters provided by IndexWriter.
            */
            public class AdvancedTextFileIndexer  {
            public static void main(String[] args) throws Exception{
            //fileDir is the directory that contains the text files to be indexed
            File   fileDir  = new File("C:\\files_to_index");
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            File[] textFiles  = fileDir.listFiles();
            long startTime = new Date().getTime();
            int mergeFactor = 10;
            int minMergeDocs = 10;
            int maxMergeDocs = Integer.MAX_VALUE;
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            indexWriter.mergeFactor = mergeFactor;
            indexWriter.minMergeDocs = minMergeDocs;
            indexWriter.maxMergeDocs = maxMergeDocs;
            //Add documents to the index
            for(int i = 0; i &lt; textFiles.length; i++){
            if(textFiles[i].isFile() &gt;&gt; textFiles[i].getName().endsWith(".txt")){
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Keyword("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("MergeFactor: " + indexWriter.mergeFactor);
            System.out.println("MinMergeDocs: " + indexWriter.minMergeDocs);
            System.out.println("MaxMergeDocs: " + indexWriter.maxMergeDocs);
            System.out.println("Document number: " + textFiles.length);
            System.out.println("Time consumed: " + (endTime - startTime) + " milliseconds");
            }
            }
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>通过这个例子，我们注意到在调整缓冲区的大小以及写磁盘的频率上面 Lucene 给我们提供了非常大的灵活性。现在我们来看一下代码中的关键语句。如下的代码首先创建了类 IndexWriter 的一个实例，然后对它的三个参数进行赋值。</p>
<br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td><code>
            <pre class=section>int mergeFactor = 10;
            int minMergeDocs = 10;
            int maxMergeDocs = Integer.MAX_VALUE;
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            indexWriter.mergeFactor = mergeFactor;
            indexWriter.minMergeDocs = minMergeDocs;
            indexWriter.maxMergeDocs = maxMergeDocs;
            </pre>
            </code></td>
        </tr>
    </tbody>
</table>
<br>
<p>下面我们来看一下这三个参数取不同的值对索引时间的影响，注意参数值的不同和索引之间的关系。我们为这个实验准备了 10000 个测试文档。表 1 显示了测试结果。</p>
<br><a name=N10129><strong>表1：测试结果</strong> </a><br><img height=199 alt=表1：测试结果 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table1.gif" width=502 border=0> <br>
<p>通过表 1，你可以清楚地看到三个参数对索引时间的影响。在实践中，你会经常的改变合并因子和最小合并文档数的值来提高索引性能。只要你有足够大的内存，你可以为合并因子和最小合并文档数这两个参数赋尽量大的值以提高索引效率，另外我们一般无需更改最大合并文档数这个参数的值，因为系统已经默认将它设置成了最大。</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N1013C><span class=atitle><font face=Arial size=4>Lucene 索引文件结构分析</font> </span></a></p>
<p>在分析 Lucene 的索引文件结构之前，我们先要理解反向索引（Inverted index）这个概念，反向索引是一种以索引项为中心来组织文档的方式，每个索引项指向一个文档序列，这个序列中的文档都包含该索引项。相反，在正向索引中，文档占据了中心的位置，每个文档指向了一个它所包含的索引项的序列。你可以利用反向索引轻松的找到那些文档包含了特定的索引项。Lucene正是使用了反向索引作为其基本的索引结构。</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10145><span class=atitle><font face=Arial size=4>索引文件的逻辑视图</font> </span></a></p>
<p>在Lucene 中有索引块的概念，每个索引块包含了一定数目的文档。我们能够对单独的索引块进行检索。图 2 显示了 Lucene 索引结构的逻辑视图。索引块的个数由索引的文档的总数以及每个索引块所能包含的最大文档数来决定。</p>
<br><a name=N10150><strong>图2：索引文件的逻辑视图</strong> </a><br><img height=415 alt=图2：索引文件的逻辑视图 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/image004.jpg" width=467 border=0> <br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10160><span class=atitle><font face=Arial size=4>Lucene 中的关键索引文件</font> </span></a></p>
<p>下面的部分将会分析Lucene中的主要的索引文件，可能分析有些索引文件的时候没有包含文件的所有的字段，但不会影响到对索引文件的理解。</p>
<p>1．索引块文件</p>
<p>这个文件包含了索引中的索引块信息，这个文件包含了每个索引块的名字以及大小等信息。表 2 显示了这个文件的结构信息。</p>
<br><a name=N10171><strong>表2：索引块文件结构</strong> </a><br><img height=198 alt=表2：索引块文件结构 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table2.gif" width=568 border=0> <br>
<p>2．域信息文件</p>
<p>我们知道，索引中的文档由一个或者多个域组成，这个文件包含了每个索引块中的域的信息。表 3 显示了这个文件的结构。</p>
<br><a name=N10189><strong>表3：域信息文件结构</strong> </a><br><img height=179 alt=表3：域信息文件结构 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table3.gif" width=567 border=0> <br>
<p>3．索引项信息文件</p>
<p>这是索引文件里面最核心的一个文件，它存储了所有的索引项的值以及相关信息，并且以索引项来排序。表 4 显示了这个文件的结构。</p>
<br><a name=N101A1><strong>表4：索引项信息文件结构</strong> </a><br><img height=252 alt=表4：索引项信息文件结构 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table4.gif" width=568 border=0> <br>
<p>4．频率文件</p>
<p>这个文件包含了包含索引项的文档的列表，以及索引项在每个文档中出现的频率信息。如果Lucene在索引项信息文件中发现有索引项和搜索词相匹配。那么 Lucene 就会在频率文件中找有哪些文件包含了该索引项。表5显示了这个文件的一个大致的结构，并没有包含这个文件的所有字段。</p>
<br><a name=N101B9><strong>表5：频率文件的结构</strong> </a><br><img height=124 alt=表5：频率文件的结构 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table5.gif" width=567 border=0> <br>
<p>5．位置文件</p>
<p>这个文件包含了索引项在每个文档中出现的位置信息，你可以利用这些信息来参与对索引结果的排序。表 6 显示了这个文件的结构</p>
<br><a name=N101D1><strong>表6：位置文件的结构</strong> </a><br><img height=69 alt=表6：位置文件的结构 src="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/images/table6.gif" width=568 border=0> <br>
<p>到目前为止我们介绍了 Lucene 中的主要的索引文件结构，希望能对你理解 Lucene 的物理的存储结构有所帮助。</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"> <br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0> </td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"> <br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0> <br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/wa-lucene/index.html#main"><strong><u><font color=#996699>回页首</font><font color=#0000ff> </font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N101E4><span class=atitle><font face=Arial size=4>总结</font> </span></a></p>
<p>目前已经有非常多的知名的组织正在使用 Lucene，比如，Lucene 为 Eclipse 的帮助系统，麻省理工学院的 OpenCourseWare 提供了搜索功能。通过阅读这篇文章，希望你能对 Lucene 的索引机制有所了解，并且你会发现利用 Lucene 创建索引是非常简单的事情。</p>
<img src ="http://www.blogjava.net/masen/aggbug/121115.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/masen/" target="_blank">Masen</a> 2007-05-31 11:52 <a href="http://www.blogjava.net/masen/articles/121115.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>lucene简介</title><link>http://www.blogjava.net/masen/articles/120785.html</link><dc:creator>Masen</dc:creator><author>Masen</author><pubDate>Tue, 29 May 2007 16:43:00 GMT</pubDate><guid>http://www.blogjava.net/masen/articles/120785.html</guid><wfw:comment>http://www.blogjava.net/masen/comments/120785.html</wfw:comment><comments>http://www.blogjava.net/masen/articles/120785.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/masen/comments/commentRss/120785.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/masen/services/trackbacks/120785.html</trackback:ping><description><![CDATA[<h3 class=entry-header>在应用中加入全文检索功能——基于Java的全文索引引擎Lucene简介</h3>
<div class=entry-content>
<div class=entry-body>
<p>关键词：Lucene java full-text search engine&nbsp;Chinese&nbsp;word segment</p>
<p><span style="FONT-WEIGHT: bold"></span></p>
Lucene不是一个完整的全文索引应用，而是是一个用Java写的全文索引引擎工具包，它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。
<p>Lucene的作者：Lucene的贡献者<a href="http://www.nutch.org/blog/cutting.html"><u><font color=#0000ff>Doug Cutting</font></u></a>是 一位资深全文索引/检索专家，曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者，后在Excite担任高级系统 架构设计师，目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。</p>
</div>
<div class=entry-more id=more>Lucene的发展历程：早先发布在作者自己的<a href="http://www.lucene.com/"><u><font color=#0000ff>www.lucene.com</font></u></a>，后来发布在<a href="http://sourceforge.net/projects/lucene/"><u><font color=#0000ff>SourceForge</font></u></a>，2001年年底成为APACHE基金会jakarta的一个子项目：<a href="http://jakarta.apache.org/lucene/"><u><font color=#800080>http://jakarta.apache.org/lucene/</font></u></a>
<p>已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎，比较著名的有：</p>
<ul>
    <li><a href="http://www.jivesoftware.com/"><u><font color=#0000ff>J</font></u></a><a href="http://www.jivesoftware.com/"><u><font color=#0000ff>ive</font></u></a>：WEB论坛系统；
    <li><a href="http://eyebrowse.tigris.org/"><u><font color=#0000ff>Eyebrows</font></u></a>：邮件列表HTML归档/浏览/查询系统，本文的主要参考文档&#8220;<a href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html"><u><font color=#0000ff>TheLucene search engine: Powerful, flexible, and free</font></u></a>&#8221;作者就是EyeBrows系统的主要开发者之一，而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。
    <li><a href="http://xml.apache.org/cocoon/index.html"><u><font color=#0000ff>Cocoon</font></u></a>:基于XML的web发布框架，全文检索部分使用了Lucene
    <li>
    <p align=left><a href="http://www.eclipse.org/"><u><font color=#0000ff>Eclipse</font></u></a>:基于Java的开放开发平台，帮助部分的全文索引使用了Lucene</p>
    </li>
</ul>
<p>对于中文用户来说，最关心的问题是其是否支持中文的全文检索。但通过后面对于Lucene的结构的介绍，你会了解到由于Lucene良好架构设计，对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。</p>
<p><strong><a name=compare>全文检索的实现机制</a></strong></p>
<p>Lucene的API接口设计的比较通用，输入输出结构都很像数据库的表==&gt;记录==&gt;字段，所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看：可以先把<strong>Lucene当成一个支持全文索引的数据库系统</strong>。</p>
<p>比较一下Lucene和数据库：</p>
<table width="100%" border=1>
    <tbody>
        <tr>
            <td align=middle width="50%">Lucene</td>
            <td align=middle width="50%">数据库</td>
        </tr>
        <tr>
            <td width="50%">
            <pre>索引数据源：doc(field1,field2...) doc(field1,field2...)<br>                  \  indexer /<br>                 _____________<br>                | Lucene Index|<br>                --------------<br>                 / searcher \<br> 结果输出：Hits(doc(field1,field2) doc(field1...))</pre>
            </td>
            <td width="50%">
            <pre> 索引数据源：record(field1,field2...) record(field1..)<br>              \  SQL: insert/<br>               _____________<br>              | DB  Index   |<br>               -------------<br>              / SQL: select \<br>结果输出：results(record(field1,field2..) record(field1...))</pre>
            </td>
        </tr>
        <tr>
            <td width="50%">Document：一个需要进行索引的&#8220;单元&#8221;<br>一个Document由多个字段组成</td>
            <td width="50%">Record：记录，包含多个字段</td>
        </tr>
        <tr>
            <td width="50%">Field：字段</td>
            <td width="50%">Field：字段</td>
        </tr>
        <tr>
            <td width="50%">Hits：查询结果集，由匹配的Document组成</td>
            <td width="50%">RecordSet：查询结果集，由多个Record组成</td>
        </tr>
    </tbody>
</table>
<p><strong>全文检索 &#8800; like "%keyword%"</strong></p>
<p>通常比较厚的书籍后面常常附关键词索引表（比如：北京：12, 34页， 上海：3,77页&#8230;&#8230;），它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样，想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍&#8230;&#8230;而索引之所以效率高，另外一个原因是它是排好序的。<strong>对于检索系统来说核心是一个排序问题</strong>。</p>
<p align=left>由于数据库索引不是为全文索引设计的，因此，<strong>使用like "%keyword%"时，数据库索引是不起作用的</strong>，在使用like查询时，搜索过程又变成类似于一页页翻书的遍历过程了，所以对于含有模糊查询的数据库服务来说，LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配：like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。</p>
<p>所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制，将数据源（比如多篇文章）排序顺序存储的同时，有另外一个排好序的关 键词列表，用于存储关键词==&gt;文章映射关系，利用这样的映射关系索引：[关键词==&gt;出现关键词的文章编号，出现次数（甚至包括位置：起始 偏移量，结束偏移量），出现频率]，检索过程就是把<strong>模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程</strong>。从而大大提高了多关键词查询的效率，所以，全文检索问题归结到最后是一个排序问题。</p>
<p>由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题，这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制，并提供了扩展接口，以方便针对不同应用的定制。</p>
<p>可以通过一下表格对比一下数据库的模糊查询：</p>
<table height=283 width="100%" border=1>
    <tbody>
        <tr>
            <td align=middle width="9%" height=16>　</td>
            <td align=middle width="47%" height=16>Lucene全文索引引擎</td>
            <td align=middle width="40%" height=16>数据库</td>
        </tr>
        <tr>
            <td width="9%" height=48>索引</td>
            <td width="47%" height=48>将数据源中的数据都通过全文索引一一建立反向索引</td>
            <td width="40%" height=48>对于LIKE查询来说，数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配，比有索引的搜索速度要有多个数量级的下降。</td>
        </tr>
        <tr>
            <td width="9%" height=49>匹配效果</td>
            <td width="47%" height=49>通过词元(term)进行匹配，通过语言分析接口的实现，可以实现对中文等非英语的支持。</td>
            <td width="40%" height=49>使用：like "%net%" 会把netherlands也匹配出来，<br>多个关键词的模糊匹配：使用like "%com%net%"：就不能匹配词序颠倒的xxx.net..xxx.com</td>
        </tr>
        <tr>
            <td width="9%" height=32>匹配度</td>
            <td width="47%" height=32>有匹配度算法，将匹配程度（相似度）比较高的结果排在前面。</td>
            <td width="40%" height=32>没有匹配程度的控制：比如有记录中net出现5词和出现1次的，结果是一样的。</td>
        </tr>
        <tr>
            <td width="9%" height=32>结果输出</td>
            <td width="47%" height=32>通过特别的算法，将最匹配度最高的头100条结果输出，结果集是缓冲式的小批量读取的。</td>
            <td width="40%" height=32>返回所有的结果集，在匹配条目非常多的时候（比如上万条）需要大量的内存存放这些临时结果集。</td>
        </tr>
        <tr>
            <td width="9%" height=32>可定制性</td>
            <td width="47%" height=32>通过不同的语言分析接口实现，可以方便的定制出符合应用需要的索引规则（包括对中文的支持）</td>
            <td width="40%" height=32>没有接口或接口复杂，无法定制</td>
        </tr>
        <tr>
            <td width="9%" height=32>结论</td>
            <td width="47%" height=32>高负载的模糊查询应用，需要负责的模糊查询的规则，索引的资料量比较大</td>
            <td width="40%" height=32>使用率低，模糊匹配规则简单或者需要模糊查询的资料量少</td>
        </tr>
    </tbody>
</table>
<p><span style="FONT-WEIGHT: bold">全文检索和数据库应用最大的不同在于：让</span><span style="FONT-WEIGHT: bold">最相关的</span><span style="FONT-WEIGHT: bold">头100条结果满足98%以上用户的需求<br></span><br>Lucene的创新之处：</p>
<p>大部分的搜索（数据库）引擎都是用B树结构来维护索引，索引的更新会导致大量的IO操作，Lucene在实现中，对此稍微有所改进：不是维护一个索 引文件，而是在扩展索引的时候不断创建新的索引文件，然后定期的把这些新的小索引文件合并到原先的大索引中（针对不同的更新策略，批次的大小可以调整）， 这样在不影响检索的效率的前提下，提高了索引的效率。</p>
<p>Lucene和其他一些全文检索系统/应用的比较：</p>
<table width="100%" border=1>
    <tbody>
        <tr>
            <td align=middle width="18%">　</td>
            <td align=middle width="45%">Lucene</td>
            <td align=middle width="37%">其他开源全文检索系统</td>
        </tr>
        <tr>
            <td width="18%">增量索引和批量索引</td>
            <td width="45%">可以进行增量的索引(Append)，可以对于大量数据进行批量索引，并且接口设计用于优化批量索引和小批量的增量索引。</td>
            <td width="37%">很多系统只支持批量的索引，有时数据源有一点增加也需要重建索引。</td>
        </tr>
        <tr>
            <td width="18%">数据源</td>
            <td width="45%">Lucene没有定义具体的数据源，而是一个文档的结构，因此可以非常灵活的适应各种应用（只要前端有合适的转换器把数据源转换成相应结构），</td>
            <td width="37%">很多系统只针对网页，缺乏其他格式文档的灵活性。</td>
        </tr>
        <tr>
            <td width="18%">索引内容抓取</td>
            <td width="45%">Lucene的文档是由多个字段组成的，甚至可以控制那些字段需要进行索引，那些字段不需要索引，近一步索引的字段也分为需要分词和不需要分词的类型：<br>&nbsp;&nbsp; 需要进行分词的索引，比如：标题，文章内容字段<br>&nbsp;&nbsp; 不需要进行分词的索引，比如：作者/日期字段</td>
            <td width="37%">缺乏通用性，往往将文档整个索引了</td>
        </tr>
        <tr>
            <td width="18%">语言分析</td>
            <td width="45%">通过语言分析器的不同扩展实现：<br>可以过滤掉不需要的词：an the of 等，<br>西文语法分析：将jumps jumped jumper都归结成jump进行索引/检索<br>非英文支持：对亚洲语言，阿拉伯语言的索引支持</td>
            <td width="37%">缺乏通用接口实现</td>
        </tr>
        <tr>
            <td width="18%">查询分析</td>
            <td width="45%">通过查询分析接口的实现，可以定制自己的查询语法规则：<br>比如： 多个关键词之间的 + - and or关系等</td>
            <td width="37%">　</td>
        </tr>
        <tr>
            <td width="18%">并发访问</td>
            <td width="45%">能够支持多用户的使用</td>
            <td width="37%">　</td>
        </tr>
    </tbody>
</table>
<p>　</p>
<p><strong><a name=segment>关于亚洲语言的的切分词问题(Word Segment)</a></strong></p>
<p>对于中文来说，全文索引首先还要解决一个语言分析的问题，对于英文来说，语句中单词之间是天然通过空格分开的，但亚洲语言的中日韩文语句中的字是一个字挨一个，所有，首先要把语句中按&#8220;词&#8221;进行索引的话，这个词如何切分出来就是一个很大的问题。</p>
<p>首先，肯定不能用单个字符作(si-gram)为索引单元，否则查&#8220;上海&#8221;时，不能让含有&#8220;海上&#8221;也匹配。</p>
<p>但一句话：&#8220;北京天安门&#8221;，计算机如何按照中文的语言习惯进行切分呢？<br>&#8220;北京 天安门&#8221; 还是&#8220;北 京 天安门&#8221;？让计算机能够按照语言习惯进行切分，往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。</p>
<p>另外一个解决的办法是采用自动切分算法：将单词按照2元语法(bigram)方式切分出来，比如：<br>"北京天安门" ==&gt; "北京 京天 天安 安门"。</p>
<p>这样，在查询的时候，无论是查询"北京" 还是查询"天安门"，将查询词组按同样的规则进行切分："北京"，"天安安门"，多个关键词之间按与"and"的关系组合，同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言：韩文，日文都是通用的。</p>
<p>基于自动切分的最大优点是没有词表维护成本，实现简单，缺点是索引效率低，但对于中小型应用来说，基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多，而对于英文，索引文件一般只有原文件的30%-40%不同，</p>
<table height=68 width="100%" border=1>
    <tbody>
        <tr>
            <td align=middle width="11%" height=18><br></td>
            <td align=middle width="39%" height=18>自动切分</td>
            <td align=middle width="50%" height=18>词表切分</td>
        </tr>
        <tr>
            <td width="11%" height=16>实现</td>
            <td width="39%" height=16>实现非常简单</td>
            <td width="50%" height=16>实现复杂</td>
        </tr>
        <tr>
            <td width="11%" height=16>查询</td>
            <td width="39%" height=16>增加了查询分析的复杂程度，</td>
            <td width="50%" height=16>适于实现比较复杂的查询语法规则</td>
        </tr>
        <tr>
            <td width="11%" height=16>存储效率</td>
            <td width="39%" height=16>索引冗余大，索引几乎和原文一样大</td>
            <td width="50%" height=16>索引效率高，为原文大小的30％左右</td>
        </tr>
        <tr>
            <td width="11%" height=16>维护成本</td>
            <td width="39%" height=16>无词表维护成本</td>
            <td width="50%" height=16>词表维护成本非常高：中日韩等语言需要分别维护。<br>还需要包括词频统计等内容</td>
        </tr>
        <tr>
            <td width="11%" height=16>适用领域</td>
            <td width="39%" height=16>嵌入式系统：运行环境资源有限<br>分布式系统：无词表同步问题<br>多语言环境：无词表维护成本</td>
            <td width="50%" height=16>对查询和存储效率要求高的专业搜索引擎<br></td>
        </tr>
    </tbody>
</table>
<p>目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法，大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。</p>
<p><a name=demo><strong>安装和使用</strong></a></p>
<p>下载：<a href="http://jakarta.apache.org/lucene/"><u><font color=#800080>http://jakarta.apache.org/lucene/</font></u></a></p>
<p>注意：Lucene中的一些比较复杂的词法分析是用JavaCC生成的（JavaCC：JavaCompilerCompiler，纯Java的词法分析生成器），所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器，还需要从<a href="https://javacc.dev.java.net/"><u><font color=#0000ff>https://javacc.dev.java.net/</font></u></a>下载javacc。</p>
<p>lucene的组成结构：对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口</p>
<table width="100%" border=1>
    <tbody>
        <tr>
            <td width="27%">org.apache.Lucene.search/</td>
            <td width="73%">搜索入口</td>
        </tr>
        <tr>
            <td width="27%">org.apache.Lucene.index/</td>
            <td width="73%">索引入口</td>
        </tr>
        <tr>
            <td width="27%">org.apache.Lucene.analysis/</td>
            <td width="73%">语言分析器</td>
        </tr>
        <tr>
            <td width="27%">org.apache.Lucene.queryParser/</td>
            <td width="73%">查询分析器</td>
        </tr>
        <tr>
            <td width="27%">org.apache.Lucene.document/</td>
            <td width="73%">存储结构</td>
        </tr>
        <tr>
            <td width="27%">org.apache.Lucene.store/&nbsp;</td>
            <td width="73%">底层IO/存储结构</td>
        </tr>
        <tr>
            <td width="27%">org.apache.Lucene.util/</td>
            <td width="73%">一些公用的数据结构</td>
        </tr>
    </tbody>
</table>
<p>简单的例子演示一下Lucene的使用方法：</p>
索引过程：从命令行读取文件名（多个），将文件分路径(path字段)和内容(body字段)2个字段进行存储，并对内容进行全文索引：索引的单位是 Document对象，每个Document对象包含多个字段Field对象，针对不同的字段属性和数据输出的需求，对字段还可以选择不同的索引/存储字 段规则，列表如下：
<table border=1>
    <tbody>
        <tr>
            <th>方法</th>
            <th>切词</th>
            <th>索引</th>
            <th>存储</th>
            <th>用途</th>
        </tr>
        <tr>
            <td>Field.Text(String name, String value)</td>
            <td>Yes</td>
            <td>Yes</td>
            <td>Yes</td>
            <td vAlign=top>切分词索引并存储，比如：标题，内容字段</td>
        </tr>
        <tr>
            <td>Field.Text(String name, Reader value)</td>
            <td>Yes</td>
            <td>Yes</td>
            <td>No</td>
            <td vAlign=top>切分词索引不存储，比如：META信息，<br>不用于返回显示，但需要进行检索内容</td>
        </tr>
        <tr>
            <td>Field.Keyword(String name, String value)</td>
            <td>No</td>
            <td>Yes</td>
            <td>Yes</td>
            <td vAlign=top>不切分索引并存储，比如：日期字段</td>
        </tr>
        <tr>
            <td>Field.UnIndexed(String name, String value)</td>
            <td>No</td>
            <td>No</td>
            <td>Yes</td>
            <td vAlign=top>不索引，只存储，比如：文件路径</td>
        </tr>
        <tr>
            <td>Field.UnStored(String name, String value)</td>
            <td>Yes</td>
            <td>Yes</td>
            <td>No</td>
            <td vAlign=top>只全文索引，不存储</td>
        </tr>
    </tbody>
</table>
<pre>public class IndexFiles { <br>  //使用方法：: IndexFiles [索引输出目录] [索引的文件列表] ... <br>  public static void main(String[] args) throws Exception {<br>    String indexPath = args[0];<br>    IndexWriter writer;<br>    //用指定的语言分析器构造一个新的写索引器（第3个参数表示是否为追加索引）<br>    writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<br><br>    for (int i=1; i&lt;args.length; i++) {<br>      System.out.println("Indexing file " + args[i]);<br>      InputStream is = new FileInputStream(args[i]);<br><br>      //构造包含2个字段Field的Document对象<br>      //一个是路径path字段，不索引，只存储<br>      //一个是内容body字段，进行全文索引，并存储<br>      Document doc = new Document();<br>      doc.add(Field.UnIndexed("path", args[i]));<br>      doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<br>      //将文档写入索引<br>      writer.addDocument(doc);<br>      is.close();<br>    };<br>    //关闭写索引器<br>    writer.close();<br>  }<br>}<br>　</pre>
<p>索引过程中可以看到：</p>
<ul>
    <li>语言分析器提供了抽象的接口，因此语言分析(Analyser)是可以定制的，虽然lucene缺省提供了2个比较通用的分析器 SimpleAnalyser和StandardAnalyser，这2个分析器缺省都不支持中文，所以要加入对中文语言的切分规则，需要修改这2个分析 器。
    <li>Lucene并没有规定数据源的格式，而只提供了一个通用的结构（Document对象）来接受索引的输入，因此输入的数据源可以是：数据库，WORD文档，PDF文档，HTML文档&#8230;&#8230;只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。
    <li>对于大批量的数据索引，还可以通过调整IndexerWrite的文件合并频率属性（mergeFactor）来提高批量索引的效率。 </li>
</ul>
<p>检索过程和结果显示：</p>
<p>搜索结果返回的是Hits对象，可以通过它再访问Document==&gt;Field中的内容。</p>
<p>假设根据body字段进行全文检索，可以将查询结果的path字段和相应查询的匹配度(score)打印出来，</p>
<pre>public class Search { <br>  public static void main(String[] args) throws Exception {<br>    String indexPath = args[0], queryString = args[1];<br>    //指向索引目录的搜索器<br>    Searcher searcher = new IndexSearcher(indexPath);<br>    //查询解析器：使用和索引同样的语言分析器<br>    Query query = QueryParser.parse(queryString, "body", <br>                              new SimpleAnalyzer());<br>    //搜索结果使用Hits存储<br>    Hits hits = searcher.search(query);<br>    //通过hits可以访问到相应字段的数据和查询的匹配度<br>    for (int i=0; i&lt;hits.length(); i++) {<br>      System.out.println(hits.doc(i).get("path") + "; Score: " + <br>                         hits.score(i));<br>    };<br>  }<br>}</pre>
在整个检索过程中，语言分析器，查询分析器，甚至搜索器（Searcher）都是提供了抽象的接口，可以根据需要进行定制。
<p><strong><a name=hacking>Hacking Lucene</a></strong></p>
<p><strong>简化的查询分析器</strong></p>
<p>个人感觉lucene成为JAKARTA项目后，画在了太多的时间用于调试日趋复杂QueryParser，而其中大部分是大多数用户并不很熟悉的，目前LUCENE支持的语法：</p>
<p>Query ::= ( Clause )*<br>Clause ::= ["+", "-"] [&lt;TERM&gt; ":"] ( &lt;TERM&gt; | "(" Query ")")</p>
<p>中间的逻辑包括：and or + - &amp;&amp;||等符号，而且还有"短语查询"和针对西文的前缀/模糊查询等，个人感觉对于一般应用来说，这些功能有一些华而不实，其实能够实现 目前类似于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以，Lucene早期版本的QueryParser仍是比较好的选择。</p>
<p><strong>添加修改删除指定记录（Document）</strong></p>
<p>Lucene提供了索引的扩展机制，因此索引的动态扩展应该是没有问题的，而指定记录的修改也似乎只能通过记录的删除，然后重新加入实现。如何删除 指定的记录呢？删除的方法也很简单，只是需要在索引时根据数据源中的记录ID专门另建索引，然后利用IndexReader.delete (Termterm)方法通过这个记录ID删除相应的Document。</p>
<p><strong>根据某个字段值的排序功能</strong></p>
<p>lucene缺省是按照自己的相关度算法（score）进行结果排序的，但能够根据其他字段进行结果排序是一个在LUCENE的开发邮件列表中经常 提到的问题，很多原先基于数据库应用都需要除了基于匹配度（score）以外的排序功能。而从全文检索的原理我们可以了解到，任何不基于索引的搜索过程效 率都会导致效率非常的低，如果基于其他字段的排序需要在搜索过程中访问存储字段，速度回大大降低，因此非常是不可取的。</p>
<p>但这里也有一个折中的解决方法：在搜索过程中能够影响排序结果的只有索引中已经存储的docID和score这2个参数，所以，基于score以外 的排序，其实可以通过将数据源预先排好序，然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访 问不在索引中的某个字段值。</p>
<p>这里需要修改的是IndexSearcher中的HitCollector过程：</p>
<pre>...<br>　scorer.score(new HitCollector() {<br>	private float minScore = 0.0f;<br>	public final void collect(int doc, float score) {<br>	  if (score &gt; 0.0f &amp;&amp;			  // ignore zeroed buckets<br>	      (bits==null || bits.get(doc))) {	  // skip docs not in bits<br>	    totalHits[0]++;<br>	    if (score &gt;= minScore) {<br>              /* 原先：Lucene将docID和相应的匹配度score例入结果命中列表中：<br>	       * hq.put(new ScoreDoc(doc, score));	  // update hit queue<br>               * 如果用doc 或 1/doc 代替 score，就实现了根据docID顺排或逆排<br>               * 假设数据源索引时已经按照某个字段排好了序，而结果根据docID排序也就实现了<br>               * 针对某个字段的排序，甚至可以实现更复杂的score和docID的拟合。<br>               */<br>              hq.put(new ScoreDoc(doc, (float) 1/doc )); <br>	      if (hq.size() &gt; nDocs) {		  // if hit queue overfull<br>		hq.pop();			  // remove lowest in hit queue<br>		minScore = ((ScoreDoc)hq.top()).score; // reset minScore<br>	      }<br>	    }<br>	  }<br>	}<br>      }, reader.maxDoc());</pre>
<p><strong>更通用的输入输出接口</strong></p>
<p>虽然lucene没有定义一个确定的输入文档格式，但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口，然后其他数据，比如 PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主，类似实现已经不下4，5个：</p>
<pre>数据源: WORD       PDF     HTML    DB       other<br>         \          |       |      |         /<br>                       XML中间格式<br>                            |<br>                     Lucene INDEX</pre>
<p>目前还没有针对MSWord文档的解析器，因为Word文档和基于ASCII的RTF文档不同，需要使用COM对象机制解析。这个是我在Google上查的相关资料：<a href="http://www.intrinsyc.com/products/enterprise_applications.asp"><u><font color=#0000ff>http://www.intrinsyc.com/products/enterprise_applications.asp</font></u></a><br>另外一个办法就是把Word文档转换成text：<a href="http://www.winfield.demon.nl/index.html"><u><font color=#0000ff>http://www.winfield.demon.nl/index.html</font></u></a><br></p>
<p><br><strong>索引过程优化</strong></p>
<p>索引一般分2种情况，一种是小批量的索引扩展，一种是大批量的索引重建。在索引过程中，并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作（文件I/O是一件非常消耗资源的事情）。</p>
<p>Lucene先在内存中进行索引操作，并根据一定的批量进行文件的写入。这个批次的间隔越大，文件的写入次数越少，但占用内存会很多。反之占用内存 少，但文件IO操作频繁，索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充 分利用内存减少文件的操作。根据我的使用经验：缺省Indexer是每20条记录索引后写入一次，每将MERGE_FACTOR增加50倍，索引速度可以 提高1倍左右。<br></p>
<p><span style="FONT-WEIGHT: bold">搜索过程优化<br></span></p>
<p><span style="FONT-WEIGHT: bold"></span>lucene支持内存索引：这样的搜索比基于文件的I/O有数量级的速度提升。<br><a href="http://www.onjava.com/lpt/a/3273"><u><font color=#0000ff>http://www.onjava.com/lpt/a/3273</font></u></a><br>而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。<br><span style="FONT-WEIGHT: bold"></span></p>
<p>Lucene面向全文检索的优化在于首次索引检索后，并不把所有的记录（Document）具体内容读取出来，而起只将所有结果中匹配度最高的头 100条结果（TopDocs）的ID放到结果集缓存中并返回，这里可以比较一下数据库检索：如果是一个10,000条的数据库检索结果集，数据库是一定 要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多，Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应 用是用不到这么多的结果的，头100条已经可以满足90%以上的检索需求。<br></p>
<p>如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存，并再重新向后抓取。所以如果 构造一个Searcher去查1－120条结果，Searcher其实是进行了2次搜索过程：头100条取完后，缓存结果用完，Searcher重新检索 再构造一个200条的结果缓存，依此类推，400条缓存，800条缓存。由于每次Searcher对象消失后，这些缓存也访问那不到了，你有可能想将结果 记录缓存下来，缓存数尽量保证在100以下以充分利用首次的结果缓存，不让Lucene浪费多次检索，而且可以分级进行结果缓存。<br></p>
<p>Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。</p>
<p><a href="http://sourceforge.net/projects/weblucene/"><u><font color=#0000ff>我的一些尝试</font></u></a>：</p>
<ul>
    <li>支持中文的Tokenizer：这里有2个版本，一个是通过JavaCC生成的，对CJK部分按一个字符一个TOKEN索引，另外一个是从SimpleTokenizer改写的，对英文支持数字和字母TOKEN，对中文按迭代索引。
    <li>基于XML数据源的索引器：XMLIndexer，因此所有数据源只要能够按照DTD转换成指定的XML，就可以用XMLIndxer进行索引了。
    <li>根 据某个字段排序：按记录索引顺序排序结果的搜索器：IndexOrderSearcher，因此如果需要让搜索结果根据某个字段排序，可以让数据源先按某 个字段排好序（比如：PriceField），这样索引后，然后在利用这个按记录的ID顺序检索的搜索器，结果就是相当于是那个字段排序的结果了。 </li>
</ul>
<p><a name=learn><strong>从Lucene学到更多</strong></a></p>
<p>Luene的确是一个面对对象设计的典范</p>
<ul>
    <li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要；
    <li>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务；
    <li>所 有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构 IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修 改其他模块。&nbsp;
    <li>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。 </li>
</ul>
<p>这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。</p>
<p>此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</p>
<ul>
    <li>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。
    <li>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的，
    <li>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。
    <li>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的操作。<br></li>
</ul>
<p>参考资料：</p>
<p>Apache: Lucene Project<br><a href="http://jakarta.apache.org/lucene/"><u><font color=#800080>http://jakarta.apache.org/lucene/<br></font></u></a>Lucene开发/用户邮件列表归档<br><a href="http://www.mail-archive.com/lucene-dev@jakarta.apache.org/"><font color=#0000ff><u>Lucene-dev@jakarta.apache.org</u></font></a><br><a href="http://www.mail-archive.com/lucene-user@jakarta.apache.org/"><u><font color=#0000ff>Lucene-user@jakarta.apache.org</font></u></a></p>
<p>The Lucene search engine: Powerful, flexible, and free<br><a href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html"><u><font color=#0000ff>http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html</font></u></a></p>
<p>Lucene Tutorial<br><a href="http://www.darksleep.com/puff/lucene/lucene.html"><u><font color=#0000ff>http://www.darksleep.com/puff/lucene/lucene.html</font></u></a></p>
<p>Notes on distributed searching with Lucene<br><a href="http://home.clara.net/markharwood/lucene/"><u><font color=#0000ff>http://home.clara.net/markharwood/lucene/</font></u></a></p>
<p>中文语言的切分词<br><a href="http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment"><u><font color=#0000ff>http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment</font></u></a></p>
<p>搜索引擎工具介绍<a href="http://searchtools.com/"><br><u><font color=#0000ff>http://searchtools.com/</font></u></a></p>
<p>Lucene作者Cutting的几篇论文和专利<br><a href="http://lucene.sourceforge.net/publications.html"><u><font color=#0000ff>http://lucene.sourceforge.net/publications.html</font></u></a>&nbsp;</p>
<p>Lucene的.NET实现：dotLucene<br><a href="http://sourceforge.net/projects/dotlucene/"><u><font color=#0000ff>http://sourceforge.net/projects/dotlucene/<br></font></u></a></p>
<p>Lucene作者Cutting的另外一个项目：基于Java的搜索引擎Nutch<br><a href="http://www.nutch.org/"><u><font color=#0000ff>http://www.nutch.org/</font></u></a> &nbsp; <a href="http://sourceforge.net/projects/nutch/"><u><font color=#0000ff>http://sourceforge.net/projects/nutch/<br></font></u></a></p>
<p>关于基于词表和N-Gram的切分词比较<br><a href="http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html"><u><font color=#0000ff>http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html</font></u></a><br><br>2005-01-08 <a href="http://lucene.sourceforge.net/talks/pisa/"><u><font color=#0000ff>Cutting在Pisa大学做的关于Lucene的讲座：非常详细的Lucene架构解说</font></u></a> </p>
<p>特别感谢：<br><a href="http://www.google.com/search?q=%22Jack+Xu%22+Excite"><u><font color=#0000ff>前网易CTO许良杰(Jack Xu)</font></u></a>给我的指导：是您将我带入了搜索引擎这个行业。</p>
原文出处：http://www.chedong.com/tech/lucene.html </div>
</div>
<img src ="http://www.blogjava.net/masen/aggbug/120785.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/masen/" target="_blank">Masen</a> 2007-05-30 00:43 <a href="http://www.blogjava.net/masen/articles/120785.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>