write down,forget

[收藏]lucene知识点

<Category: Lucene> 查看评论

From:http://ybzshizds.javaeye.com/blog/279113


一、lucene索引的建立

1.索引文件
.fdt文件:主要保存数据源数据,存储的field的值仅为Document中具有的Store.YES属性的field
.fdx文件:记录当前Document在.fdt文件中的位置,以便后面读取时方便
segments文件:通常,在一个完整的索引中,有且只有一个segments文件,该文件没有后缀,它记录了当前索引中所有的
segment的信息
.fnm文件:包含了Document中的所有field名称
.tis文件:用于存储分词后的词条(Term);
.tii文件:是.tis文件的索引文件,它标明了每个.tis文件中的词条的位置
deletable文件:类似于Windows的回收站原理,所有的文档在被删除后,会首先在deletable文件中留一个记录,要真正删
除时,才将索引除去。
2.文档倒排
作用:统计词条的位置和词频信息
说明:倒排完毕后,所有的词条都被放入到了一个HashTable中,它的key是词条的Term对象,value是Posting类型的对象
lucene首先将这个HashTable转化成一个Posting类型的数组,然后对这个数组进行排序,使所有的词条按字典顺序
排列。那样,就可以将词条信息写入到.tii和.tis文件中。另外,将频率和位置信息写入.frq和.prx文件中去

3.StandardAnalyzer能将标点过滤掉

4.FSDirectory和RAMDirectory
FSDirectory:指的是在文件系统中的一个路径
RAMDirectory:是内存中的一个区域
二者都可以作为索引的存储路径

5.合并索引
用户不但可以将存放于不同文件系统路径下的索引合并,还可以将内存中的索引与文件系统中的索引进行合并,以次来
保存那些存放于RAMDirectory中的索引
注意:合并内存中的索引时,一定要先将其相应的IndexWriter关闭,以保证滞留在缓存中的文档被刷到RAMDirectory中
去,这点与试用FSDirectory时一样。如果不使用close方法关闭IndexWriter,就会发现索引文件并未真正写入目
录中去。
合并索引的目的:是为了减少目录内的索引文件的数量,以使lucene能够更快地处理索引,这将大大加快lucene的检索速
度。

6.索引的优化
目的:对整个索引目录内、未合并的segment进行一个优化的合并,以保证检索时的速度
IndexWriter的optimize()方法能够对当前IndexWriter所指定的索引目录及其所使用的缓存目录下的所有的segment做优化
使所有的segment合并成一个完整的segment,即整个索引目录内只出现一种文件前缀

7.试用文档ID号来删除特定文档
逻辑删除:(相当于windows的回收站)
IndexReader reader=IndexReader(INDEX_STORE_PATH);
reader.deleteDocument(0);
reader.close();

逻辑删除后恢复:
IndexReader reader=IndexReader(INDEX_STORE_PATH);
reader.undeleteAll();
reader.close();

物理删除:
IndexReader reader=IndexReader(INDEX_STORE_PATH);
reader.optimize();
reader.close();

8.lucene索引的同步问题
a. lucene的同步问题只可能发生在对索引进行文档添加、文档删除、合并segment和优化时
b. lucene有自己的锁机制可以防止非法操作的发生
write.lock出现在向索引添加文档时,或是将文档从索引中删除时
commit.lock主要是与segment合并和读取的操作相关
c. 开发者在开发应用的过程中,不能仅仅依赖于lucene的锁机制来防止非法操作的发生,而
是应该养成良好的编写具有同步特性的代码的习惯,才能编写出高质量的代码

9. 2.0以上的版本中出现了个新类,indexModifier类,它集成了IndexWriter的大部分功
能和IndexReader中的对索引删除的功能

二、lucene的搜索

1.几个基础类简要介绍
.Query:表示一次查询
.Hits:表示一次查询的结果
.Filter:表示对索引中文档集合的过滤器,它使检索在某一个文档集合的子集中进行
.Sort:对索引的结果进行排序的工具
.HitCollector:对检索结果进行选择的一个工具,并将选择后的结果保存在其中
.Weight:就是"权重",表示一次查询时,索引中的某个文档的重要性

2.IndexSearcher类search方法可以分为三组
第一组:
public final Hits search(Query query)
public Hits search(Query query, Filter filter)
public Hits search(Query query, Sort sort)
返回值为一个Hits型的对象,它们通过一个Query对象,在索引中检索相关的文档,并返回检索
的结果

