本章要点

  • 执行基本索引操作
  • 在索引过程中对文档和域进行加权操作
  • 对日期、数字和可排序域进行索引
  • 高级索引技术

如果想要搜索存储在硬盘上的文件、电子邮件、网页或是数据库中的数据,Lucene都可帮你完成。但在进行搜索前,你必须对搜索内容进行索引,Lucene同样能帮你完成,这就是本章要讨论的内容。

在第1章我们演示了一个索引示例。本章将更深入讲解索引更新操作,如何通过参数来调整索引过程,以及更高级的索引技巧,从而帮助你更好理解Lucene。本章还将涉及Lucene索引的结构,使用多线程和多进程访问Lucene索引时要关注的重点内容,Lucene索引API的事务语义,通过远程文件系统共享索引,以及防止并发修改索引的锁机制等内容。

在进入大量细节内容之前,别忘了概况:索引操作仅仅是整个搜索程序的一个简单的步骤而已。重要的是搜索程序带给用户的搜索体验;索引操作“只不过”是为了增强用户搜索体验而需要跨越的一道障碍而已。因此,尽管本章有很多有趣的索引操作细节,你最好还是将主要精力花在如何提升用户搜索体验上。对于几乎所有搜索程序来说,搜索功能都比索引操作细节重要得多。话虽这么说,在实现搜索功能时还是要依赖于索引操作期间的相关重要步骤的,正如本章所述。

注意:本章内容非常长。这个长度是必要的,因为Lucene公开了大量索引操作细节。好消息是大多数搜索程序都不比采用Lucene的高级索引选项。事实上,2.1小节、2.2小节、和2.3小节所介绍的内容才是这些搜索程序所需要的。如果你对索引比较感兴趣,同时又不想丢掉其中任何一部分内容,或者你的搜索程序需要使用所有的其他功能,那么可以研读本章余下内容。

下面我们从Lucene有关搜索内容的概念模型开始介绍

2.1 Lucene如何对搜索内容进行建模

我们首先阐述有关内容建模的方法概念。我们从Lucene有关索引和搜索、文档和域的基本单元开始,然后将重点转移到Lucene与当代数据库更为结构化的数据模型之间的区别。

2.1.1 文档和域

文档是Lucene索引和搜索的原子单位。文档为包含一个或多个域的容器,而域则依次包含【真正的】被搜索内容。每个域都有一个标识名称,该名称为一个文本值或二进制值。当你将文档加入到索引中时,可以通过一系列选项来控制Lucene的行为。在对原始数据进行索引操作时,你得首先将数据转换成Lucene所能识别的文档和域。在随手的搜索过程中,被搜索对象则为值域;例如,用户在输入搜索内容“title:lucene”时,搜索结果则为标题值域包含单词【lucene】的所有文档。

进一步的,Lucene可以针对域进行3种操作。

  • 值域可以被索引(或者不被索引)。如果需要搜索一个域,则必须首先对它进行索引。被索引的值域必须是文本格式的(二进制格式的值域只能被存储而不能被索引)。在索引一个域时,需要首先使用分析过程将域值转换成语汇单元,然后将语汇单元加入到索引中。有关索引域值的具体操作选项可以参考2.4.1小结
  • 域被索引后,还可以选择性的存储项向量,后者可以看做该域的一个小型反向索引集合,通过该向量可以检索该域的所有语汇单元。这个机制有助于实现一些高级功能,比如搜索与当前文档相似的文档(更多高级共轭能详见5.7小节)。有关控制索引项向量的具体选项请参考2.4.3小节。
  • 域值可以被单独存储,即是说备份洗钱的域值备份也可以写进索引中,以便后续的检索。这个机制可以使你将原始域值展现给用户,比如文档的标题或摘要。域值的存储选项请参考2.4.2小节。

如何将包含各类信息的原始数据转换成Lucene文档和域呢?这一版需要将搜索程序设计成递归处理方式来完成。Lucene并不知道搜索程序使用哪些域,以及对应的域名称等。文档一般包含多个域,比如标题、作者、日期、只要、正文、URL和关键词等。有时还需要使用杂项域,即包含所有文本的一个独立域以供搜索。一旦建好文档,并将它加入到索引后,就可以在随后的搜索过程中检索哪些匹配查询条件的文档,并将读取到的文档对应域值作为搜索结果展现给用户。

人们通常将Lucene与数据库进行比较,因为二者都会存储数据内容并提供内容检索功能。但两者之间有着重大差别,首先是灵活架构的差别

