Lucene索引建立的过程,类似于将数据进行关键字提取,设置标签,在后续工作中,可以通过这个标签进行内容过滤获取期望数据的操作。

lucene建立索引过程很简单,归结起来,就是:

获取数据 -> 设置建立索引规则 -> 建立索引 -> 写入磁盘/内存

建立索引涉及几个类:

1、 IndexWriter

执行索引写操作,并且控制索引建立过程的各种参数。包括参数:

1)索引存放位置

索引存放位置有两种:磁盘 和 内存

所以以下几种方式来建立IndexWriter:

// 建立于磁盘中1

IndexWriter indexWriter = new IndexWriter("c://index",analyzer,true);

// 建立磁盘中2

IndexWriter indexWriter = new IndexWriter(new File("c://index"),analyzer,true);

// 在内存中建立索引

IndexWriter indexWriter = new IndexWriter(new RAMDirectory(),analyzer,true);

2)分词器

分词器是一个重要的成分,在写入索引之前,将文本信息切割成为多个可以用于索引的词条,分词器继承于 org.apache.lucene.analysis.Analyzer ,lucene内置了一系列分词器,但是相当较为简单,目前有一些开源组织开发的分词器,比如IK_CAnalyzer等(IK_CAnalyzer这个类在提供的jar中式final的,说明了这个开发者相对保守,无法继承重写,不开源)

 

3)是否重新建立索引

true:清空、重新建立索引

false:增量建立索引

 

2、Document


document是一条索引内容的集合,一条document类似于一条数据库记录,有自己的字段和字段对应的值,类似如下结构

[<key1,value>,<key2,value>,<key3,value>...]

document建立在vector之上,对其中“字段”的操作,都直接体现在vector的操作。

建立方式如下:

Document document = new Document();

 

3、Field

 

相对于Document,其中的字段,即Field,即具体的一个索引


* Field.Index有四个属性,分别是:
Field.Index.TOKENIZED:分词索引
Field.Index.UN_TOKENIZED:分词进行索引,如作者名,日期等,Rod Johnson本身为一单词,不再需要分词。
Field.Index:不进行索引,存放不能被搜索的内容如文档的一些附加属性如文档类型, URL等。
Field.Index.NO_NORMS:;
* Field.Store也有三个属性,分别是:
Field.Store.YES:索引文件本来只存储索引数据, 此设计将原文内容直接也存储在索引文件中,如文档的标题。
Field.Store.NO:原文不存储在索引文件中,搜索结果命中后,再根据其他附加属性如文件的Path,数据库的主键等,重新连接打开原文,适合原文内容较大的情况。
Field.Store.COMPRESS 压缩存储;
* termVector是Lucene 1.4.3新增的它提供一种向量机制来进行模糊查询,很少用。
1.1 Field 的解释
从源代码中,可以看出Field 构造函数如下:
Field(String name, byte[] value, Field.Store store)
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
Field(String name, String value, Field.Store store, Field.Index index)
Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector)

在Field当中有三个内部类:Field.Index,Field.Store,Field.termVector。其中

* Field.Index有四个属性,分别是:
Field.Index.TOKENIZED:分词索引 analysis?
Field.Index.UN_TOKENIZED:分词进行索引,如作者名,日期等,Rod Johnson本身为一单词,不再需要分词。不同?
Field.Index:不进行索引,存放不能被搜索的内容如文档的一些附加属性如文档类型, URL等。
Field.Index.NO_NORMS:;


col:Store, row:Index

YES

NO

COMPRESS

NO

存储,但是不建立索引,当然也就不分析。这样的字段无法搜索,但是会出现在搜索结果中。

无意义。引发Illegal Argument Exception。

基本等同于YES,但是外加了压缩。对于较长的文本和二进制的字段,应该选用这个参数。计算量更大。

ANALYZED

分析,索引,存储。

分析,索引,不存储。

分析,索引,压缩存储。

NOT_ANALYZED

不分析,但是索引,存储。

不分析,直接索引,但是不存储。

不分析,但是索引,压缩存储。

NOT_ANALYZED_NO_NORMS

(高级)

(高级)

(高级)

ANALYZED_NO_NORMS

(高级)

(高级)

(高级)


/**
	 * 获取IndexWriter
	 * @param searchConfigure
	 * @return
	 * @throws CorruptIndexException
	 * @throws LockObtainFailedException
	 * @throws IOException
	 */
	private IndexWriter getIndexWriter(SearchConfigure searchConfigure, boolean isBuildNew) throws CorruptIndexException, LockObtainFailedException, IOException{
		String floder = getFloder(searchConfigure);
		// 默认的分词器
		Analyzer analyzer = new IK_CAnalyzer();
		// 可以控制不同字段不同分词器
		PerFieldAnalyzerWrapper preFiledAnalyzerWrapper = new PerFieldAnalyzerWrapper(analyzer);
		// 设置特定的域 - 特殊的不需要使用IK分词的使用空格分词器,这样一般适用于那些不适用拆分的字段
		List<String> fieldsNotNeedAnalyzer = searchConfigure.getFieldsNoNeedAnalyzer();
		if(null != fieldsNotNeedAnalyzer){
			for(String field:fieldsNotNeedAnalyzer){
				preFiledAnalyzerWrapper.addAnalyzer(field, new WhitespaceAnalyzer());
			}
		}
		
		IndexWriter indexWriter = new IndexWriter(floder, preFiledAnalyzerWrapper, isBuildNew);
		// 控制写入一个新的segment前在内存中保存的最大的document数目
		indexWriter.setMaxBufferedDocs(500);
		// 控制多个segment合并的频率,最大文档合并数
		indexWriter.setMaxMergeDocs(Integer.MAX_VALUE);
		// 是否使用复合索引
		// indexWriter.setUseCompoundFile(true);
		
		return indexWriter;
	}
......//
while (resultIndex.hasNext()) {
					// 每一个新建document,防止field重名覆盖
					Document document = new Document();
					Object object = resultIndex.next();
					Iterator<String> fieldIndex = fieldList.iterator();

					while (fieldIndex.hasNext()) {
						// 获取需要分页的域
						String field = fieldIndex.next();
						// 过滤空白
						if (StringUtils.isBlank(field)) {
							continue;
						}
						// 获取值
						Object value = PropertyUtils.getProperty(object, field);
						
						// 判断是否是需要转义的字段
						if(convers.contains(field.trim())){
							// 获取转以后的值
							value = luceneDataAdaptor.convertData(field.trim(), value.toString());
						}
						
						// 写入doc 是否存储/是否切割   现在对不需要分词的字段不进行切割
						document.add(new Field(field, value.toString(), Field.Store.YES, Field.Index.TOKENIZED));
						//searchConfigure.getNeedsConvert().contains(field)? Field.Index.UN_TOKENIZED:Field.Index.TOKENIZED
					}
					// 写入索引文件
					indexWriter.addDocument(document);
				}// while
indexWriter.optimize();
indexWriter.close();
....//