Skip to content

Apache Lucene 简介

最开始接触到Apache Lucene可以追述到大一下的暑假了,当时学院的实验室招新,为了让外在的环境驱动自己学习,参加了AC&Lab实验室的宣讲【也就是笔者后续加入的实验室】,当时加入实验室的条件是自己做一个搜索引擎或者推荐系统【由实验室提供学习的资料和教程】。也就是在那时,养成了自我学习的一个能力。通过网上查阅资料,接触到了Apache Lucene,但以当时的心智和知识积累,也只是看一看,增长一下阅历,了解到了Apache Lucene是什么。现在回过头来,希望能重新拾起这些知识,知其然,知其所以然...

熟悉 Apache Lucene

Apache Lucene 是一个高性能、可扩展的信息检索(IR)库,用于在软件应用中添加搜索功能。它是用Java编写的,但也有针对其他编程语言的端口,如PyLucene(Python)。Lucene提供了一个简单却强大的应用程序接口(API),允许进行全文检索和分析。

核心特性:

  • 全文搜索能力:Lucene支持高级全文搜索功能,如短语搜索、通配符搜索、邻近搜索和模糊搜索。
  • 高性能索引:它具有高速的索引创建速度,能够处理大量数据并支持增量索引更新。
  • 强大的分析引擎:Lucene提供了强大的分析框架,用于文本分词和索引,支持多种语言。
  • 可扩展性:它的架构支持自定义扩展,如文本分析器、查询解析和评分算法,以满足特定需求。
  • 跨平台:作为一个Java库,Lucene可以在任何支持Java的平台上运行。

应用场景:

  • 网站搜索:为网站提供内部搜索功能,如电子商务网站、新闻门户等。 企业内部搜索:帮助企业内部用户快速找到存储在不同位置(如文档、电子邮件、数据库)的信息。
  • 桌面搜索:为桌面应用或文件系统提供搜索功能,帮助用户快速定位文件和内容。
  • 垂直搜索引擎:专注于特定领域或类型的信息检索,如学术文献搜索、产品搜索等。

笔者当时是直接使用Spring Boot集成的Elasticsearch写了一个博客系统,后续在大二下的时候学习了《搜索引擎》这一门课后又通过使用Python爬取网上的图书数据,结合ElasticSearch制作了一个图书搜索引擎,后续参加工作了则更多的是结合Elasticsearch和对应的kibana之类的工具做一些日志搜索分析了,总的来说对于Apache Lucene的直接使用确实是少之又少,也算是趁此次机会重新学习一下了...

工作原理简述

Lucene的核心是通过建立索引来实现快速搜索的。索引过程涉及文档的分析、分词,然后将词条(Tokens)存储在一个倒排索引中。倒排索引是一个从词条到其出现文档的映射。当执行搜索查询时,Lucene使用这个索引来快速找到包含查询词条的文档。在开始前,先了解一下一些关于倒排索引的基础概念:

  • 文档(document):索引与搜索的主要数据载体,它包含一个 或多个字段,存放将要写入索引的或将从索引搜索出来的数据。
  • 字段(field):文档的一个片段,它包括字段的名称和字段的内 容两个部分。
  • 词项(term):搜索时的一个单位,代表了文本中的一个词。
  • 词条(token):词项在字段文本中的一次出现,包括词项的文 本、开始和结束的偏移以及词条类型。

举个例子,假设有下面三个文档:

  • Elasticsearch Server(文档1)
  • Mastering Elasticsearch(文档2)
  • Apache Solr 4 Cookbook(文档3)

则这三个文档对应建立的倒排索引则如下所示:

词项数量文档
41<3>
Apache1<3>
Cookbook(文档3)1<3>
Elasticsearch2<1> <2>
Mastering1<1>
Server1<1>
Solr1<3>

正如你所见,每个词项指向该词项所出现过的文档数。这种索引组织方式允许快速有效的搜索操作,例如基于词项的查询。除了词项本身以外,每个词项有一个与之关联的计数(即文档频率),该计数可以告诉Lucene这个词项在多少个文档中出现过。当然,实际的Lucene索引比前面提到的更复杂、更高深,除了词项的文档频率和出现该词项的文档列表外,还包含其他附加信息,如:norm、词项向量、倒排格式、doc values等等。

  1. norm

    norm是一种与每个被索引文档相关的因子,它存储文档的归一化 结果,被用于计算查询的相关得分。norm基于索引时的文档加权值 (boost)计算得出,与文档一起被索引存储。使用norm可以让Lucene 在建立索引时考虑不同文档的权重,不过需要一些额外的磁盘空间和 内存来索引和存储norm信息。

  2. 词项向量

    词项向量(term vector)是一种针对每个文档的微型倒排索引。 词项向量的每个维由词项和出现频率结对组成,还可以包括词项的位 置信息。Lucene和Elasticsearch默认都禁用词项向量索引,不过要实 现某些功能,如关键词高亮等需要启用这个选项。

  3. 倒排项格式

    倒排项中可以存储字段、词项、文档、词项位置和偏移以及载荷 (payload,一个在Lucene索引中随意存放的字节数组,可以包含任何 我们需要的信息)。针对不同的使用目的,Lucene提供了不同的倒排 项格式。比如,有一种优化后的格式是专门为高散列范围字段如唯一 标识提供的。

  4. doc values

    针对某些功能,如切面(faceting)或聚合(aggregation),需要把索引翻转过来构成正排索引才能完成这些功能所需要的计算

