小弟我理解的lucene原理(初级)

我理解的lucene原理(初级)

1 先上一段代码,分析最简单的lucene的Hello World代码

 

package cn.itcast.lesson;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.Field.TermVector;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.junit.Test;

public class HelloWorld {
	String title = "IndexWriter.addDocument 's javadoc";
	String content = "Adds  a document to this index. If the document contains more than setMaxFieldLength(int) terms for a given field, the remainder are discarded.";

	String indexPath = "d:/luceneIndex_20100316/"; // 索引库所在的文件夹
	Analyzer analyzer = new StandardAnalyzer();    // 分词器

	/** 建立索引 */
	@Test
	public void createIndex() throws Exception {
		// 1,把对象转为Lucene的Document
		Document doc = new Document();
		
		Field field = new Field("title", title, Store.YES, Index.ANALYZED);
		Field field2 = new Field("content", content, Store.YES, Index.ANALYZED);
		doc.add(field);
		doc.add(field2);
		/**
		 * 上面代码分析:
		 * 其中创建了document,什么是document?在源码中我们发现,document中有一个名为field名称的List集合。
		 * 这个list里面存放的为Filed类型的数据。
		 * 每一个Field文件类似于Map文件,他有一个name和value值。value只接受字符串(非字符串类型要先转换成字符串才行)。
		 * 找到Field的构造函数 public Field(String name, String value, Store store, Index index, TermVector termVector)
		 * Store,指定Field是否或怎样存储。--- Store.NO,不存储。 Store.YES,存储。Store.COMPRESS,压缩后存储
		 * Index,指定Field是否或怎么被索引。----Index.NO,不索引(不索引就不能被搜索到)。Index.ANALYZED,分词后索引。Index.NOT_ANALYZED,不分词,直接索引(把整个Field值做为一个term)。
		 */
		
		
		// 2,把Document添加到索引库中
		IndexWriter indexWriter = new IndexWriter(indexPath, analyzer, MaxFieldLength.LIMITED);
		indexWriter.addDocument(doc);
		indexWriter.close();
		
		/**
		 * 上面代码分析:
		 * IndexWriter是Lucene用来创建索引的一个核心的类。使用构造方法IndexWriter(Directory d, Analyzer a, MaxFieldLength mfl); 
		 * Directory,代表了 Lucene 的索引的存储的位置。这是一个抽象类,常用的有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引位置。
		 * Analyzer,在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给IndexWriter 来建立索引。
		 * MaxFieldLength,用于限制Field的大小。这个变量可以让用户有计划地对大文档Field进行截取。假如取值为10000,就只索引每个Field的前10000个Term(关键字)。也就是说每个Field中只有前10000个Term(关键字)建立索引,除此之外的部分都不会被Lucene索引,当然也不能被搜索到。
		 */
	}

	/** 搜索 */
	@Test
	public void search() throws Exception {
		String queryString = "document";

		IndexSearcher indexSearcher = new IndexSearcher(indexPath);

		QueryParser queryParser = new QueryParser("content", analyzer);
		Query query = queryParser.parse(queryString);
		Filter filter = null;
		int n = 10000;
		/**
		 * 查询方法为IndexSearcher.search(Query, Filter, int);
		 * Query,查询对象,把用户输入的查询字符串封装成Lucene能够识别的Query。
		 * Filter,用来过虑搜索结果。
		 * 第三个参数(int类型),最多返回的Document的数量。
		 * 返回的是一个TopDocs类型的对象,调用TopDocs.scoreDocs得到查询结果。
		 */
		TopDocs topDocs = indexSearcher.search(query, filter, n); // 搜索
		
		
		

		System.out.println("共有【" + topDocs.totalHits + "】条匹配结果"); // 匹配的结果的数量
		for( ScoreDoc scoreDoc : topDocs.scoreDocs ){
			int docSn = scoreDoc.doc; // 文档的内部编号
			Document doc = indexSearcher.doc(docSn); // 通过编号取出文档
			System.out.println("------------------");
			System.out.println("title   = " + doc.getField("title").stringValue());
			System.out.println("content = " + doc.getField("content").stringValue());
		}
		
		indexSearcher.close();
	}
}

 

 

2  分析lucene的原理

 

 索引文件的保存形式。   索引文件共分为两个部分(索引区和数据区);

 

 

 

索引区 索引区里面存放的均为关键字和什么类型的关键字。并且存放该关键字所对应的数据区内容的标号。(倒序索引)
数据区 数据区中保存的是Document,docuemnt中里面是filed。Filed可以理解为map,里面的数据均为String类型。

 

当开始建立索引时,lucene把内容进行分词,分词产生的关键字存储到索引区中去,并且标注该关键字的类型,如关键字为netease类型为title的索引“netease——title”,其中保存一系列引号去指向数据区中的field。当然这些filed中就是还有该索引的内容。这也就是“倒序索引

当我们进去搜索时,输入关键字后,lucene把我们输入的关键字进行分析,产生了相应的关键字,去到索引区中去查找,如果找到相匹配的信息,就按照索引区中保存的索引号去找到document里面的field,并把这个值返回给view层。

 