当搜索程序从通过索引检索文档时,只有被存储的域才会被作为搜索结果展现。例如,被索引但未被存储于文档的域是不会被作为搜索结果展现的。这种机制通常会使得搜索结果具有不确定性。

2.1.2 灵活的架构

与数据库不同的是,Lucene没有一个确定的全局模式。也就是说,加入索引的每个文档都是独立的,它与此前加入的文档完全没有关系;它可以包含任意的域,以及任意的索引、存储和项向量操作选项。它也不必包含于其他文档相同的域。他甚至可以做到于其他文档内容相同,仅是相关操作选项有所区别。

Lucene的这种特性非常实用:这使你能够递归访问文档并建立对应的索引。你可以随时对文档进行索引,而不必提前设计文档的数据结构表。如果随后你想向文档中添加域,那么可以完成添加后重新索引该文档或重建索引即可。

Lucene的灵活架构还意味着单一的索引可以包含不同实体的多个文档,例如,用一个文档的诸如名称和价格等域来表示零售产品,而用另一个文档的诸如姓名、年龄和性别等域来表示人,另外还可以使用一个不可达的【中间态】文档,该文档只包含有关索引或搜索程序的一些中间数据(比如最近一次更新索引的时间,或者被索引的产品目录),同时该【中间态】文档内容不在搜索结果中出现。

Lucene和数据库之间的第二个主要区别是,Lucene要求你在进行索引操作时简单化或者返现规格化原始数据。

2.1.3 反向规格化(Denormalization)

我们面临的一个挑战是解决文档真是结构和Lucene表示能力之间的“不匹配”问题。举例来说,XML文档通过嵌套标记来表示一个递归的文档结构,数据库可能有任意数量的连接点,表之间可以通过主键和次键相互关联起来。微软的Object Linking & Embedding文档可以指向其他嵌入类文档。然而Lucene文档却都是单一文档,因此在创建对应的Lucene文档之前,必须对上述递归文档结构和连接点进行反向规格化操作。建立在Lucene基础之上的开源项目。如Hibernate Search、Compass、LuSQL、DBSight、Browse Engine和Oracle/Lucene integration等,都有各自不同而有趣的方法来解决反向规格化问题。

至此你已经在概念层面上了解了Lucene的文档模型,下面我们将深入阐述Lucene索引步骤。

2.2 理解索引过程

正如第1章所述,索引一个文件只需要调用Lucene公用API的几个方法即可完成。结果是,从表面上看来,用Lucene进行索引操作是一个简单而独立的操作。其实隐藏在这些简单API背后的却是一套巧妙而相对复杂的操作。这些操作从功能上主要分为3个部分,如图2.1所示,下面几节我们将对此进行详述。

Lucene实战-第2章-构建索引
在索引操作期间,文本首先从原始数据中提取出来,并用于创建对应的Document实例,该实例包含多个Field实例,他们都用来保存原始数据信息。随后的分析过程将域文本处理成大量语汇单元。最后将语汇单元加入到段结构中。下面我们从文本提取开始阐述。

2.2.1 提取文本和创建文档

使用Lucene索引数据时,必须先从数据中提取纯文本格式信息,一遍Lucene识别该文本并建立对应的Lucene文档。在第一章我们将搜索和索引的示例文本限制为.txt格式文本,这使得我们能轻易地拆分这些文本内容,并用之建立起Field实例。但事实上文本格式并不都这么简单,图1.4有关“建立文档”步骤其实包含了很多隐藏内容。

假设你需要对一堆PDF格式的手册进行索引,你必须首先想法从这些PDF文件中提取文本格式信息,并用这些文本信息来创建Lucene文档和域。而Java中并没有对应的方法来处理PDF格式文件,该问题同样存在与Microsoft Word文件或者其他非纯文本格式文件中。即使再处理XML或HTML等纯文本格式文件时,也需要灵活考虑到底索引哪些内容,你得索引他们所表达的真正文本内容,而不是XML元素或HTML标签等无意义的文本。

有关提取文本信息的细节将在第7章结合Tika框架详述,使用该框架能使你很轻易地从各种格式的文件中提取文本信息。一旦提取出预想的文本信息并建立起对应的、包含各个域的文档后,下一步就是对这些文本信息进行分析了。

相关文章:

  • 2021-05-30
  • 2021-08-24
  • 2021-08-11
  • 2021-11-03
  • 2021-11-08
  • 2021-11-02
猜你喜欢
  • 2021-05-03
  • 2022-12-23
  • 2021-07-17
  • 2021-06-11
  • 2022-12-23
  • 2021-09-10
相关资源
相似解决方案