以上便是Lucene中索引的一些基本概念,当有了这些概念后,接下来需要了解一下段,在Lucene中每个索引由多个段(segment)组成,每个段才是一个逻辑的可搜索可检索的单元。每个段写入一次但是查询多次。索引期间,一个段创建以后不再修改。例如,文档被删除以后,删除信息单独保存在一个文件中,而段本身并没有被修改。多个段将会在段合并(segments merge)阶段被合并在一起。或者强制执行段合并,或者由Lucene的内在机制决定在某个时刻执行段合并,合并后段的数量更少,但是更大。段合并非常耗费I/O,合并期间有些不再使用的信息将被清理掉,例如,被删除的文档。对于容纳相同数据的索引,段的数量更少的时候搜索速度更快。

对于段,其实究其根本都是为了提升索引的效率,每个段是独立的,具有不变性,write-once单次提交写入内存后,此时该段的内容对外不可见,只有当满足特定条件(如时间间隔、文档数量)时,Lucene会将这个内存中的段写入磁盘,形成一个新的段,此时段内容才可见【这也是为什么说Elasticsearch是近实时的原因之一】。同时对于文档的写操作【删除和更新类似的】,都会将对应文档写入一个新的段,将旧的文档标记为已删除,在进行段合并时才会真正删除旧的文档。

聊到段,那不得不提一嘴docID。倒排索引(reverted index)是Lucene设计的核心。本质上是将一个term分词映射到包含这个分词的文档docID列表。在Lucene搜索引擎中,当执行搜索时,涉及到两阶段查询:

  • 第一阶段是根据term找到包含此term的docID列表。这正是Lucene倒排索引(reverted-index)发挥的意义。

  • 第二阶段是根据找到的docID列表找到整个doc文档。这便是Lucene正排索引(term-vector)发挥的作用。

    term -> <doc-id, doc-freq, doc-position>*

在Lucene中正是使用这个docID唯一的标识文档,但值得注意的是docID实际上并不是索引(index-scope)唯一的,而是段 (segment-scope)唯一的。Lucene这么设计的初衷是为了优化与压缩(docID值越小占用的磁盘空间就越小,感兴趣的可以去了解一下)

分析数据

现在我们知道了 段(segment)->索引(index)->文档(document) 的一个构成,接下来我们从一个词(Term)的搜索正向去看Lucene怎么进行的一个检索。在开始前,对于词(Term)的识别或者转换过程,这里称之为分析过程,也就是将输入的搜索词:Java,转化分析为java、Java Script等不同的一个词,进而去检索想要的document【即上面的两阶段查询过程】。

文本分析由分析器来执行,它建立在分词器(tokenizer)、过滤 器(filter)及字符映射器(character mapper)之上。

Lucene的分词器用来将文本切割成词条,词条是携带各种额外信 息的词项,这些信息包括:词项在原始文本中的位置,词项的长度。 分词器工作的结果被称为词条流,因为这些词条被一个接一个地推送 给过滤器处理。

除了分词器,过滤器也是Lucene分析器的组成部分。过滤器数额 可选,可以为零个、一个或多个,用于处理词条流中的词条。例如, 它可以移除、修改词条流中的词条,甚至可以创造新的词条。Lucene 中有很多现成的过滤器,你也可以根据需要实现新的过滤器。

常见的过滤器:

  • 小写过滤器:将所有词条转化为小写。
  • ASCII过滤器:移除词条中所有非ASCII字符。
  • 同义词过滤器:根据同义词规则,将一个词条转化为另一个词 条。
  • 多语言词干还原过滤器:将词条的文本部分归约到它们的词根形式,即词干还原。当分析器中有多个过滤器时,会逐个处理,理论上可以有无限多个过滤器。

过滤器可以一个接一个地被调用,因此我们可以通过逐个添加多个过滤器的方式来获得近乎无限的分析能力。至于最后的字符引射器,它用于调用分词器之前的文本预处理操作。字符映射器的一个例子就是HTML文本的去标签处理。

需要注意的是:在检索时,如果你使用了某个查询分析器(query parser),那么你的查询串将会被分析,但是也有例外:如前缀查询(prefix query)不会被分析,而匹配查询(match query)会被分析。 另外:索引期与检索期的文本分析要采用同样的分析器,只有查询(query)分词出来的词项与索引中词项能匹配上,才会返回预期的文档集。例如,如果在索引期使用了词干还原与小写转换,那么在查询期,也应该对查询串做相同的处理,否则,查询可能不会返回任何结果。

综上,对于Apache Lucene的简介就先介绍到这里,毕竟该系列主要是讲解Elasticsearch的,但是Elasticsearch是建立在Apache Lucene之上的,想要学好Elasticsearch,还是要对Lucene下功夫,同时通过源码与实战不断的Debug。

Released under the MIT License.