什么是全文检索?
什么叫做全文检索呢?这要从我们生活中的数据说起。我们生活中的数据总体分为两种:
结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。
非结构化数据(也叫全文数据) :指不定长或无固定格式的数据,如邮件,word文档等。
半结构化数据:如XML,HTML等,当根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。
按照数据的分类,搜索也分为两种:
对结构化数据的搜索 :如对数据库的搜索,用SQL语句。再如对元数据的搜索,如利用windows搜索对文件名,类型,修改时间进行搜索等。
对非结构化数据的搜索 :如利用windows的搜索也可以搜索文件内容,Linux下的grep命令,再如用Google和百度可以搜索大量内容数据。
对非结构化数据也即对全文数据的搜索主要有两种方法:
顺序扫描法 (Serial Scanning)
所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。如果你有一个80G硬盘,如果想在上面找到一个内容包含某字符串的文件,不花他几个小时,怕是做不到。Linux下的grep命令也是这一种方式。
这种方法比较原始,但对于小数据量的文件,这种方法还是最直接,最方便的。但是对于大量的文件,这种方法就很慢了。
全文检索
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜一个索,从而达到搜索相对较快的目的。
Apache Lucene是什么?
Apache Lucene其实就是apache提供的一个开源关于搜索引擎的类库,囊括了较为全面的有关搜索引擎相关的技术解决思路,使得现如今的soler以及ElasticSearch尤为的火爆,不管是在互联网项目中以及大数据领域占据了不可多得的地位。
索引
正排索引
为什么顺序扫描的速度慢:其实是由于我们想要搜索的信息和非结构化数据中所存储的信息不一致造成的。
倒排索引
非结构化数据中所存储的信息是每个文件包含哪些字符串,也即已知文件,欲求字符串相对容易,也即是从文件到字符串的映射。而我们想搜索的信息是哪些文件包含此字符串,也即已知字符串,欲求文件,也即从字符串到文件的映射。两者恰恰相反。于是如果索引总能够保存从字符串到文件的映射,则会大大提高搜索速度。
倒排索引一般表示为一个关键词,然后是它的频度(出现的次数),位置(出现在哪一篇文章或网页中,及有关的日期,作者等信息),它相当于为互联网上几千亿页网页做了一个索引,好比一本书的目录、标签一般。想看哪一个主题相关的章节,直接根据目录即可找到相关的页面。不必再从书的第一页到最后一页,一页一页的查找。
例如:
文章 | head | content |
---|---|---|
文章1 | 超级塞亚人 | 我是超级塞亚人我喜欢吃苹果,我不是天朝的人,也不是地球人 |
文章2 | 天朝大国 | 我大天朝威武,我大天朝13亿人,我大天朝 |
文章3 | 游泳 | 游泳有很多好方法 |
文章4 | 动画片 | 喜欢看动画片,尤其是七龙珠,因为里面有塞亚人,而且塞亚人喜欢吃苹果,他们不是地球人 |
文章5 | 运动 | 喜欢运动,喜欢跑步,喜欢游泳,喜欢健身,喜欢 |
文章6 | 打炮 | 我喜欢打游戏,我最幸福的时光就是在天朝吃着苹果玩打炮游戏 |
关键字:塞亚人 苹果 天朝 地球 七龙珠 游泳 喜欢
关键词 | 索引 |
---|---|
塞亚人 | 文章1[1],文章4[2], n n n |
苹果 | 文章1[1],文章4[1],文章6[1], n n n |
天朝 | 文章1[1],文章2[2],文章6[1], n n n n |
地球 | 文章1[1],文章4[1], n n |
游泳 | 文章3[1],文章5[1], n n |
七龙珠 | 文章4[1], n |
喜欢 | 文章1[1],文章4[2],文章5[4],文章6[1], n n n n n n n n |
倒排索引2个重要构成:
1.倒排文件(inverted file):存储倒排索引的物理文件
2.倒排索引组成:单词词典和倒排文件。
何为倒排索引
核心术语
Index
Lucene的索引(Index)是由多个文件组成,这些文件被存放在同一目录下。
Token
词元(Token)在Lucene与在自然语言处理(NLP)中的概念相同,表示“词元”。词元即自然语言中的基本单位:在中文表现为一个独立的字或词,在英文中表现为一个单词。
Term
经过分词和语言处理后得到的字符串,它是索引的最小单位。
Field
不同的域可以指定不同的索引方式,比如指定不同的分词方式,是否构建索引,是否存储等。
Document
文档是索引的基本单位,代表一些域(Field)的集合。在Lucene中,Document是一种逻辑文件。可以近似地认为它表示的是计算机中的一个文件。这个Document是一种抽象的表示,它从各种维度来描述一个数据源中的每一条数据。
可以使用关系型数据库中的记录来理解Document,数据库的每一条记录可以表示为一个Document,而每一列可以用Field来表示。但不同的是,Document并非结构化的,并没有schema的约束。不同的文档保存在不同的段中的,一个段中可以包含多篇文档。新添加的文档被单独保存在一个新生成的段中,随着段的合并,不同的文档会合并到至相同的段中。
Segment
一个Lucene的索引(Index)由多个段组成,段与段之间是独立的。当添加索引时会生成新的Document,但并不是把新的Document 立即添加到同一个索引文件,而是把它们先被写入到不同的小文件(Segment),当小文件的个数达到阈值(段的个数,段中包含的文件数等)时,然后再合并成一个大索引文件(不同的段可以合并)。
Directory
Lucene索引的存放位置。
文档模型
文档名称 | 扩展名 | 说明 |
---|---|---|
Segments File | segments_N | 保存提交点的信息 |
Lock File | write.lock | 写文件锁,用于防止多个IndexWriters同时写一个文件 |
Segment Info | .si | 保存段的元数据信息 |
Compound File | .cfs, .cfe | 采用混合格式下该文件包括其他所有格式的文件 |
Fields | .fnm | 保存域信息 |
Field Index | .fdx | 保存指向域数据的指针 |
Field Data | .fdt | 保存域数据 |
Term Dictionary | .tim | 保存词项信息 |
Term Index | .tip | Term Dictionary的索引信息 |
Frequencies | .doc | 记录文档信息,以及文档中词项对应的词频 |
Positions | .pos | 记录词项的位置信息 |
Payloads | .pay | 全文索引的字段,使用了一些像payloads的高级特性会有该文件,保存了term在doc中的一些高级特性 |
Norms | .nvd, .nvm | 文件保存索引字段加权数据 |
Per-Document Values | .dvd, .dvm | lucene的docvalues文件,即数据的列式存储,用作聚合和排序 |
Term Vector Index | .tvx | 记录文档数据记录中的偏移量 |
Term Vector Documents | .tvd | 记录有词项向量的文档的信息 |
Term Vector Fields | .tvf | 词项向量的域级别信息 |
Live Documents | .liv | 存活文件的信息 |
Point values | .dii, .dim | 记录被索引的点 |
Segment_N
Segments_N为段的元数据信息文件。保存了此索引包含多少个段,每个段包含多少篇文档等信息。
Lucene当前活跃的Segment都会存在一个Segment Info文件里,也就是segments_N。如果有多个segments_N, 那么序号最大的就是最新的。
1.Format:索引文件格式的版本号。由于Lucene是在不断开发过程中的,不同版本的Lucene可能有不同索引文件格式,所以规定了文件格式的版本号。
2.Version:索引的版本号
3.NameCount:下一个新段的段名
4.SegCount:段的个数
[Lucece图形化工具Luke:]{https://www.github.com/DmitryKey/luke}
域文件格式
域元数据文件(fnm)
一个段(Segment)包含多个域,每个域都有一些元数据信息,保存在.fnm文件中,.fnm文件的格式如下:
域的数据信息存储在 .fdt 和 .fdx 文件中。其中,.fdx 文件中存放FieldValuesPositon,指向.fdt文件,也就是说域的具体数据是存放在fdt文件中的。
域数据文件(fdt)
真正保存域(stored field)信息的是fdt文件。
假如在一个Segment中包含N篇文档,那么fdt文档中一会有N个项,每一个项都保存对应文档的域信息。
FieldCount,表示此文档包含的域数目
紧接着是FieldCount域信息项,每个项保存一个域的信息。对于每一个域的信息:
FieldNum是域编号
接着是一个8bit的byte。根据填充值(0或1),代表不同的意义。最低一位表示此域是否分词;倒数第二位表示此域保存的是字符串数据还是二进制数据;倒数第三位表示此域是否被压缩。最后存储的是这个存储域的值。
域索引文件(fdx)
由域数据文件格式可知,每篇文档包含的域的个数、每个存储域的值都是不一样的。
因为segment中会包含N篇文档(Document),每篇文档占用的大小也是不一样的,那么如何快速在fdt文件中辨别每一篇文档的起始地址和终止地址?如何能够更快的找到第N篇文档的存储域的信息呢?
这就需要借助域索引文件。域索引文件也总共有N个项(如果segment中包含N篇Document),每篇文档都对应一个项,每一项中都存储一个Long型数值(大小固定),该数值代表文档在fdt中的起始地址偏移量。
词向量文件
词向量信息是从索引(Index)到文档(Document)到域(Field)到词(Term)的正向信息,有了词向量信息,就可以得到一篇文档包含哪些词的信息 。
词向量数据文件(tvd)
首先,是此文档包含域的个数:NumFields
之后,是一个NumFields大小的数组,数组每一项都是域号
最后,是(NumField - 1)大小的数组。每一篇文档的第一个域的偏移量信息存储在 tvx 文件中。而其他(NumFields - 1)域的偏移量就是第一个域的偏移量加上第(NumField - 1)个数组的值。
词向量索引文件(tvx)
一个段(segment)包含N篇文档,此文件就有N项,每一项代表一篇文档。每一项包含两部分信息:
第一部分是词向量文档文件(tvd)中此文档的偏移量
第二部分是词向量文件(tvf)中此文档的第一个域的偏移量
词向量域文件(tvf)
该文件包含了此段(Segment)中的所有域,并且不对文档做区分,到底第几个域到第几个域是属于那篇文件,是由tvx文件中的第一个域的偏移量以及tvd文件中的(NumField - 1)个域的偏移量来决定哪些域数据属于哪个文档。
对于每一个域,包含如下项:
首先,是说明此域中包含的多少(NumTerms)个词(Term)
之后,是8bit的byte(最后一位指定是否保存位置信息,倒数第二位表示是否保存偏移量信息)
最后,NumTerms个项的数组,每一项代表一个词(Term)。每一个词,由如下几部分构成
词的文本TermText
词频TermFreq(词在该文档中出现的次数)
词的位置
词的偏移量
创建倒排索引流程
全文检索的索引创建过程一般有以下几步:
第一步(原始素材)
为了方便说明索引创建过程,这里特意用两个文件为例:
文件一:Students should be allowed to go out with their friends, but not allowed to drink beer.
文件二:My friend Jerry went to school to see his students but found them drunk which is not allowed.
第二步(分词)
将原文档传给分词器(Tokenizer),分词器会做以下几件事情( 此过程称为Tokenize) :
将文档分成一个一个单独的单词。
去除标点符号。
去除停词(Stop word) 。
所谓停词(Stop word)就是一种语言中最普通的一些单词,由于没有特别的意义,因而大多数情况下不能成为搜索的关键词,因而创建索引时,这种词会被去掉而减少索引的大小。
每一种语言的分词组件(Tokenizer),都有一个停词(stop word)集合。
经过分词(Tokenizer) 后得到的结果称为词元(Token) ,如下:
第三步(语言处理)
将得到的词元(Token)传给语言处理组件(Linguistic Processor),语言处理组件(linguistic processor)主要是对得到的词元(Token)做一些同语言相关的处理。
对于英语,语言处理组件(Linguistic Processor) 一般做以下几点:
变为小写(Lowercase) 。
将单词缩减为词根形式,如“cars ”到“car ”等。这种操作称为:stemming 。
将单词转变为词根形式,如“drove ”到“drive ”等。这种操作称为:lemmatization 。
经过语言处理,得到的词(Term)如下:
第四步(构建索引)
至此,Lucene有关于搜索引擎以及基础知识点还有如何创建索引和大家分享完,欢迎大家和我一起分享学习!