背景
全文搜索技术的发展,经历了以下五个阶段的发展,由最初的Ftp文件检索阶段,到现在的网页链接分析阶段以及用户意图识别阶段,已经发展到比较成熟的地步了。
如上图可知,五个阶段的发展顺序,以下简要对每个阶段进行说明:
-
FTP文件检索阶段:将文件存储到FTP服务器中,用户输入精确的文件名去搜索文件,可以得到文件的FTP地址,用户再根据此地址进行文件下载。
-
分类目录导航阶段:将网页分类目保存并展示给用户,用户可以根据所需到具体分类下获取网页的URL,随后根据URL重定向。
-
文件相关性阶段:随着互联网内容的不断丰富,单纯依靠分类来定位一个网页,显得不再那么准确了。为了解决这个问题,搜索引擎引入了全文搜索技术,以此来保证搜索的主题与网页内容具有强相关性。
-
网页链接分析阶段:此阶段主要是利用了外部链接来统计每个网站的重要性和流行性,在用户进行搜索性,结合网站的重要性和流行性进行数据过滤,以此改善搜索信息的质量。
-
用户意图识别阶段:此阶段主要是以用户为中心,力求根据最短的搜索关键字,返回与用户搜索意图最匹配的网页内容,做到千人千面。
其中,网页链接分析阶段的代表作就是谷歌搜索了,而用户意图搜索阶段的代表作就是百度搜索了。但此类的搜索都是属于站外搜索。除了站外搜索,还有站内搜索,比如一些管理后台的全文搜索、电商内的商品搜索等。
现在成熟的全文搜索引擎ElasticSearch、Solr都是基于Lucene搜索引擎的进一步封装,对于了解Lucene的基本原理也是很重要的。
基础使用
创建索引
创建索引需要进行以下操作:
- 创建索引目录:用于存放创建后的索引文件。
- 使用分词器:对内容进行分词处理。
- 创建IndexWriter: 用于索引的创建。
- 创建Document: 使用Document来保存需要添加到索引中的内容。
demo代码如下:
class LuceneServiceImplTest {
@Test
void createIndex() throws IOException {
final String indexPath = "lucene/indexDir/";
//创建索引目录
Path path = Paths.get(indexPath);
File file = path.toFile();
if (!file.exists()) {
//如果文件夹不存在,则创建
file.mkdirs();
}
FSDirectory directory = FSDirectory.open(path);
//使用标准分词器
StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
//创建indexWriter
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(standardAnalyzer);
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//创建document
Document document = new Document();
//document添加域
document.add(new TextField("goodsName", "goods", Field.Store.YES));
document.add(new TextField("goodsPrice", "100", Field.Store.YES));
//创建索引
indexWriter.addDocuments(Collections.singletonList(document));
//提交
indexWriter.commit();
}
}
复制代码
以上代码的作用,就是将goodsName以及goodsPrice作为document的两个Field, 并添加进索引中。运行以上结果,可以从lucene/indexDir目录下有以下文件:
基础概念
在了解Lucene原理前,我们需要对以下概念进行了解。
- Document: 类似数据库内的行或者文档数据库内的文档的概念,一个Index内会包含多个Document。写入Index的Document会被分配一个唯一的ID,即Sequence Number(更多被叫做DocId)。
- Field: 一个Document会由一个或多个Field组成(如下图,从源码可以看出它们之间的关系),Field是Lucene中数据索引的最小定义单位。Lucene提供多种不同类型的Field,例如StringField、TextField、LongFiled。
- Term:Lucene中索引和搜索的最小单位,一个Field会由一个或多个Term组成,Term是由Field经过Analyzer(分词)产生。
- Term Dictionary:Term词典,是根据条件查找Term的基本索引。
- Segment:用段来对索引进行拆分,可以拆分为多个段,段之间也可以合并,以此来避免索引文件膨胀。
根据以上概念,我们整理它们之间的关系图:
创建索引原理
在开始了解创建索引流程前,我们先来看下整体流程:
Lucenef负责的模块图:
由以上两图可以得知,Lucene不负责帮我们创建Docuemnt以及Field, 它仅仅提供了相关功能的,我们还是需要自己去读取文档的内容、创建Document以及Field。
由以上流程图,我们可以看出来,Lucene会使用分词器对文档Field进行相关的分词处理,直到输出Term。以下对分词处理的几个重要的阶段进行说明,如下:
- 去除停词:停词(Stop word)就是一种语言中最普通的一些单词,由于没有特别的意义,因而大多数情况下不能成为搜索的关键词,因而创建索引时,这种词会被去掉而减少索引的大小。英语中停词(Stop word)如:“the”,“a”,“this”等。
- 将单词缩减为词根形式:如“cars ”到“car ”等。
- 将单词转变为词根形式:如“drove ”到“drive ”等。
分词后,得到Term,下一步就是提交给索引组件,索引组件负责创建到排索引来存储Term。其中,Lucene利用了FST数据结构来保存词典,利用跳表来存储倒排表,关系图如下:
最后
关于Lucene是如何保证线程安全的,将会在下一篇进行说明。
参考资料
- 《Elasticsearch实战与原理解析》
- Lucene解析-基本概念
- Lucene全文检索原理和流程