3 lucene帮助

 


*** 1,Directory,
有两个:
  a) FSDirectory,将索引放到磁盘中。
  b) RAMDirectory,将索引放到内存中。速度快,但是在jvm退出之后,内存中
的索引就不存在了。可以在jvm退出之前调用另一个使用FSDirectory的
IndexWriter 把内存中的索引转存到文件系统中。
    相应的API为IndexWriter.addIndexesNoOptimize(Directory[]),注意这个调
用代码要放在内存中索引的操作RAMDirectory的IndexWriter关闭后,以便所有的
document都进入RAMDirectory。在创建RAMDirectory的实例时,可以使用无参的构
造方法,或是有一个参数的构造方法:可以指定一个文件路径,以便加载磁盘中的索
引到内存中。


*** 2,相关度排序,
Lucene的搜索结果默认按相关度排序的,所谓相关度,就是文档的得分。Lucene
有一个评分机制,就是对检索结果按某种标准进行评估,然后按分值的高低来对
结果进行排序。

a) 文档的得分与用户输入的关键字有关系,而且是实时运算的结果。得分会受关
键字在文档中出现的位置与次数等的影响。

b) 可以利用Boost影响Lucene查询结果的排序,通过设置Document的Boost来影响
文档的权重,以达到控制查询结果顺序的目的:Document.setBoost(float)。默
认值为1f,值越大,得分越高。

c) 也可以在查询时给Filed指定boost。当同样的term属于不同的field时,如果
field的boost不一样,其所属的文件的得分也不一样。
后面.

*** 3,Analyzer,
分词器,对文本资源进行切分,将文本按规则切分为一个个可以进行索引的最小
单位(关键字)。

对于英文的分词,是按照标点、空白等拆分单词,比较简单;而中文的分词就比
较复杂了,如”*“可以分为”中华”、“人民”、“*”,但不应有“华人”
这个词(不符合语义)。

中文分词几种常用的方式:
a) 单字分词,就是按照中文一个字一个字地进行分词。如:我们是中国人,效果:
  我\们\是\中\国\人。(StandardAnalyzer就是这样)。
b) 二分法:按两个字进行切分。如:我们是中国人,效果:我们\们是\是中\中
  国\国人。(CJKAnalyzer就是这样)。
c) 词库分词:按某种算法构造词,然后去匹配已建好的词库集合,如果匹配到就切
  分出来成为词语。通常词库分词被认为是最理想的中文分词算法,如:我们是中
  国人,效果为:我们\中国人。(使用MMAnalyzer)。


*** 4,Highlighter,高亮器,用于高亮显示匹配的关键字。
包含三个主要部分:
1) 格式化器:Formatter(使用SimpleHTMLFormatter,在构造时指定前缀和后缀)。
2) 计分器:Scorer(使用QueryScorer)。
3) 段划分器:Fragmenter(使用SimpleFragmenter,在构造时指定关键字所在的
   内容片段的长度)。

通过方法getBestFragment(Analyzer a, String fieldName,String text)实现高亮。
(如果进行高亮的field中没有出现关键字,返回null)。


* 5,查询,有两种方式:通过Query Parser解析查询字符串或使用API构建查询。
使用Query Parser时不匹分大小写。以下是常用的查询:

1) TermQuery,按Term(关键字)查询(term的值应是最终的关键字,英文应全部小写)。
   TermQuery(Term t);
   syntax: propertyName:keyword;

2) RangeQuery,指定范围查询。
   RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive);
   syntax: propertyName:[lower TO upper](包含lower和upper);
   syntax: propertyName:{lower TO upper}(不包含lower和upper);

3) PrefixQuery,前缀查询。
   PrefixQuery(Term prefix);
   syntax:propertyName:prefix*;

4) WildcardQuery,通配符查询,可以使用"?"代表一个字符,"*"代表0个或多个字符。
   (通配符不能出现在第一个位置上)
   WildcardQuery(Term term);
   syntax: propertyName:chars*chars?chars

5) MultiFieldQueryParser,在多个Field中查询。
   queryParser = MultiFieldQueryParser(String[] fields, Analyzer analyzer);
   Query query = parse(String query);
  
6) BooleanQuery,布尔查询。这个是一个组合的Query,可以把各种Query添加进
   去并标明他们的逻辑关系。
   TermQuery q1 = new TermQuery(new Term("title","javadoc"));
   TermQuery q2 = new TermQuery(new Term("content","term"));
   BooleanQuery boolQuery = new BooleanQuery();
   boolQuery.add(q1, Occur.MUST);
   boolQuery.add(q2, Occur.MUST_NOT);
  
   Occur.MUST,必须出现。
   Occur.MUST_NOT,必须未出现,不要单独使用。
   Occur.SHOULD,只有一个SHOULD时为必须出现,多于一个时为或的关系。

   syntax: + - AND NOT OR (必须为大写)。

 

 

1 楼 hanwesley 2010-10-15  
谢谢,我在你的基础之上学习的。感觉还不错。