Lucene5.5.2开发入门详解

 

1、 全文检索的概念

常见的全文检索

1) 在window系统中,能够指定磁盘中的某一个位置来搜索你想要获得的东西。这个功能是windows比较经常使用的功能。java

2) 在eclipse中,帮助文档搜索:heltp 》help content,文件搜索ctrl+h,ctrl+shift+R.sql

 

其实eclipse的搜索功能就是用lucene写的数据库

 

3) 在百度和google 中,能够搜索互联网中的信息,有:网页、pdf、word音频、视频等内容。apache

4) Taobao搜索商品。windows

全文检索的应用场景

一、站内搜索

l 一般用于在大量数据出现的系统中,找出你想要的资料。常见的有缓存

l baidu贴吧框架

l 商品网站的搜索等eclipse

l 中关村在线     商品的名称、电脑硬件名称 (CPU)iphone

l 文件管理系统工具

l 对文件的搜索功能。Window的文件搜索

二、垂直搜索

l 是针对 某个行业的搜索引擎

l 是搜索引擎的细分和延伸

l 是针对网页库中的专门信息的整合

l 其特色是专、深、精,并具备行业色彩

l 能够应用于购物搜索、房产搜索、人才搜索

说明

l 从大量的信息中快速、准确地查找出要的信息

l 搜索的内容是文本信息(不是多媒体)

l 全文检索只是一个概念,而具体实现有不少框架,lucene是其中的一种,常见的还有solr,es等。其实solr和es底层也是lucene,只是作了一些封装。

 

2、Lucene与sql性能比较

lsql:好比我要查找某个商品,根据商品名,好比select * from product where doctname like %keywords%,这样查询的话对于数据量少是能够的,但是一旦你的数据量巨大几万几十万的时候,你的性能将会极大的减弱。

l lucene:在索引库里面会把全部的商品名根据分词器创建索引,就比如新华字典,索引对应document,好比输入衬衫,那么就会根据索引迅速的翻到衬衫对应的商品名,时间迅速,性能很好。

 

3、 第一个lucene程序

开发前的准备

1. 版本说明

因为lucene的版本很是多,目前从apach官网上能下载到完整jar包的版本目前只有5.5.2和6.2.0,所以这里选择5.5.2版本,5.5.2版本所须要的jdk须要1.7。

2. 下载地址

http://apache.fayea.com/lucene/java/5.5.2/

3. 本例使用到的jar包

一、 lucene-core-5.5.2.jar(核心包)

二、 lucene-analyzers-common-5.5.2.jar(分词器)

三、 lucene-backward-codecs-5.5.2.jar(兼容老版本)

四、 lucene-queryparser-5.5.2.jar(查询解析)

创建索引

 

步骤:

1. 建立IndexWriter对象

a) 须要指定索引库的位置,索引库的位置多是磁盘,也有多是内存。

b) 须要指定用什么分词器,由于建立索引的时候,会按照必定的切份内容的方式去建立索引,这样才能经过关键字查找到对应的内容。

2. 把JavaBean转化为Document

a) Javabean的每个字段就表明一个field,一个javabean就表明一个document。

3. 利用IndexWriter.addDocument方法增长索引

a) 说明:关于这个方法,实际上作了两件事情,先是建立好内容,再根据指定的分词器分词出关键字,建立索引目录。

 

4. 关闭IndexWriter

说明:使用完以后,要关闭掉资源,由于这里有io流资源相关的操做。另一个缘由关闭indexwriter,同时会提交操做,这样才能建立索引成功。

关键类说明:

IndexWriter是增、删、改索引过程的核心组件。这个类负责建立新索引或者打开已有索引,以及向索引中添加、删除或者更新被索引文档的信息。

IndexWriterConfig获取indexwriter所须要的对象,能够理解为获取indexwriter的一个设置对象,能够设定使用什么分词建立索引,也能够设定建立索引的方式,是追加仍是覆盖。

Directory:在数据库中,数据库中的数据文件存储在磁盘上。索引库也是一样,索引库中的索引数据也在磁盘上存在,咱们用Directory这个类来描述。

Analyzer指定录入的内容按照切分红若干个关键词放入到目录库中。

Field:存放一个键值对。这么理解:相似于数据库的一个字段。Filed的实现有不少种,如:StringField、TextField、IntField。Field包含了三个属性:类型、name、value。不一样的实现体现了不一样的类型,name、value表明的是一对键值对的名称和值。

Document:这么理解:相似于数据库的一条记录。Document的结构为:Document(List<Field>)。

