学习Lucene的原因:以前使用数据库精确搜索,但是其使用(%和like)导致其速度慢、功能不强、性能低;所以我们今天来学习 Lucene相关的搜索引擎。
- 原来的方式实现搜索功能,我们的搜索流程如下图:
2.现在的方案(使用Lucene),如下图:
什么是Lucene:Lucene是Apache的一个全文检索引擎工具包,通过lucene可以让程序员快速开发一个全文检索功能。但它不是 一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引 擎。Lucene并不是现成的搜索引擎产品,但可以用来制作搜索引擎产品。
Lucene与搜索引擎的区别:
Lucene和搜索引擎不同,Lucene是一套用java或其它语言写的全文检索的工具包,为应用程序提供了很多个api接 口去调用,可以简单理解为是一套实现全文检索的类库,搜索引擎是一个全文检索系统,它是一个单独运行的软件 系统。
全文检索的定义:
全文检索首先对要搜索的文档进行分词,然后形成索引,通过查询索引来查询文档。
全文检索就是先创建索引,然后根据索引来进行搜索的过程,就叫全文检索。
Lucene实现全文检索的流程图:
通过Java代码调用Lucene API实现对索引库的增删改查,索引库数据来源于数据库,所以增加操作需要先从数据库 将数据查询出来,再调用Lucene API将数据加入到索引库中。
Lucene实现全文检索的思路示意图:
全文检索的流程分为两大部分:索引流程、搜索流程。
- 索引流程:即采集数据->构建文档对象->分析文档(分词)->创建索引。
- 搜索流程:即用户通过搜索界面输入->创建查询->执行搜索,搜索器从索引库搜->渲染搜索结果。
为什么要采集数据:
全文检索搜索的内容的格式是多种多样的,比如:视频、mp3、图片、文档等等。对于这种格式不同的数据,需要先将他们采集到本地,然后统一封装到lucene的文档对象中,也就是说需要将存储的内容进行统一才能对它进行查询。
采集数据的方式:
- 对于互联网中的数据,使用爬虫工具(http工具)将网页爬取到本地
- 对于数据库中的数据,使用jdbc程序进行数据采集
- 对于文件系统的数据,使用io流采集
搜索文件的逻辑结构:
- 文档域
文档域存储的信息就是采集到的信息,通过Document对象来存储,具体说是通过Document对象中field域来存储数据。
比如:数据库中一条记录会存储一个一个Document对象,数据库中一列会存储成Document中一个field域。
文档域中,Document对象之间是没有关系的。而且每个Document中的field域也不一定一样。
- 索引域
索引域主要是为了搜索使用的。索引域内容是经过lucene分词之后存储的。
- 倒排索引表
传统方法是先找到文件,如何在文件中找内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大就搜索慢。
倒排索引结构是根据内容(词语)找文档,倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它是在索引中匹配搜索关键字,由于索引内容量有限并且采用固定优化算法搜索速度很快,找到了索引中的词汇,词汇与文档关联,从而最终找到了文档。
创建索引的流程图:
注:IndexWriter是索引过程的核心组件,通过IndexWriter可以创建新索引、更新索引、删除索引操作。IndexWriter需要通过Directory对索引进行存储操作。Directory描述了索引的存储位置,底层封装了I/O操作,负责对索引进行存储。它是一个抽象类,它的子类常用的包括FSDirectory(在文件系统存储索引)、RAMDirectory(在内存存储索引)。
分词器:
在对Docuemnt中的内容进行索引之前,需要使用分词器进行分词 ,分词的目的是为了搜索。分词的主要过程就是先分词后过滤。
分词:将field域中的内容一个个的分词。 停用词:单独应用没有特殊意义的词。比如的、啊、等,英文中的this is a the等等。
过滤:将分好的词进行过滤,比如去掉标点符号、大写转小写、词的型还原(复数转单数、过去式转成现在式)、停用词过滤
Field域:
Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个Field,Document只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。
Field中的三个非常重要的三个属性 :
- 是否分词(Tokenized)
是:对该field存储的内容进行分词,分词的目的,就是为了索引。
比如:商品名称、商品描述、商品价格
否:不需要对field存储的内容进行分词,不分词,不代表不索引,而是将整个内容进行索引。
比如:商品id
- 是否索引(Indexed)
是:将分好的词进行索引,索引的目的,就是为了搜索。
比如:商品名称、商品描述、商品价格、商品id
否:不索引,也就是不对该field域进行搜索。
- 是否存储(Stored)
是:将field域中的内容存储到文档域中。存储的目的,就是为了搜索页面显示取值用的。 - 例如:商品介绍。如果需要展示,根据ID从数据库查询展示在详情页面
-
否:不将field域中的内容存储到文档域中。不存储,则搜索页面中没法获取该field域的值。
比如:商品描述,由于商品描述在搜索页面中不需要显示,再加上商品描述的内容比较多,所以就不需要进行存储。
如果需要商品描述,则根据搜索出的商品ID去数据库中查询,然后显示出商品描述信息即可。
Field的常用类型:
Field类 | 数据类型 | Analyzed是否分词 | Indexed是否索引 | Stored是否存储 | 说明 |
---|---|---|---|---|---|
StringField(FieldName, FieldValue,Store.YES)) | 字符串 | N | Y | Y或N | 这个Field用来构建一个字符串Field,但是不会进行分词,会将整个串存储在索引中,比如(订单号,身份证号等)是否存储在文档中用Store.YES或Store.NO决定 |
LongField(FieldName, FieldValue,Store.YES) | Long型 | Y | N | Y或N | 这个Field用来构建一个Long数字型Field,进行分词和索引,比如(价格)是否存储在文档中用Store.YES或Store.NO决定 |
StoredField(FieldName, FieldValue) | 重载方法,支持多种类型 | N | N | Y | 这个Field用来构建不同类型Field不分析,不索引,但要Field存储在文档中 |
TextField(FieldName, FieldValue, Store.NO)或TextField(FieldName, reader) | 字符串或流 | Y | Y | Y或N | 如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略. |
我想提到Lucene,不得不提ElasticSearch了。
很多刚接触Lucene和ElasticSearch的人都会问这个明显的问题:我应该使用Lucene还是ElasticSearch?
答案很简单:如果你问自己这个问题,在99%的情况下,你想使用的是ElasticSearch. 形象的来说ElasticSearch和Lucene之间关系的方式是汽车及其引擎。 你不能驾驶一台发动机,但可以开一辆汽车。 同样,Lucene是一个程序化库,您不能按原样使用,而ElasticSearch是一个完整的应用程序,您可以立即使用它。