[转]Lucene经验总结 (转注:较旧,但有干货)
// 转自:http://zdm2008.blog.163.com/blog/static/20491545200971894520912/
个人的一点经验,分享之佘,还请大家补充!!
如果你想学Lucene,那么Sphinx你一定不能放过
------------------------------------------------------------------------------------------------------------
搜索引擎知识总结
1:搜索引擎按原理和工作方式可分为:
A:爬虫式,主要用Socket实现,基于TCP/IP协议
B:目录索引式,以早期的yahoo为代表
C:元搜索引擎,即将多个搜索引擎的结果合并返回
2:按领域范围可分为:
A:通用搜索引擎---针对全互联网全部网站和各种数据信息,信息全,领域广
B:垂直搜索引擎---针对果某一行业,如企业库搜索,供求信息搜索,房产搜索等
3:信息类型分类:
二进制的文档,音频,视频,图片等。其中常见的二进制文档有:TXT,WORD,EXCEL,PPT,PDF,XML,HTML
5:搜索引擎研究网站
A:数据挖掘研究院---http://www.dmresearch.net
B:哈工大语言技术网---http://www.langtech.org.cn
C:车东的博客---http://www.chedong.com
D:Google黑板报---http://googlechinablog.com
E:搜狗实验室---http://www.sogou.com/labs/
F:Search Engine Watch,这是全球最大的搜索引擎研究站---http://searchenginewatch.com
6:搜索引擎算法
A:深度优先算法,B:广度优先算法,C:IP段扫描算法
更多的请参考:http://hi.baidu.com/chenhaoxian/blog/item/26d7560f14b69f2f6059f394.html
7:网页分析过程
单词提取->标点符号去除->大小写转换->超高频词汇去除->中文分词->提取URL,EMAIL,真正意文的文本信息等
8:信息检索模型分类
A:布尔模型,B:向量模型,C:概率模型,D:混合模型等
9:搜索引擎通常要解决的基本问题
A:信息抓取,B:数据解析,C:索引创建,D:执行检索
10:你要知道什么叫“倒排索引”
以字或词做为索引,非常适合“关键词”搜索
11:当前主流的索引技术
A:倒排索引
B:反缀数组
C:签名文件
------------------------------------------------------------------------------------------------------------
Lucene常识总结
12:Lucene中的分词方法
(1)单词切分
A:对于English和Chinese,我们用StandardAnalyzer就可以了
B:ChineseAnalyzer
(2)二分法
对于中文,可用CJKAnalyzer,NGram(综合了单词切分和二分法)
注:StandardAnalyzer,ChineseAnalyzer,CJKAnalyzer,NGram相关的jar包在:
%LUCENE%/contrib/analyzers/lucene-analyzer-2.1.0.jar包中,视版本不同而异
(3)词典法
如IK分词器,实现了正反向全切分算法,它包括了两个分析器,MIK_CAnalyzer使用最大全切分算法和IKAnalyzer使用细粒度全切分算法,相关jar包为:IKAnalyzer.jar
(4)语义法
(5)基于词库,如JE分词器,可以向词库中添加新词,查看词,删除词,相关jar包为:je-analysis-1.5.1.jar
Plus:中科院有个分词器,是dll形式,在Java中必须借助JNI,这样效率和效果都不好,容易死机,不建议采用
(6)好了,说了这么多分词器,最后推荐大家用JE分词器,足够了
13:常用二进制文档解析组件
A:PDF---PDFBox,从http://sourceforge.net/projects/pdfbox/下载PDFBox类库并解压,将external和lib下的jar包全copy到你的应用程序/lib下
B:WORD---用POI的一个组件包,textmining,从http://mirrors.ibiblio.org/pub/mirrors/maven2/org/textmining/tm-extractors/0.4/下载tm-extractors-0.4.zip
C:EXCEL---JExcel,从http://www.andykhan.com/jexcelapi/下载
D:XML---DOM4J,从http://www.dom4j.org/下载
E:HTML---HTMLParser,从http://htmlparser.sourceforge.net/下载,我们需要filterbuilder.jar,htmlparser.jar,htmllexer.jar,thumbelina.jar
F:Lius,用于从各种文档中提取文本信息,只是对PPT文档,只能解析出英文
注:推荐使用Lius,
14:索引创建方式
静态索引,动态索引(增量索引)
IndexWriter indexWriter = new IndexWriter(indexPath, new SimpleAnalyzer(), false);
第三个参数的意思就是:是否覆盖原来的索引,false时为增量索引
15:索引结构:
多文件索引,复合索引
调用indexWriter.useCompoundFile(false),为多文件索引,默认true为附和索引
多文件索引时,会创建很多的文件,在打索引开时,会占用大量的文件句柄资源,造成系统响应慢。而复合索引则会成n倍的减少文件数量,但是,统一文件存储大量数据会造成数据更新比较慢
16:建议不用IndexModifier,它在close时并没有真正删除Document,最好自己封装IndexWriter和IndexReader
17:不要过度使用“过滤器”,会有不小的性能开销
18:Lucene的逻辑组成,个人理解
lucene索引由多个“索引块”(segment)组成,增量索引以新块形式出现,每个“索引块”由多个“文档”(document)组成,每个“文档”由多个“索引域”(field)组成,“分析器”对每个“索引域”进行分析,拆分成很多“索引项”,最终构成了lucene的索引
19:Lucene的物理组成,个人理解
A:通常所说的索引指的是:在某个目录下的所有索引文件
B:多文件索引在创建时,至少会有fdt,fdx,fnm,frq,nrm,prx,tii,tis(这8个可以称为一个索引段)和segments.gen,segments_*10(这两个为独一的)文件,可以有单段多文件索引,也可以有多段多文件索引
C:复合索引在创建时,会将以上8个文件合并,形成一个cfs文件,再加个两个独一的文件,所以,它也有单段复合索引和多段复合索引两种
D:此外,多文件索引和复合索引还可以共存,但只有两个独一文件
E:最后我们通过IndexWriter.optimize(),对所有的文件进行优化成3个文件,cfs,segments.gen和segments_*
------------------------------------------------------------------------------------------------------------
实用分类总结
20:我们大至可以分为:
A:索引的建立和优化
B:索引的管理
C:解析用户请求
D:组合多条件搜索
E:过滤结果,进行排序,高亮处理等
21:索引建立和优化
一:建立
(1)增量索引:IndexWriter indexWriter = new IndexWriter(indexPath, new SimpleAnalyzer(), false);
(2)复合索引:indexWriter.useCompoundFile(true)
(3)Field参数含义,Field field = new Field(名称,内容,存储方式,索引方式);
存储方式有三种:Field.Store.NO(不存储),Field.Store.YES(存储),Field.Store.Compress(压缩存储)
索引方式有四种:Field.Index.NO(不索引),Field.Index.TOKENIZED(分词并索引),Field.Index.UN_TOKENIZED(不分词索引),Field.Index.NO_NORMS(禁用分析器处理)
(4)对不同文档Document使用不同分析器indexWriter.addDocument(doc,new StandardAnalyzer());
(5)限制Field词条数量,indexWriter.setMaxFieldLength(200);
(6)由于lucene采用统一的文档形式,所以对于非格式文本我们需要提前格式化,如数字和时间,最好定长表示,不足的用0补充,以保证检索和排序的正确性
二:优化
(1)利用缓存,减少磁盘读写频率
A:indexWriter.setMergeFactor(10),每10个document合并为一个索引块,每10个索引块合并成一个大索引块,每10个大索引块合并成更大的索引块,依次类推
B:indexWriter.setMaxBufferedDocs(100),用更多的内存换取更快的索引
C:先建立内存索引,再写入磁盘,用RAMDirectory
D:用indexWriter.optimize()自身优化
(2)减少索引文件数量和大小
22:索引的管理
A:了解索引本身信息,我们需要了解HITS,HIT,Document各自的属性和方法,请参照相应的API
B:索引的删除,添加,更新(本质是先删除,后添加)
创建表态索引用和增量索引用:IndexWriter
删除和添加索引用:IndexReader
用IndexReader删除某个Document,只是做了删除标记,不参与检索,要用IndexWriter.optimize()将其物理删除
C:不同目录的索引合并,用indexWriter.addIndexes(Directory directory),先把其中一个索引读入内存后优化,再用此方法加入到另一FSDirectory参数的indexWriter中,优化并关闭,这样这实现了两个索引的合并
D:推荐用Luke,是一套lucene索引管理的软件
23:解析用户请求
A:自己写程序分析,提取用户输入的关键字
B:用lucene自己的QueryParser解析,默认是OR逻辑
24:组合多条件搜索
A:用BooleanQuery可以实现“多索引域”搜索
B:MultiSearcher虽然可以多索引搜索,但实质还是一个一个顺序进行的,可以用ParallelMultiSearcher实现多线程多索引搜索
C:在BooleanQuery中,不能只含有BooleanClause.Occur.MUST_NOT,否则,只会返回空的结果集
D:组合用QueryParser,TermQuery,BooleanQuery,RegexQuery足以满足检索需求,如下:
- String indexPath = "你的索引目录";
- String word_list[] = {"中国","北京"};
- String feild_list[] ={"feild_a","feild_b"};
- IndexSearcher indexSearcher = new IndexSearcher(indexPath);
- Analyzer analyzer = new StandardAnalyzer();
- BooleanQuery boolQuery = new BooleanQuery();
- for(int i = 0;i < 2;i++){
- QueryParser parser = new QueryParser(feild_list[i],analyzer);
- Query query = parser.parse(word_list[i]);
- boolQuery.add(query,BooleanClause.Occur.SHOULD);
- }
- //以上实现了对用户输入的数据实现了lucene自带的QueryParser解析
- //同时在相应的feild_a和feild_b索引域附进行检索
- Term ta = new Term("date","20090101");
- Term tb = new Term("date","20090131");
- RangeQuery rangeQuery = new RangeQuery(ta,tb,true);
- //日期以年月日方式,定才存储,这样才可以得到正确结果,true表示包括边界
- boolQuery.add(rangeQuery,BooleanClause.Occur.MUST);
- Hits hits = indexSearcher.search(boolQuery);
String indexPath = "你的索引目录";String word_list[] = {"中国","北京"};String feild_list[] ={"feild_a","feild_b"};IndexSearcher indexSearcher = new IndexSearcher(indexPath);Analyzer analyzer = new StandardAnalyzer();BooleanQuery boolQuery = new BooleanQuery();for(int i = 0;i < 2;i++){ QueryParser parser = new QueryParser(feild_list[i],analyzer); Query query = parser.parse(word_list[i]); boolQuery.add(query,BooleanClause.Occur.SHOULD);}//以上实现了对用户输入的数据实现了lucene自带的QueryParser解析//同时在相应的feild_a和feild_b索引域附进行检索Term ta = new Term("date","20090101");Term tb = new Term("date","20090131");RangeQuery rangeQuery = new RangeQuery(ta,tb,true);//日期以年月日方式,定才存储,这样才可以得到正确结果,true表示包括边界boolQuery.add(rangeQuery,BooleanClause.Occur.MUST);Hits hits = indexSearcher.search(boolQuery);
由此,我们可以实现,在用lucene自身QueryParser的前提下,
D-1:同一“索引域”中检索不同关键字
D-2:同一“索引域”中检索同一关键字
D-3:不同“索引域”中检索不同关键字
D-4:不同“索引域”中检索同一关键字
是不是够你一般的实际检索了!!
25:检索结果的过滤
A:QueryFilter带有缓存,其它的RangeFilter,PrefixFilter,ChainedFilter都不带有缓存,要用CachingWrapperFilter包装才可
B:尽量不用过滤器
C:两种过滤方式,一种是在搜索结果提取出来后过滤,另一种是把过滤条件加在搜索条件中,后者是其于前者的,由此可见,在搜索前加入太多搜索条件对性能有很大的影响,在有必要更精确的搜索时,还是建议使用前者吧
26:对检索结果进行排序
A:默认排序规则:得分-->时间
B:对多字段排序,如下
- SortField sf1 = new SortField("id",false);//升序
- SortField sf2 = new SortField("date",true);//降序
- SortField fields[] = new SortField[]{sf1,sf2};
- Sort sort = new Sort(fields);
- Hits hits = searcher.search(q,sort);//q是一个TermQuery
SortField sf1 = new SortField("id",false);//升序SortField sf2 = new SortField("date",true);//降序SortField fields[] = new SortField[]{sf1,sf2};Sort sort = new Sort(fields);Hits hits = searcher.search(q,sort);//q是一个TermQuery
27:关于高亮显示
可以参照API和下面这个网址:
http://hi.baidu.com/deepeye/blog/item/83a8e8c4bec788ae8326ac5f.html
另外,我们也可以用Jquery,这个JavaScript框架来实现高亮显示,可以参考下面这个网址
效果一:http://www.cnblogs.com/yjmyzz/archive/2008/10/19/1314494.html
效果二:http://www.webwoo.org/jquery/200811/21-28450.html
28:其它搜索引擎参考:
1:sphinx
http://dev.cgfinal.com/sphinx/sphinx.html
2:MySQL全文检索
http://hi.baidu.com/longchengjiang/blog/item/ef9b05d32527f1083af3cf19.html