1、实现查询类TermQuery怎么去关联Analyzer?
QueryParser检索的时候就必须指定。 TermQuery不需要。 TermQuery是根据分词后的Term来检索的。所以不需要啦。
http://code.google.com/p/luke/ (对应你Lucene的版本)能够查看lucene生产的那些索引文件。 这里你能看到有些域进行分词以后的Term。
如果只下载了luke的jar包,那么运行方式如下:
2、QueryParser搜索有多个字的时候默认是或关系的搜索,要转化为与的关系的搜索,如下:
第一种方式:
QueryParser parser = new QueryParser(Version.LUCENE_35, field, new StandardAnalyzer(Version.LUCENE_35)); //下面这么写的话在对用户输入进行扫描时,就会用空格分开的关键字理解为“与”, //其实也就是构建了一个“与”关系的布尔型查询 parser.setDefaultOperator(Operator.AND); Query query = parser.parse(keyword);
第二种方式:
是把每个词进行分出来,然后自己组织Lucene的表达式
QueryParser parser = new QueryParser(Version.LUCENE_35, field, new StandardAnalyzer(Version.LUCENE_35)); String k = analyzerKey(name); Query query = parser.parse(k);
private String analyzerKey(String key){ StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_35); StringReader reader = new StringReader(key); TokenStream tokenStream = analyzer.tokenStream("", reader); CharTermAttribute termattr = tokenStream.addAttribute(CharTermAttribute.class); StringBuilder sb = new StringBuilder(); try { while(tokenStream.incrementToken()){ String k = termattr.toString(); sb.append(k).append(" "); } } catch (IOException e) { e.printStackTrace(); } key = sb.toString().trim(); key = key.replaceAll("\\s+", " AND "); return key.toString(); }
3、如果你是使用词库分词的,比如IK,MM,庖丁等,如果你是使用TermQuery这种基于Term的词单位来进行搜索的话,默认是你输入的关键字在索引所分出的词能找到才会有结果,那么这时候你有两种方式进行解决:
第一种:把要搜索的词用分词器进行分成若干个词语,然后对每个进行TermQuery等的检查,然后用BooleanQuery进行关联查询
第二种:使用QueryParser来进行查询,这个类会让你传入一个分词器,会把你传入的词进行分词之后然后再进行搜索
QueryParser parser = new QueryParser(Version.LUCENE_35, field, analyzer);
4、
lucene3.5版本的数字索引存储和数字区间查询查询有了变化
NumberTools这个类进行标记为过时了,这个类有两个方法,
一个是longToString把long型转化为string型在索引中存储,因为lucene中数字的长度要相等,比如说200和1000在比较的时候是200大,要把200写成0200才行,所以这个工具就是帮助转化的
另一个是stringToLong,这个是在得到结果的时候把lucene中得到的值进行转化为数字
DateTools这个类目前没有被标记为过时的。
以前查询数字区间的话是使用TermRange来进行的,现在改成了
Query query = NumericRangeQuery.newIntRange(field,start, end,true,true);
但是如果使用这种查询的话这个field在建立索引的时候不能使用普通的new Field 而要使用new NumericField才行
5、Lucene中reader和writer的打开和关闭的问题
Lucenek中的IndexReader和IndexWriter的打开和关闭都是十分消耗时间的,所以尽量使用单例模式来进行处理。
6、QueryParser的查询语法:
Mike |
默认域包含mike |
Mike john Mike OR john |
默认域包含mike或者john |
+mike +address:zhaotong Mike AND address:zhaotong |
默认域即使mike并且address是zhaotong |
id :2 |
Id域为2 |
Address:Kunming –desc:she Address:Kunming AND NOT desc:she |
Address是kunming并且desc不是she |
(mike OR john) AND address:zhaotong |
默认域是mike或者john 并且address是zhaotong |
Desc: “she like” |
Desc域是she like |
desc:”happy girl”~5 |
查找happy和girl之间距离小于5的文档 |
J* |
默认域是j开头 |
Johe~ |
模糊搜索johe |
Id:[“1” TO “3”] |
Id从1到3 |
7、Lucene删除文档,使用IndexReader还是IndexWriter?
从IndexReader deleteDocuments方法可知:“Once a document is deleted it will not appear in TermDocs or TermPostitions enumerations.”
即:reader进行删除后,此reader马上能够生效,而用writer删除后,会被缓存,只有写入到索引文件中,当reader再次打开的时候,才能够看到。
使用IndexWriter进行删除操作,删除后对删除的内容任然可以检索到,说明在当前IndexWriter实例当中删除的内容被缓存起来,并未马上生效!注意:
即使IndexWriter在删除后进行提交(commit操作),也不会对删除动作马上生效。
IndexReader进行删除操作后,对删除的内容已不能检索,说明当前IndexReader实例已经对删除生效。
从以上说法我们可以清楚了解IndexWriter和IndexReader在删除文档方面的不同之处,不过在此建议:
在应用中使用IndexWriter进行删除操作!
为什么呢?
1.在实时索引当中,我们通常会对一份磁盘索引用IndexWriter打开,然后一份磁盘索引被IndexWriter打开后,IndexReader的删除操作将无法进行,否则会报LockObtainFailedException异常。
2.Lucene的更新操作都是先删除后再添加的过程,如果使用IndexReader删除后,该动作马上生效,而添加动作又不会马上可见,会造成数据上的混乱(至少是理解上的混乱)。
例如:用户更新文档A,用IndexReader先删除A文档,然后再添加修改过后的A文档,此时若添加的文档未被IndexSearcher重新打开,而用户对A文档进行检索发现检索不出A文档,此时将产生数据不一致。
8、lucene中不输入搜索的字符串让它全部输出MatchAllDocsQuery matchAll = new MatchAllDocsQuery();//这里不需要输入参数
TopDocs topDocs=indexSearcher.search(matchAll,pageSize*curPage);
9、 当磁盘索引被IndexSearcher打开时,为什么优化操作后索引容量翻倍?当IndexSearcher打开一份磁盘索引后,IndexSearcher实例持有的是打开索引时的一份镜像索引,而执行优化动作后,将会把优化后的索引存储到原磁盘路径下,导致索引容量翻倍。
那么怎么解决这个问题呢?IndexSearcher实例执行re-open动作,然后再执行优化操作,此时的优化操作很快,相当于是删除之前IndexSearcher持有的镜像索引。
特别是在实时索引过程中,对大索引的优化一般比较耗时,因此合理的解决方案是:单独开一条线程执行优化操作,当优化完成后对IndexSearcher实例进行re-open操作,然后再执行一次优化操作。
在这一系列过程中,不会因为优化操作而对检索服务造成影响,也不会占用额外的磁盘空间(第二次优化的时候会将重复的索引文件进行删除,节约磁盘空间)。
但是必须注意的是:在第一优化操作时,必须保证空闲磁盘空间大于目前的索引容量。
10、lucene的多线程索引
lucene一个索引只能有一个indexWriter。但是可以一个indexWriter被多个线程共享
11、
在Lucene3.X版本中,与前几个版本的不同的地方包括了IndexWriter实例的初始化,其中需要用到IndexWriterConfig这个类。另外,从Lucene的API中可以看到目前IndexWriter类最新的构造函数需要用到IndexWriterConfig这个类(其它的构造函数不建议使用,被废弃掉了),其中需要设置OpenMode属性:
iwconf.setOpenMode(OpenMode.CREATE); //或 iwconf.setOpenMode(OpenMode.APPEND); //或 iwconf.setOpenMode(OpenMode.CREATE_OR_APPEND);注意:这行代码设置了存放索引的文件夹将以覆盖或者新建的方式建立。如果没有这样设置,并且在原索引文件夹中索引没有被删除的情况下,新的索引文件将会append到原来索引文件之后,这样会导致索引结果发生错误。
12、lucene的docId是从0开始的
13、
IndexReader是一个线程安全的对象,跟索引目录是一一对应,实例化IndexReader很耗资源,通常搜索时同一个索引目录只需要实例化一个IndexReader即可。
当索引数据比较大的时候,一般把索引数据按照某种规则散列在多个文件目录(如:indexdir0,indexdir01,indexdir02)。
当索引目录有增量更新时,可以使用lucene的reopen方法来加载那些变更过的索引片断,而不是重新加载完整的索引从而节省资源。
那么如何使用reopen呢?
因此,必须要用reader去构造searcher,然后通过searcher.getIndexReader()可以获取当前searcher的reader,
再用reader.iscurrent()判断索引文件是否有变化,如果索引文件有变化,那么先关闭当前的searcher,再通过reader.reopen()获取新的reader,然后重新创建新的searcher。
如果是通过上面提到的构造方式一的方式获得的searcher的话,就不能使用reopen,否则会报reader已经关闭的异常。
14、针对每个域使用不同的分析器
Map<String,Analyzer> fieldAnalyzers = new HashMap<String,Analyzer>(); fieldAnalyzers.put("nameSingle", new ChineseAnalyzer()); PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(AnalyzerUtil.getAnalyzer(), fieldAnalyzers);在创建PerFieldAnalyzerWrapper时,你需要提供默认的分析器,然后对于需要使用不同的分析器进行处理的域来说,你可以在构造方法中传入一个map,key为域的名称,value为分析器对象,而其他没有指定对应分析器的域在处理时都会回退到默认的分析器