Apache Lucene简介

Apache Lucene简介

什么是Lucene

Lucene是一个开放源代码的全文搜索引擎工具包,提供了完整的查询引擎和索引引擎,部分文本分析引擎。

基本概念

首先,我们了解下lucene中的一些基本概念,它们包括:

文档(document):索引与搜索的主要数据载体,它包含一个或多个字段,存放将要写入索引或将从索引搜索出来的数据。

字段(field):文档的一个片段,它包含两个部分:字段的名称和内容。

词项(term):搜索时的一个单位,代表文本中的某个词。

词条(token):词项在文档中的一次出现,包括词项的文本、开始和结束的位移以及类型。

倒排索引

搜索引擎的关键步骤是建立倒排索引,倒排索引一般表示为一个关键词,然后是它的频度、位置。

倒排索引(inverted index)是一种将词项映射到文档的数据结构。

倒排索引源于实际应用中需要根据属性的值查找记录,这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引。

Lucene使用的是倒排索引文件结构,下面用例子介绍该结构及相应的生成算法。

假设有两篇文章1和文章2.

文章1的内容为:Tom lives in Guangzhou, I live in Guangzhou too.

文章2的内容为:He once lived in Shanghai.

取得关键词

由于Lucene是基于关键词索引和查询的,首先我们要取得这两篇文章的关键词。

  • 现有的文章内容,即一个字符串,我们先要找出字符串中的所有单词,即分词。英文单词由于用空格分隔,比较好处理。中文单词间由于是连在一起的,所以需要特殊的分词处理。
  • 文章中的“in” “once” “too”等词没有实际意义,中文中的“的” “是”等字通常也无具体含义,这些不代表概念的词可以过滤掉。
  • 用户通常希望查“He”时能把“he”和“HE”的文章也找出来,所以所有单词需要同意大小写。
  • 用户通常希望查“live”时能把“lives”和“lived”的文章也找出来,所以需要把“lives”,“lived”还原成“live”。
  • 文章中的标点符号通常不表示某种概念,也可以过滤掉。

文章1的所有关键词为:[tom] [live] [guangzhou] [I] [live] [guangzhou]

文章2的所有关键词为:[he] [live] [shanghai]

建立倒排索引

有了关键词后,就可以建立倒排索引。上面的对应关系是 “文章号”对“文章中所有的关键词”。倒排索引把这个关系倒过来。

倒排索引关键词频率位置示例:

关键词 文章号[出现频率] 出现位置
guangzhou 1[2] 3,6
he 2[1] 1
i 1[1] 4
live 1[2]
2[1]
2,5
2
shanghai 2[1] 3
tom 1[1] 1

以live这行为例,说明一下该结构:live在文章1中出现了2次,那么“2,5”就表示live在文章1中出现的两个位置。

文章2中出现了1次,“2”就表示“live”是文章2中的第2个关键字。

关键词在文章中出现的位置,通常有两种:

  • 字符位置,即记录该词是文章中的第几个字符(优点是显示并定位关键词快)。
  • 关键词位置,即记录该词是文章中的第几个关键词(优点是节约索引空间、词组查询快),Lucene中记录的就是这种位置。

通过观察发现关键字是按字符顺序排序的,因此Lucene可以用二元搜索算法快速定位关键词。

实现

实现时,Lucene将上面三列分别作为词典文件,频率文件,位置文件保存。其中词典文件中不仅保存了每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。

压缩算法

为了减小索引文件的大小,Lucene对索引还使用了压缩技术。

首先对词典文件中的关键词进行了压缩,关键词压缩为<前缀文件,后缀>,例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>。

其次用到的是对数字的压缩,数字只保存与上一个值的差值(这样可以减少数字的长度,进而减少保存该数字需要的字节数)。例如当前文章号是16389(不压缩用3个字节保存),上一个文章号是16382,压缩后保存7(只用一个字节)。

应用场景

通过对比来解释为什么要建立索引。

假设要查询单词”live”,Lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有的文章号,然后返回结果。词典通常非常小,因而整个过程的时间是毫秒级的。

而用普通的顺序匹配算法,不建索引,对所有的文章内容进行字符串匹配,这个过程会相当缓慢,当文章数目很大时,时间是无法忍受的。

数据分析