Store:一个枚举类,两个值yes和no,yes表明内容存放在索引库中,no表明内容不存放在索引库中,这里要注意是不存放内容,可是是否会做为索引关键词是另外一回事。

编码实现:

 

注:关于上面状况下使用IntField,StringField,TextField的问题。

IntField:主要在保存相似数量,例如库存数量,须要常常用到范围查询的时候。使用这个字段保存的数据,没法经过正常的关键字查找到。

StringField:不须要分词,须要精确匹配的这段,如数据库的id,地理名等。

TextField:须要分词的字段

索引查看工具luke

搜索

步骤:

1. 建立IndexSearch

a) 须要拿到indexReader对象

2. 建立Query对象

3. 进行搜索

4. 根据获取到的TopDocs,得到总结果数和前N行目录ScoreDoc。

5. 根据目录ID列表获取到document。indexSearcher.doc(int)。

关键类说明:

IndexSearch:是查索引过程的核心组件。

QueryParser 解析用户的查询字符串进行搜索,是一个解析用户输入的工具,能够经过扫描用户输入的字符串,生成Query对象。

Query查询对象。封装要查询的相关信息。

TopDocs索引库目录的一个引用。能够理解为搜索到的目录结果集的引用。主要包含了两个信息,总的记录数和目录结果集。

ScoreDoc一条目录。包含了得分和索引下标。

编码:

删除索引

编码:

说明:获取到的indexwriter必定要关闭,缘由和建立索引的缘由同样。

更新索引

lucene的更新操做与数据库的更新操做是不同的。由于在更新的时候,有可能变换了关键字的位置,这样分词器对关键字还得从新查找,并且还得在目录和内容中替换,这样作的效率比较低,因此lucene的更新操做是删除增长两步骤来完成的。

注:关于索引库优化。

旧版本:每执行一次就生成一个cfs文件。若是增长、删除反复操做不少次,就会形成文件大量增长,这样检索的速度也会降低,因此咱们有必要去优化索引结构。使文件的结构发生改变从而提升效率。

新版本当达到一个数量后会自动优化。

优化lucene能过forceMerge方法来将当小文件达到多少个时,就自动合并多个小文件为一个大文件,由于它的使用代价较高不意见使用此方法,默认状况下lucene会本身合并。

编码:

内存索引库

特色

在内存中开辟一块空间,专门为索引库存放。这样有如下几个特征:

1) 由于索引库在内存中,因此访问速度更快。

2) 在程序退出时,索引库中的文件也相应的消失了。

3) 若是索引库比较大,必须得保证足够多的内存空间。

根据Directory不一样的子类获取到内存索引仍是文件索引库。建立索引的操做基本相似。

编码:

分词器

英文分词器

步骤:切分关键词

去除停用词

转为小写(搜索时不区分大小写,由于分词器会帮你转化)

如:I am a programmer,live in shenzhen,what about you?

分词后的结果为:

i

am

programmer

live

shenzhen

what

about

you

分词代码(无需研究没有什么意义,看分词后的结果就行了):