第二组:
public TopFieldDocs search(Query query, Filter filter, int n,Sort sort)
public TopDocs search(Weight weight, Filter filter, final int nDocs)
public TopFieldDocs search(Weight weight, Filter filter, final int nDocs,Sort sort)
public TopDocs search(Query query, Filter filter, int n)
返回的是TopDocs类型和TopFieldDocs(是TopDocs的一个子类)类型,它们表示索引中得分较高的文
档集合,该组方法中都带有一个int型的参数,它表示取出置于TopDocs集合中的文档的数量

第三组:
public void search(Query query, HitCollector results)
public void search(Query query, Filter filter, HitCollector results)
public void search(Weight weight, Filter filter,final HitCollector results)
返回值为空,但是并不是说它们不具备返回值的功能,这一组方法的参数中,都有一个HitCollector
类型的参数,该参数表示由这一组方法检索而返回的结果,将被保存在这个HitCollector对象中

3.Hits内部缓存
Hits的缓存是一个Vector类型的对象,默认存放200个文档数量,如果当前链表的文档数量大于规定
的数量,则从中删除最后一个也就是不常使用的一个,如果缓存中没有或者是缓存不慎丢失,则从
searcher中重新取回Document

4.Query搜索相关
Query类是个抽象类
它有如下几种子类
TermQuery:词条搜索,它是lucene内最为简单也是最为原子的一种搜索方式。通过对某个固定词条的
指定,它实现了检索索引中存在的该词条的所有文档
BooleanQuery:布尔型查询就是一个由多个子句和子句(默认的最大子句数为1024,如果超过了这个数量,
会抛出TooManyClauses异常,用户可以通过BooleanQuery.setMaxClauseCount(int MaxClauseCount)
去更改其最大的子句数量)间的布尔逻辑所组成的查询,各种子句间都是如"与","或"这样的布尔
逻辑。布尔逻辑由三种情况去任意组合,它们分别是BooleanClause.Qccur.MUST
BooleanClause.Qccur.MUST_NOT BooleanClause.Qccur.SHOULD
RangeQuery:提供对一定范围内的文档进行查找的途径,这种范围可以是时间、日期、数字大小等。
PrifixQuery:根据前缀进行查找
PhraseQuery:短语搜索,对一个或多个关键字进行搜索。PhraseQuery.setSlop(int i)可以设定关键字
中允许插入无关的字的个数
MultiPhraseQuery:可以对多个短语同时进行搜索,指定前缀或后缀,或者同时指定前缀和后缀的搜索。
FuzzyQuery:模糊搜索,可以帮助用户进行单字的模糊查找,这里有个相似度的概念,它决定模糊匹配
时的严格程度。默认的相似度是0.5,当这个值越小时,通过模糊查找出的文档的匹配程度就越低
文档的数量也就越多,反之亦然。
WildcardQuery:通配符搜索,"*"表示0到多个字符,"?"表示一个单一的字符
SpanQuery:跨度搜索,这里的跨度是指一种数据源中每个词条的位置所构成的一个跨度

5.多域搜索(MultiFieldQueryParser)和多索引搜索(MultiSearcher)
每次构造一个IndexSearcher的实例时,只能打开一个索引就行搜索。如果此时系统的环境使用了多索
引目录的结构,即用户希望检索到的数据,可能被存放于多个索引目录随机的一个中,这样就无法进行检索
了,这时就必须借助于多域搜索MultiFieldQueryParser和多索引搜索MultiSearcher
注意:MultiSearcher是利用一个for循环将所有的IndexSearcher对象取出,然后顺序循环搜索并得出
结果,最后将结果合并返回给用户,在这个过程中,始终只有一个索引被搜索,其它索引目录处于等待状态
多线程搜索(ParalellMultiSearcher)可以很好的解决这个问题,一旦一个搜索的请求发出,所有索引可以
在同一时刻被检索,也就是被多个线程检索。这样,可以省去排队等待的时间,提高检索效率。但是实际情
况并非如此,结测试单核CPU和双核CPU环境中,ParalellMultiSearcher比MultiSearcher的检索效率要低
问题的根源可能是Lucene没有为ParalellMultiSearcher开设一个专门的线程池。这样每次都需要实例化新
线程,导致了性能的降低。

6.跨虚拟机器的搜索
java中的RMI提供了调用远程虚拟机中的对象方法的能力。可以通过RMI,获取远程虚拟机中的对象存根Stub
然后调用它的方法,并在远程虚拟机内执行,最后将结果返回本机进行处理。

三、排序过滤和分页

