lucene的小知识点

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的打开和关闭都是十分消耗时间的,所以尽量使用单例模式来进行处理。

如果只是单纯的将IndexReader设置为static,则会产生无法使用最新的索引的问题。即使有其它用户对索引进行了删除等操作,查询时仍旧会包括已删除的结果,这样会影响数据的准确性。

解决这个问题是使用reopen,不过reopen是在旧的版本中的,在新版本中有openifchange方法来进行Reader的重新打开。

针对writer来说,如果不使用close来进行关闭,那么其所进行的更改则无法提到。不过lucene提供了commit方法来实现索引的提交。

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、

searcher的构造的两种方式

IndexReader是一个线程安全的对象,跟索引目录是一一对应,实例化IndexReader很耗资源,通常搜索时同一个索引目录只需要实例化一个IndexReader即可。
当索引数据比较大的时候,一般把索引数据按照某种规则散列在多个文件目录(如:indexdir0,indexdir01,indexdir02)。

当索引目录有增量更新时,可以使用lucene的reopen方法来加载那些变更过的索引片断,而不是重新加载完整的索引从而节省资源。

那么如何使用reopen呢?

1、searcher的构造方式一

传一个文件路径的字符串或者Directory给searcher, searcher将维护一个内部reader,当本次搜索结束后这个内部reader就会关掉。

2、searcher的构造方式二

传reader给searcher然后构造这个searcher, 那么这个reader在本次搜索结束后不会被关掉, 除非调用reader.close()才能关闭。

因此,必须要用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为分析器对象,而其他没有指定对应分析器的域在处理时都会回退到默认的分析器

你可能感兴趣的:(lucene的小知识点)