文档中的数据是如何转化为倒排索引的,而查询串又是如何转换为可用于搜索的词项的?这个转换过程称为分析。

文本分析由分析器来执行,而分析器由分词器,过滤器和字符映射器组成。

Lucene的分词器用来将文本切割成词条,其中携带各种额外信息的词项,包括 词项在原始文本中的位置、词项的长度。这些词条呗一个接一个地推送给过滤器处理。

Lucene提供了很多现成的过滤器

  • 小写过滤器:将所有词条转换为小写。
  • ASCII过滤器:移除词条中所有非ASCII字符。
  • 同义词过滤器:根据同义词规则,将一个词转化为另一个词条。
  • 多语言词干还原过滤器,将词条的文本部分规约到它们的词根形式,即词干还原。

当分析器中有多个过滤器时,会逐个处理,理论上可以有无限多个过滤器。

字符映射器,用于调用分词器之前的文本预处理操作,如HTML文本的去标签处理。

查询语言

在Lucene中,一个查询通常被分割为词项与操作符。Lucene中的词项可以是单个词,也可以是一个短语(用双引号括起来的一组词)。

布尔操作符

用于连接多个词项,使之构成从句。

  • AND :它的含义是,文档匹配当前从句当且仅当AND操作符左右两边的词项都在文档中出现。例如:执行apache AND lucene这样的查询,只有当同时包含apache和lucene这两个词项的文档时才会返回给用户。
  • OR:它的含义是,包含当前从句中的任意词项的文档都会被视为与该从句匹配。执行 apache OR lucene 这样的查询,任意包含词项apache或词项lucene的文档都会返回给用户。
  • NOT:它的含义是,与当前从句匹配的文档必须不包含NOT操作符后面的词项。例如执行lucene NOT elasticsearch这样的查询,只有包含词项lucene且不包含词项elasticsearch的文档才会会返回给用户。
  • +:它的含义是,只有包含+操作符后面词项的文档才会被认为是与从句匹配。例如,查找那些必须包含lucene,但是apache可出现可不出现的文档,可执行查询:+lucene apache
  • -:它的含义是,与从句匹配的文档不能出现-操作符后的词项。例如,查找那些包含lucene但不包含elasticsearch的文档,可以执行+lucene-elasticsearch。

在字段中查询

为了实现针对某个字段的查询,用户需要提供字段的名称,再加上冒号以及将要在该字段中执行查询的从句。

例如要查询所有在title字段中包含词项elasticsearch的文档,可以执行如下查询:

title:elasticsearch

也可以在一个字段中使用多个从句。例如,要查找所有在title字段中同时包含词项elasticsearch和短语mastering book的文档,可执行如下查询:

title:(+elasticsearch +”mastering book”)

词项修饰符

Lucene支持两种通配符:?和*。前者匹配任意一个字符,而后者匹配多个字符。

出于对性能的考虑,通配符不能作为词项的第一个字符出现。

Lucene支持模糊查询,通过使用~字符以及一个紧随其后的整数值。

当使用该修饰符修饰一个词项时,意味着我们想搜索那些包含该词项近义词项的文档。

例如当我们执行查询:writer~2,意味着包含词项writer和writers的文档都能与查询匹配。

当执行这个查询:title:”mastering elasticsearch”~2,则title:”mastering book elasticsearch”,title:”mastering elasticsearch”都会被认为与查询匹配。

此外可以使用^字符并赋以一个浮点数对词项加权(boosting),以提高该词项的重要程度。默认情况下词项权重为1。

我们也可以使用方括号和花括号来构建范围查询。

例如:

price:[10.00 TO 15.00]

查询所返回文档的price字段的值大于等于10.00并小于等于15.00。

name:[Adam TO Adria]

查询所返回文档的name字段包含按字典排序介于Adam和Adria之间(包括Adam和Adria)的词项。

price:[10.00 TO 15.00}

查找price字段值大于等于10.00但小于15.00的文档。

特殊字符处理

很多应用场景中,需要搜索某个特殊字符(这些特殊字符包括+、-、&&、||、!、(,)、{}、[]、^、”、~、*、?、:、\、/),这时先使用反斜杠对这些特殊字符进行转义。

例如搜索abc“efg这个词项,需要按如下方式处理:

abc\“efg