/**
	 * 英文分词器
	 */
	@Test
	public void testEnAnalyzer() throws Exception{
		
		Analyzer analyzer = new StandardAnalyzer();
		
		String text = "I am a programmer,live in shenzhen,what about you?";
		
		this.testAnalyzer(text, analyzer);
	}
	
	/**
	 * 中文分词器ik
	 */
	@Test
	public void testIKAnalyzer() throws Exception{
		
		Analyzer analyzer = new IKAnalyzer();
		
//		String text = "我是一名程序猿,居住在深圳,你呢?";
		
		String text = "立创商城";
		
		this.testAnalyzer(text, analyzer);
	}

	private void testAnalyzer(String text , Analyzer analyzer) throws Exception {

		TokenStream ts = null;
		
		try {
			
			ts = analyzer.tokenStream("myfield", new StringReader(text));

			// 获取词元文本属性
			CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);

			// 重置TokenStream(重置StringReader)
			ts.reset();
			
			// 迭代获取分词结果
			while (ts.incrementToken()) {
				
				System.out.println( term.toString());
			}
			// 关闭TokenStream(关闭StringReader)
			ts.end(); 

		} catch (IOException e) {
			
			e.printStackTrace();
		} finally {
			
			// 释放TokenStream的全部资源
			if (ts != null) {
				try {
					ts.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

中文分词器

1) 经常使用的分词器有:IKAnalyzer、paoding、mmseg4j、imdict。 这里选用:IKAnalyzer

2) 如何自定义扩展词汇:将IKAnalyzer.cfg.xml拷贝到src下,而后新建一个本身的自定义词典,配置IKAnalyzer.cfg.xml便可。

源码分析入口:

初始化最主要的工做就是读入词典,并将这些词放入内存字典树

1.main2012.dic(关键词)2.quantifier.dic(量词)3.stopword.dic(停用词,可扩展)4.ext.dic(扩展词,可选)   

注意:自定义的词汇文件要注意编码格式:UTF-8无BOM格式,不然分词会没有做用。

关于中文分词器,一定会有一个默认的词库文件。

IK Analyzer2012支持 细粒度切分 和 智能切分。

样例:

  1)文本原文:

  IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版本开始,IKAnalyzer已经推出了3个大版本。

  智能分词结果:

  ikanalyzer | 是 | 一个 | 开源 | 的 | 基于 | java | 语言 | 开发 | 的 | 轻量级 | 的 | 中文 | 分词 | 工具包 | 从 | 2006年 | 12月 | 推出 | 1.0版 | 开始 | ikanalyzer | 已经 | 推 | 出了 | 3个 | 大 | 版本

  最细粒度分词结果:

  ikanalyzer | 是 | 一个 | 一 | 个 | 开源 | 的 | 基于 | java | 语言 | 开发 | 的 | 轻量级| 量级 | 的 | 中文 | 分词 | 工具包 | 工具 | 包 | 从 | 2006 | 年 | 12 | 月 | 推出 | 1.0 | 版 | 开始 | ikanalyzer | 已经 | 推出 | 出了 | 3 | 个 | 大 | 版本

Lucene分页

Lucene的分页,总的来讲有两种形式。 

方式一:在ScoresDocs里进行分页

原理:在lucene里面,每个索引内容都会对应一个不重复的docid,而这一点跟Oralce数据库的伪列rownum同样,偏偏正是因为这个docid的 存在,因此让lucene在海量数据检索时从而拥有更好的性能,咱们都知道Oracle数据库在分页时,使用的就是伪列进行分页,那么个人lucene也是同样,既然有一个docid的存在,那么就能够把数据所有拿到内存中,根据docid进行分页。 

编码实现:

方式二:利用SearchAfter,再次查询分页

原理:先拿到上一页最后一个ScoreDoc,利用lucene提供的SearchAfter和获得的ScoreDoc查询当前页的索引结果。

编码实现:

两种方式的优缺点对比

编号

方式

优势

缺点

1

在ScoresDocs里进行分页

无需再次查询索引,速度很快

在海量数据时,会内存溢出

2

利用SearchAfter,再次查询分页

适合大批量数据的分页

再次查询,速度相对慢一点,但能够利用缓存弥补

 

搜索方式Query

一、TermQuery

关键字查询,不分词。

Query query = new TermQuery(new Term("id", "1"));

二、MatchAllDocsQuery

查询全部。

Query query = new MatchAllDocsQuery();

三、WildcardQuery

通配符查询。?:匹配任意一个字符,*匹配多个任意字符

Query query = new WildcardQuery(new Term("name","i?"));

四、PhraseQuery

同个字段多个关键字组合查询,而且的关系。不分词,关键字区分大小写

Query query = new PhraseQuery("name", "iphone","6plus");

五、BooleanQuery

多个字段关键字组合查询

BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();

booleanQuery.add(new TermQuery(new Term("name", "iphone")), Occur.MUST);

booleanQuery.add(new TermQuery(new Term("description", "grey")), Occur.MUST);

Query query = booleanQuery.build();

六、NumericRangeQuery

范围查询,只针对IntField、FloatField这些数字类型的Field才能进行范围查询。

Query query = NumericRangeQuery.newIntRange("id", 1, 2, true, true);

说明:第四个参数和第五个参数,表明是否大于等于的意思,true即为包含。

排序

观察源码,关于IndexSearcher.search有不少重载的方法,其中有方法能够传递Sort对象进去实现排序,以下图:

构造Sort字段实现排序,其中,这里要注意Lucene5.x的排序相比lucene4.x以前改动较大。

主要在排序字段须要在创建索引的时候单独指定,即不能根据索引的内容字段或者关键字字段去排序。以下图,若是某个字段须要支持排序,须要额外构建下面类型的Field字段,不然没法排序。

编码实现:

4、相关文件

IK分词器:http://pan.baidu.com/s/1dFMcxnb

luke:http://pan.baidu.com/s/1dE0N2d7

相关文章
相关标签/搜索