为您推荐:
精华内容
最热下载
问答
  • 5星
    45.63MB weixin_47385625 2021-01-18 10:02:04
  • 5星
    5.52MB m0_43393325 2021-05-31 10:48:09
  • 5星
    20KB qq_27595745 2021-07-10 16:07:56
  • 5星
    78.57MB u013729183 2021-03-13 19:53:28
  • 5星
    3.65MB Dream_Gao1989 2020-12-10 19:49:57
  • 5星
    18KB m0_52957036 2020-04-15 07:24:27
  • 157KB Lidx0710 2021-09-27 04:27:56
  • 嘿嘿~原文链接:http://bridgeforyou.cn/2019/07/23/Inverted-Index/以下全部非本人所写为什么需要倒排索引倒排索引,也是索引索引,初衷都是为了快速检索到你要的数据。每种数据库都有自己要解决的问题(或者说...

    FST能讲一下就好了,希望自己能回来添上去。嘿嘿~

    原文链接:http://bridgeforyou.cn/2019/07/23/Inverted-Index/

    以下全部非本人所写

    为什么需要倒排索引

    倒排索引,也是索引。

    索引,初衷都是为了快速检索到你要的数据。

    每种数据库都有自己要解决的问题(或者说擅长的领域),对应的就有自己的数据结构,而不同的使用场景和数据结构,需要用不同的索引,才能起到最大化加快查询的目的。

    对 Mysql 来说,是 B+ 树,对 Elasticsearch/Lucene 来说,是倒排索引。

    Elasticsearch 是建立在全文搜索引擎库 Lucene 基础上的搜索引擎,它隐藏了 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API,不过掩盖不了它底层也是 Lucene 的事实。

    Elasticsearch 的倒排索引,其实就是 Lucene 的倒排索引。

    为什么叫倒排索引

    在没有搜索引擎时,我们是直接输入一个网址,然后获取网站内容,这时我们的行为是:

    document -> to -> words

    通过文章,获取里面的单词,此谓「正向索引」,forward index.

    后来,我们希望能够输入一个单词,找到含有这个单词,或者和这个单词有关系的文章:

    word -> to -> documents

    于是我们把这种索引,成为inverted index,直译过来,应该叫「反向索引」,国内翻译成「倒排索引」,有点委婉了。

    现在思考一下,如果让你来设计这个可以通过单词,反向找到文章的索引,你会怎么实现?

    关于 Elasticsearch 这类「搜索引擎」要解决的问题、它和传统关系型数据库的区别等等,可以看我之前写的文章:为什么需要 Elasticsearch(文末有链接)

    倒排索引的内部结构

    首先,在数据生成的时候,比如爬虫爬到一篇文章,这时我们需要对这篇文章进行分析,将文本拆解成一个个单词。

    这个过程很复杂,比如“生存还是死亡”,你要如何让分词器自动将它分解为“生存”、“还是”、“死亡”三个词语,然后把“还是”这个无意义的词语干掉。这里不展开,感兴趣的同学可以查看文末关于「分析器」的链接。

    接着,把这两个词语以及它对应的文档id存下来:

    word documentId

    生存 1

    死亡 1

    接着爬虫继续爬,又爬到一个含有“生存”的文档,于是索引变成:

    word documentId

    生存 1,2

    死亡 1

    下次搜索“生存”,就会返回文档ID是 1、2两份文档。

    然而上面这套索引的实现,给小孩子当玩具玩还行,要上生产环境,那还远着。

    想想看,这个世界上那么多单词,中文、英文、日文、韩文 … 你每次搜索一个单词,我都要全局遍历一遍,很明显不行。

    于是有了排序,我们需要对单词进行排序,像 B+ 树一样,可以在页里实现二分查找。

    光排序还不行,你单词都放在磁盘呢,磁盘 IO 慢的不得了,所以 Mysql 特意把索引缓存到了内存。

    你说好,我也学 Mysql 的,放内存,3,2,1,放,哐当,内存爆了。

    哪本字典,会把所有单词都贴在目录里的?

    所以,上图:

    309ad6370f0f395096c5e267c97ab70c.png

    Lucene 的倒排索,增加了最左边的一层「字典树」term index,它不存储所有的单词,只存储单词前缀,通过字典树找到单词所在的块,也就是单词的大概位置,再在块里二分查找,找到对应的单词,再找到单词对应的文档列表。

    当然,内存寸土寸金,能省则省,所以 Lucene 还用了 FST(Finite State Transducers)对它进一步压缩。

    FST 是什么?这里就不展开了,这次重点想聊的,是最右边的 Posting List 的,别看它只是存一个文档 ID 数组,但是它在设计时,遇到的问题可不少。

    Frame Of Reference

    原生的 Posting List 有两个痛点:

    如何压缩以节省磁盘空间

    如何快速求交并集(intersections and unions)

    先来聊聊压缩。

    我们来简化下 Lucene 要面对的问题,假设有这样一个数组:

    [73, 300, 302, 332, 343, 372]

    如何把它进行尽可能的压缩?

    Lucene 里,数据是按 Segment 存储的,每个 Segment 最多存 65536 个文档 ID, 所以文档 ID 的范围,从 0 到 2^32-1,所以如果不进行任何处理,那么每个元素都会占用 2 bytes ,对应上面的数组,就是 6 * 2 = 12 bytes.

    怎么压缩呢?

    压缩,就是尽可能降低每个数据占用的空间,同时又能让信息不失真,能够还原回来。

    Step 1:Delta-encode —— 增量编码

    我们只记录元素与元素之间的增量,于是数组变成了:

    [73, 227, 2, 30, 11, 29]

    Step 2:Split into blocks —— 分割成块

    Lucene里每个块是 256 个文档 ID,这样可以保证每个块,增量编码后,每个元素都不会超过 256(1 byte).

    为了方便演示,我们假设每个块是 3 个文档 ID:

    [73, 227, 2], [30, 11, 29]

    Step 3:Bit packing —— 按需分配空间

    对于第一个块,[73, 227, 2],最大元素是227,需要 8 bits,好,那我给你这个块的每个元素,都分配 8 bits的空间。

    但是对于第二个块,[30, 11, 29],最大的元素才30,只需要 5 bits,那我就给你每个元素,只分配 5 bits 的空间,足矣。

    这一步,可以说是把吝啬发挥到极致,精打细算,按需分配。

    以上三个步骤,共同组成了一项编码技术,Frame Of Reference(FOR):

    ddeeb955a934b75203a8ad812287f314.png

    Roaring bitmaps

    接着来聊聊 Posting List 的第二个痛点 —— 如何快速求交并集(intersections and unions)。

    在 Lucene 中查询,通常不只有一个查询条件,比如我们想搜索:

    含有“生存”相关词语的文档

    文档发布时间在最近一个月

    文档发布者是平台的特约作者

    这样就需要根据三个字段,去三棵倒排索引里去查,当然,磁盘里的数据,上一节提到过,用了 FOR 进行压缩,所以我们要把数据进行反向处理,即解压,才能还原成原始的文档 ID,然后把这三个文档 ID 数组在内存中做一个交集。

    即使没有多条件查询, Lucene 也需要频繁求并集,因为 Lucene 是分片存储的。

    同样,我们把 Lucene 遇到的问题,简化成一道算法题。

    假设有下面三个数组:

    [64, 300, 303, 343]

    [73, 300, 302, 303, 343, 372]

    [303, 311, 333, 343]

    求它们的交集。

    Option 1: Integer 数组

    直接用原始的文档 ID ,可能你会说,那就逐个数组遍历一遍吧,遍历完就知道交集是什么了。

    其实对于有序的数组,用跳表(skip table)可以更高效,这里就不展开了,因为不管是从性能,还是空间上考虑,Integer 数组都不靠谱,假设有100M 个文档 ID,每个文档 ID 占 2 bytes,那已经是 200 MB,而这些数据是要放到内存中进行处理的,把这么大量的数据,从磁盘解压后丢到内存,内存肯定撑不住。

    Option 2: Bitmap

    假设有这样一个数组:

    [3,6,7,10]

    那么我们可以这样来表示:

    [0,0,1,0,0,1,1,0,0,1]

    看出来了么,对,我们用 0 表示角标对应的数字不存在,用 1 表示存在。

    这样带来了两个好处:

    节省空间:既然我们只需要0和1,那每个文档 ID 就只需要 1 bit,还是假设有 100M 个文档,那只需要 100M bits = 100M * 1/8 bytes = 12.5 MB,比之前用 Integer 数组 的 200 MB,优秀太多

    运算更快:0 和 1,天然就适合进行位运算,求交集,「与」一下,求并集,「或」一下,一切都回归到计算机的起点

    Option 3: Roaring Bitmaps

    细心的你可能发现了,bitmap 有个硬伤,就是不管你有多少个文档,你占用的空间都是一样的,之前说过,Lucene Posting List 的每个 Segement 最多放 65536 个文档ID,举一个极端的例子,有一个数组,里面只有两个文档 ID:

    [0, 65535]

    用 Bitmap,要怎么表示?

    [1,0,0,0,….(超级多个0),…,0,0,1]

    你需要 65536 个 bit,也就是 65536/8 = 8192 bytes,而用 Integer 数组,你只需要 2 * 2 bytes = 4 bytes

    呵呵,死板的 bitmap。可见在文档数量不多的时候,使用 Integer 数组更加节省内存。

    我们来算一下临界值,很简单,无论文档数量多少,bitmap都需要 8192 bytes,而 Integer 数组则和文档数量成线性相关,每个文档 ID 占 2 bytes,所以:

    8192 / 2 = 4096

    当文档数量少于 4096 时,用 Integer 数组,否则,用 bitmap.

    69a9879ae2310fddf00374c6523914c5.png

    这里补充一下 Roaring bitmaps 和 之前讲的 Frame Of Reference 的关系。

    Frame Of Reference 是压缩数据,减少磁盘占用空间,所以当我们从磁盘取数据时,也需要一个反向的过程,即解压,解压后才有我们上面看到的这样子的文档ID数组:[73, 300, 302, 303, 343, 372] ,接着我们需要对数据进行处理,求交集或者并集,这时候数据是需要放到内存进行处理的,我们有三个这样的数组,这些数组可能很大,而内存空间比磁盘还宝贵,于是需要更强有力的压缩算法,同时还要有利于快速的求交并集,于是有了Roaring Bitmaps 算法。

    另外,Lucene 还会把从磁盘取出来的数据,通过 Roaring bitmaps 处理后,缓存到内存中,Lucene 称之为 filter cache.

    升华与总结

    文章的最后,如果来一段话总结(zhuang)升华(bi)一下,这篇文章就会得高分。

    有什么总结,可以拔高这篇文章的高度呢?

    首先,你会发现,很多业务上、技术上要解决的问题,最后都可以抽象为一道算法题,复杂问题简单化。

    呃,这个“华”,升的还不够。

    另一个具有高度的“华”,其实在开头已经讲出来了:

    每种数据库都有自己要解决的问题(或者说擅长的领域),对应的就有自己的数据结构,而不同的使用场景和数据结构,需要用不同的索引,才能起到最大化加快查询的目的。

    这篇文章讲的虽是 Lucene 如何实现倒排索引,如何精打细算每一块内存、磁盘空间、如何用诡谲的位运算加快处理速度,但往高处思考,再类比一下 Mysql,你就会发现,虽然都是索引,但是实现起来,截然不同。

    这个往细讲,又是一篇文章:如此不同,如此成功 —— B+ 树索引 vs 倒排索引

    标题都想好了,就看各位爷了,点赞超 50 就写 …

    可能没机会写了,那就 …

    留个作业吧

    知识要融合起来看才有意思。

    来,放大招了,两个问题:

    Lucene 为什么不用 b+ 树来搜索数据?

    Mysql 为什么不用 倒排索引来检索数据?

    附上两张图:

    2a5f391267e3db34d598b461bc65de01.png

    309ad6370f0f395096c5e267c97ab70c.png

    展开全文
    weixin_34117129 2021-01-19 03:00:31
  • 见其名知其意,有倒排索引,对应肯定,有正向索引。 正向索引(forward index),反向索引(inverted index)更熟悉的名字是倒排索引。 在搜索引擎中每个文件都对应一个文件ID,文件内容被表示为一系列关键词的...

    见其名知其意,有倒排索引,对应肯定,有正向索引。

         正向索引(forward index),反向索引(inverted index)更熟悉的名字是倒排索引。

     

         在搜索引擎中每个文件都对应一个文件ID,文件内容被表示为一系列关键词的集合(实际上在搜索引擎索引库中,关键词也已经转换为关键词ID)。例如“文档1”经过分词,提取了20个关键词,每个关键词都会记录它在文档中的出现次数和出现位置。

     

     

     

         得到正向索引的结构如下:

           “文档1”的ID > 单词1:出现次数,出现位置列表;单词2:出现次数,出现位置列表;…………。

           “文档2”的ID > 此文档出现的关键词列表。

     

      一般是通过key,去找value。

     

     

     

     

          当用户在主页上搜索关键词“华为手机”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,找出所有包含关键词“华为手机”的文档,再根据打分模型进行打分,排出名次后呈现给用户。因为互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。

           所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。

           得到倒排索引的结构如下:

           “关键词1”:“文档1”的ID,“文档2”的ID,…………。

           “关键词2”:带有此关键词的文档ID列表。

     

      从词的关键字,去找文档。

     

     

     

     

     

     

    1.单词——文档矩阵

          单词-文档矩阵是表达两者之间所具有的一种包含关系的概念模型,图1展示了其含义。图3-1的每列代表一个文档每行代表一个单词,打对勾的位置代表包含关系。

                

                              图1 单词-文档矩阵

     

         从纵向即文档这个维度来看,每列代表文档包含了哪些单词,比如文档1包含了词汇1和词汇4,而不包含其它单词。从横向即单词这个维度来看,每行代表了哪些文档包含了某个单词。比如对于词汇1来说,文档1和文档4中出现过单词1,而其它文档不包含词汇1。矩阵中其它的行列也可作此种解读。

        搜索引擎的索引其实就是实现“单词-文档矩阵”的具体数据结构。可以有不同的方式来实现上述概念模型,比如“倒排索引”、“签名文件”、“后缀树”等方式。但是各项实验数据表明,“倒排索引”是实现单词到文档映射关系的最佳实现方式,所以本博文主要介绍“倒排索引”的技术细节。

     

     

     

    2.倒排索引基本概念

           文档(Document):一般搜索引擎的处理对象是互联网网页,而文档这个概念要更宽泛些,代表以文本形式存在的存储对象,相比网页来说,涵盖更多种形式,比如Word,PDF,html,XML等不同格式的文件都可以称之为文档。再比如一封邮件,一条短信,一条微博也可以称之为文档。在本书后续内容,很多情况下会使用文档来表征文本信息。

           文档集合(Document Collection):由若干文档构成的集合称之为文档集合。比如海量的互联网网页或者说大量的电子邮件都是文档集合的具体例子。

           文档编号(Document ID):在搜索引擎内部,会将文档集合内每个文档赋予一个唯一的内部编号,以此编号来作为这个文档的唯一标识,这样方便内部处理,每个文档的内部编号即称之为“文档编号”,后文有时会用DocID来便捷地代表文档编号。

           单词编号(Word ID):与文档编号类似,搜索引擎内部以唯一的编号来表征某个单词,单词编号可以作为某个单词的唯一表征。

           倒排索引(Inverted Index):倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。

           单词词典(Lexicon):搜索引擎的通常索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。

           倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。

           倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即被称之为倒排文件,倒排文件是存储倒排索引的物理文件。

         关于这些概念之间的关系,通过图2可以比较清晰的看出来。

                  

     

     

     

    3.倒排索引简单实例

          倒排索引从逻辑结构和基本思路上来讲非常简单。下面我们通过具体实例来进行说明,使得读者能够对倒排索引有一个宏观而直接的感受。

          假设文档集合包含五个文档,每个文档内容如图3所示,在图中最左端一栏是每个文档对应的文档编号。我们的任务就是对这个文档集合建立倒排索引。

                  

                               图3   文档集合

     

      中文和英文等语言不同,单词之间没有明确分隔符号,所以首先要用分词系统将文档自动切分成单词序列。这样每个文档就转换为由单词序列构成的数据流,为了系统后续处理方便,需要对每个不同的单词赋予唯一的单词编号,同时记录下哪些文档包含这个单词,在如此处理结束后,我们可以得到最简单的倒排索引(参考图3-4)。在图4中,“单词ID”一栏记录了每个单词的单词编号,第二栏是对应的单词,第三栏即每个单词对应的倒排列表。比如单词“谷歌”,其单词编号为1,倒排列表为{1,2,3,4,5},说明文档集合中每个文档都包含了这个单词。

                  

                                图4   简单的倒排索引

      之所以说图4所示倒排索引是最简单的,是因为这个索引系统只记载了哪些文档包含某个单词,而事实上,索引系统还可以记录除此之外的更多信息。图5是一个相对复杂些的倒排索引,与图4的基本索引系统比,在单词对应的倒排列表中不仅记录了文档编号,还记载了单词频率信息(TF),即这个单词在某个文档中的出现次数,之所以要记录这个信息,是因为词频信息在搜索结果排序时,计算查询和文档相似度是很重要的一个计算因子,所以将其记录在倒排列表中,以方便后续排序时进行分值计算。在图5的例子里,单词“创始人”的单词编号为7,对应的倒排列表内容为:(3:1),其中的3代表文档编号为3的文档包含这个单词,数字1代表词频信息,即这个单词在3号文档中只出现过1次,其它单词对应的倒排列表所代表含义与此相同。

                  

                                图 5 带有单词频率信息的倒排索引

       实用的倒排索引还可以记载更多的信息,图6所示索引系统除了记录文档编号和单词频率信息外,额外记载了两类信息,即每个单词对应的“文档频率信息”(对应图6的第三栏)以及在倒排列表中记录单词在某个文档出现的位置信息。

                      

                           图6   带有单词频率、文档频率和出现位置信息的倒排索引

       “文档频率信息”代表了在文档集合中有多少个文档包含某个单词,之所以要记录这个信息,其原因与单词频率信息一样,这个信息在搜索结果排序计算中是非常重要的一个因子。而单词在某个文档中出现的位置信息并非索引系统一定要记录的,在实际的索引系统里可以包含,也可以选择不包含这个信息,之所以如此,因为这个信息对于搜索系统来说并非必需的,位置信息只有在支持“短语查询”的时候才能够派上用场。

         以单词“拉斯”为例,其单词编号为8,文档频率为2,代表整个文档集合中有两个文档包含这个单词,对应的倒排列表为:{(3;1;<4>),(5;1;<4>)},其含义为在文档3和文档5出现过这个单词,单词频率都为1,单词“拉斯”在两个文档中的出现位置都是4,即文档中第四个单词是“拉斯”。

         图6所示倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此,区别无非是采取哪些具体的数据结构来实现上述逻辑结构。

         有了这个索引系统,搜索引擎可以很方便地响应用户的查询,比如用户输入查询词“Facebook”,搜索系统查找倒排索引,从中可以读出包含这个单词的文档,这些文档就是提供给用户的搜索结果,而利用单词频率信息、文档频率信息即可以对这些候选搜索结果进行排序,计算文档和查询的相似性,按照相似性得分由高到低排序输出,此即为搜索系统的部分内部流程,具体实现方案本书第五章会做详细描述。

     

     

     

     

    4. 单词词典

      单词词典是倒排索引中非常重要的组成部分,它用来维护文档集合中出现过的所有单词的相关信息,同时用来记载某个单词对应的倒排列表在倒排文件中的位置信息。在支持搜索时,根据用户的查询词,去单词词典里查询,就能够获得相应的倒排列表,并以此作为后续排序的基础。
           对于一个规模很大的文档集合来说,可能包含几十万甚至上百万的不同单词,能否快速定位某个单词,这直接影响搜索时的响应速度,所以需要高效的数据结构来对单词词典进行构建和查找,常用的数据结构包括哈希加链表结构和树形词典结构。
    4.1   哈希加链表
           图7是这种词典结构的示意图。这种词典结构主要由两个部分构成:

            主体部分是哈希表,每个哈希表项保存一个指针,指针指向冲突链表,在冲突链表里,相同哈希值的单词形成链表结构。之所以会有冲突链表,是因为两个不同单词获得相同的哈希值,如果是这样,在哈希方法里被称做是一次冲突,可以将相同哈希值的单词存储在链表里,以供后续查找。

                          

      在建立索引的过程中,词典结构也会相应地被构建出来。比如在解析一个新文档的时候,对于某个在文档中出现的单词T,首先利用哈希函数获得其哈希值,之后根据哈希值对应的哈希表项读取其中保存的指针,就找到了对应的冲突链表。如果冲突链表里已经存在这个单词,说明单词在之前解析的文档里已经出现过。如果在冲突链表里没有发现这个单词,说明该单词是首次碰到,则将其加入冲突链表里。通过这种方式,当文档集合内所有文档解析完毕时,相应的词典结构也就建立起来了。

           在响应用户查询请求时,其过程与建立词典类似,不同点在于即使词典里没出现过某个单词,也不会添加到词典内。以图7为例,假设用户输入的查询请求为单词3,对这个单词进行哈希,定位到哈希表内的2号槽,从其保留的指针可以获得冲突链表,依次将单词3和冲突链表内的单词比较,发现单词3在冲突链表内,于是找到这个单词,之后可以读出这个单词对应的倒排列表来进行后续的工作,如果没有找到这个单词,说明文档集合内没有任何文档包含单词,则搜索结果为空。

    4.2   树形结构
           B树(或者B+树)是另外一种高效查找结构,图8是一个 B树结构示意图。B树与哈希方式查找不同,需要字典项能够按照大小排序(数字或者字符序),而哈希方式则无须数据满足此项要求。
           B树形成了层级查找结构,中间节点用于指出一定顺序范围的词典项目存储在哪个子树中,起到根据词典项比较大小进行导航的作用,最底层的叶子节点存储单词的地址信息,根据这个地址就可以提取出单词字符串。

                    

                               图8   B树查找结构 

     

     

     

     

     

    总结

    单词ID:记录每个单词的单词编号;
    单词:对应的单词;
    文档频率:代表文档集合中有多少个文档包含某个单词
    倒排列表:包含单词ID及其他必要信息
    DocId:单词出现的文档id
    TF:单词在某个文档中出现的次数
    POS:单词在文档中出现的位置
         以单词“加盟”为例,其单词编号为6,文档频率为3,代表整个文档集合中有三个文档包含这个单词,对应的倒排列表为{(2;1;<4>),(3;1;<7>),(5;1;<5>)},含义是在文档2,3,5出现过这个单词,在每个文档的出现过1次,单词“加盟”在第一个文档的POS是4,即文档的第四个单词是“加盟”,其他的类似。
    这个倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此。

    引用:http://www.cnblogs.com/zlslch/p/6440114.html

    展开全文
    zsd_31 2018-04-17 19:57:55
  • 1.正向索引 正向索引(正排索引):正排表是以文档的ID为关键字,表中记录文档中每个字的位置信息,查找时扫描表中每个文档中字的信息直到找出所有包含查询关键字的文档。 “文档1”的ID > 单词1:出现次数,出现...

    1.正向索引

    正向索引(正排索引):正排表是以文档的ID为关键字,表中记录文档中每个字的位置信息,查找时扫描表中每个文档中字的信息直到找出所有包含查询关键字的文档。

    “文档1”的ID > 单词1:出现次数,出现位置列表;单词2:出现次数,出现位置列表;…………。
    “文档2”的ID > 此文档出现的关键词列表。
    在这里插入图片描述

    正排表结构如图1所示,这种组织方法在建立索引的时候结构比较简单,建立比较方便且易于维护;因为索引是基于文档建立的,若是有新的文档加入,直接为该文档建立一个新的索引块,挂接在原来索引文件的后面。若是有文档删除,则直接找到该文档号文档对应的索引信息,将其直接删除。但是在查询的时候需对所有的文档进行扫描以确保没有遗漏,这样就使得检索时间大大延长,检索效率低下。

    尽管正排表的工作原理非常的简单,但是由于其检索效率太低,除非在特定情况下,否则实用性价值不大。

    反向索引

    反向索引(倒排索引):倒排表以字或词为关键字进行索引,表中关键字所对应的记录表项记录了出现这个字或词的所有文档,一个表项就是一个字表段,它记录该文档的ID和字符在该文档中出现的位置情况。

    由于每个字或词对应的文档数量在动态变化,所以倒排表的建立和维护都较为复杂,但是在查询的时候由于可以一次得到查询关键字所对应的所有文档,所以效率高于正排表。在全文检索中,检索的快速响应是一个最为关键的性能,而索引建立由于在后台进行,尽管效率相对低一些,但不会影响整个搜索引擎的效率。

    倒排索引的结构如下:
    “关键词1”:“文档1”的ID,“文档2”的ID,…………。
    “关键词2”:带有此关键词的文档ID列表。
    在这里插入图片描述

    3.倒排索引介绍

    3.1.单词——文档矩阵

    单词-文档矩阵是表达两者之间所具有的一种包含关系的概念模型,图1展示了其含义。下图的每列代表一个文档,每行代表一个单词,打对勾的位置代表包含关系。
    在这里插入图片描述

    从纵向即文档这个维度来看,每列代表文档包含了哪些单词,比如文档1包含了词汇1和词汇4,而不包含其它单词。从横向即单词这个维度来看,每行代表了哪些文档包含了某个单词。比如对于词汇1来说,文档1和文档4中出现过单词1,而其它文档不包含词汇1。矩阵中其它的行列也可作此种解读。

    搜索引擎的索引其实就是实现“单词-文档矩阵”的具体数据结构。可以有不同的方式来实现上述概念模型,比如“倒排索引”、“签名文件”、“后缀树”等方式。但是各项实验数据表明,“倒排索引”是实现单词到文档映射关系的最佳实现方式,所以本文主要介绍“倒排索引”的技术细节。

    3.2.倒排索引基本概念

    • 文档(Document):一般搜索引擎的处理对象是互联网网页,而文档这个概念要更宽泛些,代表以文本形式存在的存储对象,相比网页来说,涵盖更多种形式,比如Word,PDF,html,XML等不同格式的文件都可以称之为文档。再比如一封邮件,一条短信,一条微博也可以称之为文档。在本书后续内容,很多情况下会使用文档来表征文本信息。

    • 文档集合(Document Collection):由若干文档构成的集合称之为文档集合。比如海量的互联网网页或者说大量的电子邮件都是文档集合的具体例子。

    • 文档编号(Document ID):在搜索引擎内部,会将文档集合内每个文档赋予一个唯一的内部编号,以此编号来作为这个文档的唯一标识,这样方便内部处理,每个文档的内部编号即称之为“文档编号”,后文有时会用DocID来便捷地代表文档编号。

    • 单词编号(Word ID):与文档编号类似,搜索引擎内部以唯一的编号来表征某个单词,单词编号可以作为某个单词的唯一表征。

    • 倒排索引(Inverted Index):倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。

    • 单词词典(Lexicon):搜索引擎的通常索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。

    • 倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。

    • 倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即被称之为倒排文件,倒排文件是存储倒排索引的物理文件。

      关于这些概念之间的关系,通过下图可以比较清晰的看出来。
      在这里插入图片描述

    3.3.倒排索引简单实例

    倒排索引从逻辑结构和基本思路上来讲非常简单。下面我们通过具体实例来进行说明,使得读者能够对倒排索引有一个宏观而直接的感受。

    假设文档集合包含五个文档,每个文档内容下图所示,在图中最左端一栏是每个文档对应的文档编号。我们的任务就是对这个文档集合建立倒排索引。 
    在这里插入图片描述
                 文档集合

    中文和英文等语言不同,单词之间没有明确分隔符号,所以首先要用分词系统将文档自动切分成单词序列。这样每个文档就转换为由单词序列构成的数据流,为了系统后续处理方便,需要对每个不同的单词赋予唯一的单词编号,同时记录下哪些文档包含这个单词,在如此处理结束后,我们可以得到最简单的倒排索引(参考图3-4)。在图4中,“单词ID”一栏记录了每个单词的单词编号,第二栏是对应的单词,第三栏即每个单词对应的倒排列表。比如单词“谷歌”,其单词编号为1,倒排列表为{1,2,3,4,5},说明文档集合中每个文档都包含了这个单词。
                  (参考图3-4)图4

    之所以说图4所示倒排索引是最简单的,是因为这个索引系统只记载了哪些文档包含某个单词,而事实上,索引系统还可以记录除此之外的更多信息。图5是一个相对复杂些的倒排索引,与图4的基本索引系统比,在单词对应的倒排列表中不仅记录了文档编号,还记载了单词频率信息(TF),即这个单词在某个文档中的出现次数,之所以要记录这个信息,是因为词频信息在搜索结果排序时,计算查询和文档相似度是很重要的一个计算因子,所以将其记录在倒排列表中,以方便后续排序时进行分值计算。在图5的例子里,单词“创始人”的单词编号为7,对应的倒排列表内容为:(3:1),其中的3代表文档编号为3的文档包含这个单词,数字1代表词频信息,即这个单词在3号文档中只出现过1次,其它单词对应的倒排列表所代表含义与此相同。     
    在这里插入图片描述
                  图 5 带有单词频率信息的倒排索引

    实用的倒排索引还可以记载更多的信息,图6所示索引系统除了记录文档编号和单词频率信息外,额外记载了两类信息,即每个单词对应的“文档频率信息”(对应图6的第三栏)以及在倒排列表中记录单词在某个文档出现的位置信息。

    在这里插入图片描述
                 图6 带有单词频率、文档频率和出现位置信息的倒排索引

    “文档频率信息”代表了在文档集合中有多少个文档包含某个单词,之所以要记录这个信息,其原因与单词频率信息一样,这个信息在搜索结果排序计算中是非常重要的一个因子。而单词在某个文档中出现的位置信息并非索引系统一定要记录的,在实际的索引系统里可以包含,也可以选择不包含这个信息,之所以如此,因为这个信息对于搜索系统来说并非必需的,位置信息只有在支持“短语查询”的时候才能够派上用场。

    以单词“拉斯”为例,其单词编号为8,文档频率为2,代表整个文档集合中有两个文档包含这个单词,对应的倒排列表为:{(3;1;<4>),(5;1;<4>)},其含义为在文档3和文档5出现过这个单词,单词频率都为1,单词“拉斯”在两个文档中的出现位置都是4,即文档中第四个单词是“拉斯”。

    图6所示倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此,区别无非是采取哪些具体的数据结构来实现上述逻辑结构。

    有了这个索引系统,搜索引擎可以很方便地响应用户的查询,比如用户输入查询词“Facebook”,搜索系统查找倒排索引,从中可以读出包含这个单词的文档,这些文档就是提供给用户的搜索结果,而利用单词频率信息、文档频率信息即可以对这些候选搜索结果进行排序,计算文档和查询的相似性,按照相似性得分由高到低排序输出,此即为搜索系统的部分内部流程,具体实现方案本书第五章会做详细描述。

    3.4. 单词词典

    单词词典是倒排索引中非常重要的组成部分,它用来维护文档集合中出现过的所有单词的相关信息,同时用来记载某个单词对应的倒排列表在倒排文件中的位置信息。在支持搜索时,根据用户的查询词,去单词词典里查询,就能够获得相应的倒排列表,并以此作为后续排序的基础。
    对于一个规模很大的文档集合来说,可能包含几十万甚至上百万的不同单词,能否快速定位某个单词,这直接影响搜索时的响应速度,所以需要高效的数据结构来对单词词典进行构建和查找,常用的数据结构包括哈希加链表结构和树形词典结构。

    3.4.1 哈希加链表

       图7是这种词典结构的示意图。这种词典结构主要由两个部分构成:
    

    主体部分是哈希表,每个哈希表项保存一个指针,指针指向冲突链表,在冲突链表里,相同哈希值的单词形成链表结构。之所以会有冲突链表,是因为两个不同单词获得相同的哈希值,如果是这样,在哈希方法里被称做是一次冲突,可以将相同哈希值的单词存储在链表里,以供后续查找。
    在这里插入图片描述

    在建立索引的过程中,词典结构也会相应地被构建出来。比如在解析一个新文档的时候,对于某个在文档中出现的单词T,首先利用哈希函数获得其哈希值,之后根据哈希值对应的哈希表项读取其中保存的指针,就找到了对应的冲突链表。如果冲突链表里已经存在这个单词,说明单词在之前解析的文档里已经出现过。如果在冲突链表里没有发现这个单词,说明该单词是首次碰到,则将其加入冲突链表里。通过这种方式,当文档集合内所有文档解析完毕时,相应的词典结构也就建立起来了。

    在响应用户查询请求时,其过程与建立词典类似,不同点在于即使词典里没出现过某个单词,也不会添加到词典内。以图7为例,假设用户输入的查询请求为单词3,对这个单词进行哈希,定位到哈希表内的2号槽,从其保留的指针可以获得冲突链表,依次将单词3和冲突链表内的单词比较,发现单词3在冲突链表内,于是找到这个单词,之后可以读出这个单词对应的倒排列表来进行后续的工作,如果没有找到这个单词,说明文档集合内没有任何文档包含单词,则搜索结果为空。

    3.4.2 树形结构

    B树(或者B+树)是另外一种高效查找结构,图8是一个 B树结构示意图。B树与哈希方式查找不同,需要字典项能够按照大小排序(数字或者字符序),而哈希方式则无须数据满足此项要求。

    B树形成了层级查找结构,中间节点用于指出一定顺序范围的词典项目存储在哪个子树中,起到根据词典项比较大小进行导航的作用,最底层的叶子节点存储单词的地址信息,根据这个地址就可以提取出单词字符串。

    在这里插入图片描述
    图8 B树查找结构

    3.4 总结

    单词ID:记录每个单词的单词编号;
    单词:对应的单词;
    文档频率:代表文档集合中有多少个文档包含某个单词
    倒排列表:包含单词ID及其他必要信息
    DocId:单词出现的文档id
    TF:单词在某个文档中出现的次数
    POS:单词在文档中出现的位置

    以单词“加盟”为例,其单词编号为6,文档频率为3,代表整个文档集合中有三个文档包含这个单词,对应的倒排列表为{(2;1;<4>),(3;1;<7>),(5;1;<5>)},含义是在文档2,3,5出现过这个单词,在每个文档的出现过1次,单词“加盟”在第一个文档的POS是4,即文档的第四个单词是“加盟”,其他的类似。

    这个倒排索引已经是一个非常完备的索引系统,实际搜索系统的索引结构基本如此。

    参考文档:https://blog.csdn.net/starzhou/article/details/87519973#comments,并对有些图的位置做了相应调整。

    展开全文
    qq_38923792 2020-01-18 10:17:08
  • 数字图书馆有一套基于 MySQL 的电子书管理系统,电子书的基本信息保存在数据库表中,书的数字内容以多种常见的文档格式(PDF、Word、PPT、RTF、TXT、CHM、EPUB等)保存在存储系统中。现在需要利用 ElasticSearch ...

    【转】ElasticSearch 全文检索实战
    【转】ElasticSearch 5.3 载入PDF数据
    1.简介
    ElasticSearch只能处理文本,不能直接处理文档。要实现 ElasticSearch 的附件导入需要以下两个步骤:

      一、对多种主流格式的文档进行文本抽取。
    
      二、将抽取出来的文本内容导入 ElasticSearch。
    

    Ingest-Attachment是一个开箱即用的插件,替代了较早版本的Mapper-Attachment插件,使用它可以实现对(PDF,DOC等)主流格式文件的文本抽取及自动导入。

    Elasticsearch5.x 新增一个新的特性 Ingest Node,此功能支持定义命名处理器管道 pipeline,pipeline中可以定义多个处理器,在数据插入 ElasticSearch 之前进行预处理。而 Ingest Attachment Processor Plugin 提供了关键的预处理器 attachment,支持自动对入库文档的指定字段作为文档文件进行文本抽取,并将抽取后得到的文本内容和相关元数据加入原始入库文档。

    由于 ElasticSearch 是基于 JSON 格式的文档数据库,所以附件文档在插入 ElasticSearch 之前必须进行 Base64 编码。

    以下使用 REST API 调用方式。

    2.环境
    ElasticSearch 5.3.0

    ElasticSearch-head-master插件  (安装指路 Elasticsearch学习--elasticsearch-head插件安装)
    
    Ingest-attachment插件  (官方介绍 Ingest Attachment Processor Plugin)
    
    Cygwin  (curl+perl)
    

    3.实现步骤
    3.1 建立自己的文本抽取管道pipeline

    curl -X PUT "localhost:9200/_ingest/pipeline/attachment" -d '{
     "description" : "Extract attachment information",
     "processors":[
     {
        "attachment":{
            "field":"data",
            "indexed_chars" : -1,
            "ignore_missing":true
         }
     },
     {
         "remove":{"field":"data"}
     }]}'

    3.2 创建新的索引
    此处索引名为estest。

    curl -X PUT “localhost:9200/estest” -d’{
    “settings”:{
    “index”:{
    “number_of_shards”:1,
    “number_of_replicas”:0
    }}}’

    3.3 载入数据
    方法一:直接载入base64源码

    首先要确定base64编码正确,否则因为乱码可能无法正确生成attachment。

    可在 http://encode.urih.com/http://decode.urih.com/ 先进行编解码测试。

    这里: index-pdftest type-pdf id-1 皆为自定义

    curl -X PUT "localhost:9200/pdftest/pdf/1?pipeline=attachment" -d '
    {
       "data":"QmFzZTY057yW56CB6K+05piOCuOAgOOAgEJhc2U2NOe8lueggeimgeaxguaKijPkuKo45L2N5a2X6IqC77yIMyo4PTI077yJ6L2s5YyW5Li6NOS4qjbkvY3nmoTlrZfoioLvvIg0KjY9MjTvvInvvIzkuYvlkI7lnKg25L2N55qE5YmN6Z2i6KGl5Lik5LiqMO+8jOW9ouaIkDjkvY3kuIDkuKrlrZfoioLnmoTlvaLlvI/jgIIg5aaC5p6c5Ymp5LiL55qE5a2X56ym5LiN6LazM+S4quWtl+iKgu+8jOWImeeUqDDloavlhYXvvIzovpPlh7rlrZfnrKbkvb/nlKgnPSfvvIzlm6DmraTnvJbnoIHlkI7ovpPlh7rnmoTmlofmnKzmnKvlsL7lj6/og73kvJrlh7rnjrAx5oiWMuS4qic9J+OAggoK44CA44CA5Li65LqG5L+d6K+B5omA6L6T5Ye655qE57yW56CB5L2N5Y+v6K+75a2X56ym77yMQmFzZTY05Yi25a6a5LqG5LiA5Liq57yW56CB6KGo77yM5Lul5L6/6L+b6KGM57uf5LiA6L2s5o2i44CC57yW56CB6KGo55qE5aSn5bCP5Li6Ml42PTY077yM6L+Z5Lmf5pivQmFzZTY05ZCN56ew55qE55Sx5p2l44CC"
    }'

    载入结果显示:(这一版data数据尚未删除)

    方法二:载入PDF的同时进行转码导入

    首先跳转至指定文件目录

    这里我的文件ABC.pdf放在目录D:\ElasticSearch\File下

    cd D:/ElasticSearch/File
    使用perl脚本的解码功能:

    “’base64 -w 0 ABC.pdf | perl -pe's/\n/\\n/g'‘”
    完整代码:

    curl -X PUT "localhost:9200/estest/pdf/10?pipeline=attachment" -d '
    {
       "data":" '`base64 -w 0 ABC.pdf | perl -pe's/\n/\\n/g'`' "
    }'

    结果如图,可以导入成功:

    全文索引,查询指定字段

    注意查询字段名称,这个真的纠结了我太久……

    curl -X POST "localhost:9200/pdftest/pdf/_search?pretty" -d '{
      "query":{
         "match":{
            "attachment.content":"编码"
     }}}'

    查询结果:

       total=1 意为找到一个,由此验证字段可查询。
    
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
      },
      "hits" : {
        "total" : 1,
        "max_score" : 1.0446626,
        "hits" : [
          {
            "_index" : "pdftest",
            "_type" : "pdf",
            "_id" : "6",
            "_score" : 1.0446626,
            "_source" : {
              "data" : "QmFzZTY057yW56CB6K+05piOCuOAgOOAgEJhc2U2NOe8lueggeimgeaxguaKijPkuKo45L2N5a2X6IqC77yIMyo4PTI077yJ6L2s5YyW5Li6NOS4qjbkvY3nmoTlrZfoioLvvIg0KjY9MjTvvInvvIzkuYvlkI7lnKg25L2N55qE5YmN6Z2i6KGl5Lik5LiqMO+8jOW9ouaIkDjkvY3kuIDkuKrlrZfoioLnmoTlvaLlvI/jgIIg5aaC5p6c5Ymp5LiL55qE5a2X56ym5LiN6LazM+S4quWtl+iKgu+8jOWImeeUqDDloavlhYXvvIzovpPlh7rlrZfnrKbkvb/nlKgnPSfvvIzlm6DmraTnvJbnoIHlkI7ovpPlh7rnmoTmlofmnKzmnKvlsL7lj6/og73kvJrlh7rnjrAx5oiWMuS4qic9J+OAggoK44CA44CA5Li65LqG5L+d6K+B5omA6L6T5Ye655qE57yW56CB5L2N5Y+v6K+75a2X56ym77yMQmFzZTY05Yi25a6a5LqG5LiA5Liq57yW56CB6KGo77yM5Lul5L6/6L+b6KGM57uf5LiA6L2s5o2i44CC57yW56CB6KGo55qE5aSn5bCP5Li6Ml42PTY077yM6L+Z5Lmf5pivQmFzZTY05ZCN56ew55qE55Sx5p2l44CC",
              "attachment" : {
                "content_type" : "text/plain; charset=UTF-8",
                "language" : "lt",
                "content" : "Base64编码说明\n  Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='。\n\n  为了保证所输出的编码位可读字符,Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64,这也是Base64名称的由来。",
                "content_length" : 212 }
            }
          }
        ]
      }
    }

    5.参考目录
    convert the file into base64 in elasticsearch for attachment

    ElasticSearch 全文检索实战

    elasticsearch使用附件进行中文检索,无法查询中文的问题

    Getting error while parsing documents

    Sending Attachments: Unexpected end-of-input in VALUE_STRING

    How to index a pdf file in Elasticsearch 5.0.0 with ingest-attachment plugin?

    6.想法
    其实流程走下来还挺简单,但当时小白入门,我还是困扰了挺久的,查阅各路资料最终成功导入。虽然也不厉害,但还是记录一下,缕清整个流程,希望能帮到大家 。^_^

    一、概述
    Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎,它基于 Lucene 实现了强大的全文检索功能。本文针对一个通用的应用场景,讲解如何利用 ElasticSearch 快速实现对关系型数据库文本和常见文档格式附件的全文检索。

    二、应用场景
    描述
    数字图书馆有一套基于 MySQL 的电子书管理系统,电子书的基本信息保存在数据库表中,书的数字内容以多种常见的文档格式(PDF、Word、PPT、RTF、TXT、CHM、EPUB等)保存在存储系统中。现在需要利用 ElasticSearch 实现一套全文检索系统,以便用户可以通过对电子书的基本信息和数字内容进行模糊查询,快速找到相关书籍。

    数据结构
    数据库表 BOOK 结构:

    CREATE TABLE book (
    id varchar(100) NOT NULL,
    title varchar(50) DEFAULT NULL,
    desc varchar(1000) DEFAULT NULL,
    path varchar(200) DEFAULT NULL,
    create_time datetime DEFAULT NULL,
    update_time datetime DEFAULT NULL,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    字段 意义
    id 主键
    title 书名
    desc 介绍
    path 存储路径
    create_time 创建时间
    update_time 更新时间
    逻辑约束:创建书籍记录时,create_time 等于 update_time,即当前时间,每次更新书籍时,更新 update_time 时间。全文检索系统根据 update_time 时间更新书籍索引。

    三、技术方案
    示意图

    基本思路就是:

    1. 定期扫描 MySQL 中的 book 表,根据字段 update_time 批量抓取最新的电子书数据。
    2. 从 path 字段获取电子书数字内容的文档存储路径。从存储系统中抓取电子书文档并进行 BASE64编码。
    3. 将从 book 表批量抓取的数据转换为 JSON 文档,并将 BASE64编码后的电子书文档合并入 JSON,一同写入 ElasticSearch,利用 ElasticSearch 的插件 Ingest Attachment Processor Plugin 对电子书文档进行文本抽取,并进行持久化,建立全文索引。
      本文采用开源数据处理工具 Apache NiFi http://nifi.apache.org 来实现上述流程,具体使用方法后续实施过程会详细讲解。如果读者不了解 Apache NiFi ,也可以使用 Logstash、Kettle 等工具或者使用自己熟悉的编程语言开发应用来完成上述流程。

    四、安装并初始化 ElasticSearch
    安装 ElasticSearch
    访问ElasticSearch官网,根据操作系统选择下载软件包,并安装
    https://www.elastic.co/downloads/elasticsearch
    当前最新版本是 v6.2.4

    Linux/Unix 下运行 bin/elasticsearch (在windows操作系统下运行 bin\elasticsearch.bat )

    ElasticSearch的默认服务端口是 9200,所有 API 都可以通过 REST 方式调用。

    关于JVM内存:ElasticSearch是基于Java开发,部署需要配置合理的JVM Heap内存,官方建议分配内存不高于本机物理内存的二分之一,最好不要超过32G。具体配置方法如下:

    设置环境变量 ES_HEAP_SIZE,ElasticSearch启动时会读取这个环境变量。 在命令行运行如下:

    export ES_HEAP_SIZE=8g
    安装中文分词插件
    IK Analysis for Elasticsearch 是开源社区比较流行的中文分词插件
    官网:https://github.com/medcl/elasticsearch-analysis-ik

    安装方法:
    在安装目录下运行

    ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip
    运行结果:

    ➜ elasticsearch-6.2.4 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip
    -> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip
    [=================================================] 100%
    -> Installed analysis-ik
    安装成功后,在 plugin 文件夹下可以看到出现了 analysis-ik 文件夹。

    安装附件文本抽取插件
    ElasticSearch 官方提供插件:
    Ingest Attachment Processor Plugin
    https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest-attachment.html#ingest-attachment

    此插件开箱即用,用于实现常见格式文档的文本抽取,它基于另一个开源的文本抽取工具库 Apache Tika http://tika.apache.org 实现。

    安装方法:
    在安装目录下运行

    ./bin/elasticsearch-plugin install ingest-attachment
    安装过程中提示此插件需要一些额外的权限,输入y回车,继续安装即可,运行结果:

    -> Downloading ingest-attachment from elastic
    [=================================================] 100%
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    @ WARNING: plugin requires additional permissions @
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    * java.lang.RuntimePermission accessDeclaredMembers
    * java.lang.RuntimePermission getClassLoader
    * java.lang.reflect.ReflectPermission suppressAccessChecks
    * java.security.SecurityPermission createAccessControlContext
    * java.security.SecurityPermission insertProvider
    * java.security.SecurityPermission putProviderProperty.BC
    See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
    for descriptions of what these permissions allow and the associated risks.

    Continue with installation? [y/N]y
    -> Installed ingest-attachment
    安装成功后,在 plugin 文件夹下可以看到出现了 ingest-attachment 文件夹。

    重新启动 ElasticSearch
    所有插件安装完成后重新启动 ElasticSearch

    五、文档附件的文本抽取
    解决方式
    ElasticSearch只能处理文本,不能直接处理二进制文档。要利用 ElasticSearch 实现附件文档的全文检索需要 2 个步骤:

    1. 对多种主流格式的文档进行文本抽取。
    2. 将抽取出来的文本内容导入 ElasticSearch ,利用 ElasticSearch强大的分词和全文索引能力。

    上文安装的 Ingest Attachment Processor Plugin 是一个开箱即用的插件,使用它可以帮助 ElasticSearch 自动完成这 2 个步骤。

    基本原理是利用 ElasticSearch 的 Ingest Node 功能,此功能支持定义命名处理器管道 pipeline,pipeline中可以定义多个处理器,在数据插入 ElasticSearch 之前进行预处理。而 Ingest Attachment Processor Plugin 提供了关键的预处理器 attachment,支持自动对入库文档的指定字段作为文档文件进行文本抽取,并将抽取后得到的文本内容和相关元数据加入原始入库文档。

    因为 ElasticSearch 是基于 JSON 格式的文档数据库,所以附件文档在插入 ElasticSearch 之前必须进行 Base64 编码。

    当然,Attachment Processor Plugin 不是唯一方案。如果需要深入定制文档抽取功能,或基于功能解耦等考量,完全可以利用 Apache Tika http://tika.apache.org 实现独立的文档抽取应用。

    建立文本抽取管道
    ElasticSearch 支持 REST API,我们可以用 cURL、Postman 等工具调用。为方便查看,本文使用如下这种表示方式来展示 REST 调用,请注意,它并不是可执行代码。

    PUT http://localhost:9200/_ingest/pipeline/attachment

    {
    “description”: “Extract attachment information”,
    “processors”: [
    {
    “attachment”: {
    “field”: “data”,
    “ignore_missing”: true
    }
    },
    {
    “remove”: {
    “field”: “data”
    }
    }
    ]
    }
    以上,我们建立了 1 个命名 pipeline 即 “attachment”,其中定义了 2 个预处理器 “attachment” 和 “remove” ,它们按定义顺序对入库数据进行预处理。

    “attachment” 预处理器即上文安装的插件 “Ingest Attachment Processor Plugin” 提供,将入库文档字段 “data” 视为文档附件进行文本抽取。要求入库文档必须将文档附件进行 BASE64编码写入 “data” 字段。

    文本抽取后, 后续不再需要保留 BASE64 编码的文档附件,将其持久化到 ElasticSearch 中没有意义,”remove” 预处理器用于将其从源文档中删除。

    如何使用 pipeline
    按照 ElasticSearch 的 API 定义,插入文档时可以在请求地址末尾加
    ?pipeline=attachment 的形式指定使用上文建立的 “attachment” 命名 pipeline。

    六、建立文档结构映射
    ElasticSearch 是文档型数据库,以 JSON 文档为处理对象。文档结构以 mapping 形式定义,相当于关系型数据库建立表结构。以下,我们建立 MySQL 的 book 表在 ElasticSearch 中的文档结构映射。

    PUT http://localhost:9200/book
    
    {
      "mappings": {
        "idx": {
          "properties": {
            "id": {
              "type": "keyword"
            },
            "title": {
              "type": "text",
              "analyzer": "ik_max_word"
            },
            "desc": {
              "type": "text",
              "analyzer": "ik_max_word"
            },
            "path": {
              "type": "keyword"
            },
            "create_time": {
              "type": "date",
              "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
            },
            "update_time": {
              "type": "date",
              "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
            },
            "attachment": {
              "properties": {
                "content": {
                  "type": "text",
                  "analyzer": "ik_max_word"
                }
              }
            }
          }
        }
      }
    }

    除了 book 表中的原有字段外,我们在 ElasticSearch 中增加了 “attachment” 字段,这个字段是 “attachment” 命名 pipeline 抽取文档附件中文本后自动附加的字段。这是一个嵌套字段,其包含多个子字段,包括抽取文本 content 和一些文档信息元数据。

    在本文的应用场景中,我们需要对 book 的 title、desc 和 attachment.content 进行全文检索,所以在建立 mapping 时,我们为这 3 个字段指定分析器 “analyzer” 为 “ik_max_word”,以让 ElasticSearch 在建立全文索引时对它们进行中文分词。

    七、安装并配置 Apache NiFi
    Apache NiFi (http://nifi.apache.org) 是一个易用、强大、可靠的数据处理、分发系统。本文使用它来完成数据流转处理,如果读者使用其它工具或者自行编程开发应用,请忽略本章。

    本文不是专门的 Apache NiFi 教程,只针对相关应用场景介绍如何使用 Apache NiFi。

    安装 Apache NiFi
    在 Apache NiFi 官网下载并解压到本地,本文当前最新版本为 1.6.0
    下载地址:http://nifi.apache.org/download.html

    Apache NiFi 基于 java 开发,要求运行环境为 JDK 8.0 以上。

    常用配置在 conf 目录下的 nifi.properties 和 bootstrap.conf 文件中,详见:NiFi System Administrator’s Guide

    其中,web 控制台端口在 nifi.proerties 文件中的 nifi.web.http.port 参数修改,默认值 8080。JVM启动参数在 bootstrap.conf 文件中,内存分配在 # JVM memory settings 段,默认 -Xms512m -Xmx512m。

    下载 MySQL Connector/J
    因为 NiFi 需要连接 MySQL 抓取数据,请到 MySQL 官网下载 MySQL Connector/J
    https://dev.mysql.com/downloads/connector/j/

    本文当前最新版本 5.1.46, 将 mysql-connector-java-5.1.46-bin.jar 拷贝到 NiFi 安装目录备用。

    启动 Apache NiFi
    命令行进入 Apache NiFi 目录,运行命令 ./bin/nifi.sh start

    Apache NiFi 的常用命令:

    命令 说明
    run 交互式启动
    start 后台启动
    stop 停止
    status 查看服务状态
    Apache NiFi 提供图形化的 Web 管理控制台,内置丰富的功能组件,通过拖拽的方式即可建立数据处理流程, 启动以后访问 http://localhost:8080/nifi ,控制台如下图:

    NiFi控制台

    配置数据处理流程
    篇幅有限,本文不详细讲解 Apache NiFi,如果读者有兴趣,请前往阅读官方文档:
    http://nifi.apache.org/docs.html

    导入模板
    Apache NiFi 支持将配置好的流程保存为模板,鼓励社区开发者之间分享模板。本章及使用的流程模板已上传至开源项目:
    https://gitee.com/streamone/full-text-search-in-action
    模板文件在 /nifi/FullText-mysql.xml

    下载模板文件 FullText-mysql.xml ,然后点击控制台左侧 “Operate” 操作栏里的 “Upload Template” 上传模板。

    上传模板

    应用模板
    拖拽控制台顶部一排组件图标中的 “Template” 到空白网格区域,在弹出的 “Add Template” 窗口中选择刚刚上传的模板 “FullText-mysql”,点击 “Add”。空白网格区域将出现如下下图的 “process group”,它是一组 “processor” 的集合,我们的处理流程就是由这组 “processor” 按照数据处理逻辑有序组合而成。

    NiFi模板

    双击此 “process group” 进入,将看到完整的流程配置,如下图:

    NiFi process group

    运行这个流程之前需要完成几个配置项:

    配置并启动数据库连接池
    在空白网格处点击鼠标右键,在弹出菜单中点击 “configure”,在弹出的 “FullText-mysql Configuration” 窗口中打开 “controller services” 标签页如下图,点击表格中 “DBCPConnectionPool” 右侧 “Configure” 图标,进行数据库连接池配置。 NiFi controller services
    在弹出的 “Configure Controller Service” 窗口中打开 “PROPERTIES” 标签页,在表格中填写 MySQL数据库相关信息,如下图: 配置数据库连接池
    其中的 “Database Driver Location(s)” 填写我们下载的 “mysql-connector-java-5.1.46-bin.jar” 路径。 配置好数据库连接池以后点击 “APPLY” 回到 “controller services” 标签页,点击表格中 “DBCPConnectionPool” 右侧 “Enable” 图标启动数据库连接池。

    修改变量
    在空白网格处点击鼠标右键,在弹出菜单中点击 “variables”,打开 “Variables” 窗口,修改表格中的 “elasticSearchServer” 参数值为 ElasticSearch 服务地址,修改表格中的 “rootPath” 参数为电子书数字文档在文件系统中的根路径。

    回到 “process group” 流程页面,在空白网格处点击鼠标右键,在弹出菜单中点击 “start” 菜单,启动流程。

    至此,我们完成了本文应用场景中 Apache NiFi 的流程配置。Apache NiFi 每隔 10 秒扫描 MySQL 的 book 表,抓取最新的电子书数据,处理后导入 ElasticSearch。

    八、全文检索查询
    完成以上内容,我们应该已经将 MySQL 数据库中的电子书信息导入 ElasticSearch,并建立了全文索引。

    本章应用场景中,我们想要对电子书的 “title”、”desc”、”attachment.content” (抽取文本) 进行全文检索,帮助用户快速找到关键词为 “计算” 的全部电子书。

    ElasticSearch 提供 REST API,各种编程语言都可以很方便地实现客户端调用,官方提供了多种语言的 client :
    https://www.elastic.co/guide/en/elasticsearch/client/index.html

    本章沿用前述方式展示全文检索请求结构:

    POST http://localhost:9200/book/idx/_search

    {
    “query”: {
    “multi_match”: {
    “query”: “计算”,
    “fields”: [“title”, “desc”, “attachment.content”]
    }
    },
    “_source”: {
    “excludes”: [“attachment.content”]
    },
    “from”: 0, “size”: 200,
    “highlight”: {
    “encoder”: “html”,
    “pre_tags”: [““],
    “post_tags”: [“
    “],
    “fields”: {
    “title”: {},
    “desc”: {},
    “attachment.content”: {}
    }
    }
    }
    我们采用 “multi_match” 进行跨多字段查询。
    关于 “multi_match” 的更多信息,请前往 https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html

    “_source” 用于从返回结果中将 “attachment.conent” 字段过滤掉,因为此字段是从电子书中抽取的文本,内容太大,我们不希望在列表查询中显示它。

    ElasticSearch 默认是分页查询,以 “from” 和 “size” 分别表示偏移量和每页记录数。

    “highlight” 是高亮配置,其中 “fields” 属性中配置的字段高亮信息都会被查询结果返回。”encoder” 是在对关键词加高亮标签之前对原文转义的方式。”pre_tags” 和 “post_tags” 是关键词高亮标签。
    关于 “highlight” 的更多信息,请前往 https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html

    请注意,这段不是可执行程序,这样写仅仅是为了方便查看。以下为对应的 cURL 调用命令:

    curl -X POST \
    http://localhost:9200/book/idx/_search \
    -H ‘Cache-Control: no-cache’ \
    -H ‘Content-Type: application/json’ \
    -d ‘{
    “query”: {
    “multi_match”: {
    “query”: “计算”,
    “fields”: [“title”, “desc”, “attachment.content”]
    }
    },
    “_source”: {
    “excludes”: [“attachment.content”]
    },
    “from”: 0, “size”: 200,
    “highlight”: {
    “encoder”: “html”,
    “pre_tags”: [““],
    “post_tags”: [“
    “],
    “fields”: {
    “title”: {},
    “desc”: {},
    “attachment.content”: {}
    }
    }
    }’

    展开全文
    wenxindiaolong061 2018-09-09 21:16:36
  • Trisyp 2020-08-19 19:44:04
  • zmycoco2 2013-12-23 09:50:13
  • weixin_39846289 2020-12-19 00:28:07
  • Hunter_Murphy 2021-05-21 11:34:10
  • u011389474 2017-04-06 20:14:47
  • leehaicun 2004-08-17 16:31:00
  • weixin_34327761 2018-07-20 09:05:52
  • sxycgxj 2014-08-29 09:10:18
  • u013761665 2016-06-13 18:10:13
  • qq_22238021 2018-07-04 22:15:19
  • weixin_39834984 2020-11-29 10:12:59
  • fgg1234567890 2021-03-04 21:59:35
  • pbrlovejava 2021-12-12 11:47:59
  • 1.29MB vbfgm 2015-12-04 20:10:52
  • wojiushiwo987 2020-02-03 22:46:02
  • pengjunlee 2019-02-10 16:17:50
  • wangfeijiu 2021-02-02 17:30:43
  • 4星
    884KB vbfgm 2013-05-31 21:21:26
  • wangxuelei036 2020-06-03 10:14:09
  • a_zhon 2016-11-18 13:53:32
  • u013870094 2018-12-29 01:13:10
  • qq_18377515 2018-09-15 15:44:31

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 42,946
精华内容 17,178
关键字:

word索引项是什么