仅仅把东西搜出来是不够的,好的检索工具还应当能够对检索的结果进行排序,优先将最相关的内容送出
或是按照某种规则,将检索结果送出。
1.文档得分规则
文档得分主要是由4部分内容来决定,即tf(词条频率)、idf(反转文档频率)、boost(Field的激励因子)
和lengthNorm(长度因子)
tf:某个关键字在某文档中出现次数的平方根
idf:Math.log(numDocs/docFreq+1)+1.0 (numDocs:表示索引中总共的文档数量,docFreq:当前检索
的关键字的文档总数)
lengthNorm:在lucene的底层实现中,lengthNorm是一种固定值,它无需在索引时指定,只需要跟随创建
索引过程,被写入索引中。
boost:文档的boost值一般情况下默认为1.0,在建立索引时可通过预先人为的指定Document或Field的
boost值,来改变搜索时文档的得分,从而改变文档在搜索结果中的排序位置。
2.使用Sort排序
该类有6种构造函数
public Sort()
public Sort(String field) 默认为降序
public Sort(String field,boolean reverse)
public Sort(String[] fields)
public Sort(SortField field)
public Sort(SortField[] fields)
Sort可以对单一的Field进行排序,但是对多个Field的排序就必须要借助SortField类了。SortField类
是个包装类,它能描述Field和reverse(排序)信息,在搜索结果中有很多记录时,应该正确的指定Field
的类型,这样对排序过程的效率会有很大的提高。
排序有多种
a.按文档得分进行排序:这是lucene默认的排序方式
如:Hits hist=searcher.search(q,Sort.RELEVANCE); Sort.RELEVANCE表示当前的排序法则
是按照文档的得分进行降序排列
b.按文档的内部ID号来排序:在建立索引的时候,lucene会为每个文档建立一个内部的ID号
如:Hits hits=searcher.search(q,Sort.INDEXORDER) Sort.INDEXORDER表示排序法则是按
照文档的内部ID号来排序
c.按一个或多个Field来排序:把每个Field封装成SortField,然后以SortField[]数组的形式传入Sort类
注意:比较字符串时,Locale信息的不同,很有可能会影响到比较的结果,因此SortField类有这样的构
造函数,可以帮助指定Locale信息
public SortField(String field,Locale locale)
public SortField(String field,Locale locale,boolean reverse)
当指定Locale后,lucene才可以正确的对字符串进行比较,从而正确的排序。不过大多数情况下,使用Sort
进行排序时,排序的内容多为int型或是日期型。即便需要进行字符串的比较,也最好使用仅包含ASCII的
字符串Field进行,这样可以确保排序的正确,同时,尽量提高排序的效率。
3.搜索时使用过滤器
简单示例:

public class MySecurityFilter extends Filter{  
    public static final int SECURITY=0;  
    public BitSet bits(IndexReader reader) throws IOException{  
       //先初始化一个BitSet对象   
       final BitSet bits=new BitSet(reader.maxDoc());  
       //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的  
       bits.set(0,bits.size()-1);  
       //构造一个Term对象,代表最高安全级别  
       Term term=new Term("securitylevel",SECURITY+"");  
       //从索引中取出具有最高安全级别的文档  
       TermDocs termDocs=reader.termDocs(term);  
       //遍历每个文档  
       while(termDocs.next()){  
          bits.set(termDocs.doc(),false);  
       }  
      retrun bits;  
    }  
}

or

public class MySecurityFilter extends Filter{  
    public static final int SECURITY=0;  
    public BitSet bits(IndexReader reader) throws IOException{  
       //先初始化一个BitSet对象   
       final BitSet bits=new BitSet(reader.maxDoc());  
       //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的  
       bits.set(0,bits.size()-1);  
       //构造一个Term对象,代表最高安全级别  
       Term term=new Term("securitylevel",SECURITY+"");  
       //初始化一个IndexSearcher对象  
       //查找securitylevel这个Field的值为SECURITY的文档  
       IndexSearcher searcher=new IndexSearcher(term);  
       Hits hits=searcher.search(new TermQuery(term));  
       for(int i=0;i<hits.length();i++){  
          bits.set(hits.id(i),false);  
       }  
      retrun bits;  
    }  
}

注意:Filter实际上在进行真正的查询之前,已经遍历过一次一遍索引了,因此无
论使用何种Filter,都不应该忽视它对查询效率的影响。
4.在结果中查询(QueryFilter)
在QueryFilter的bits方法中,调用IndexSearcher方法,对注入的Query进行一次
检索,然后在要被返回的BitSet中,将检索结果所对应的文档标记为true,即过滤掉了
所有不再检索结果范围内的文档。

本文来自: [收藏]lucene知识点