Lucene简介(一)

  1. Lucene 是什么?
  • Lucene 是一款高性能的 / 可扩展的信息检索(IR)工具库。信息检索是指文档搜索 / 文档内信息搜索 或者 文档相关的元数据搜索等操作。Lucene 提供了一套简单而强大的核心 API,并且在使用它们时,你不必深入理解全文索引和搜索机制,只需要掌握 Lucene 中少数几个类就可以将它集成到你的应用程序中。Lucene 能够把你从文本中解析出来的数据进行索引和搜索,Lucene 并不关心数据来源 / 格式,甚至不关心数据的语种,只要能把它转换为 文本格式 即可。
  • Lucene 不是一个完整的搜索程序,它只是搜索程序的核心索引和搜索模块而已。

  1. 索引组件
  • 为了快速搜索大量的文本,必须首先建立针对文本索引,将文本内容转换成能够进行快速搜索的格式,从而消除慢速顺序扫描所带来的影响。这个过程就叫做索引操作,它的输出就叫做索引(index),可以将索引想象成一种数据结构,它允许对存储在其中的单词进行快速随机访问。就 Lucene 来说,索引是一个精心设计的数据结构,通常作为一组索引文件存储在文件系统中
  • (1)获取内容。即 搜集和界定需要索引的内容。内容获取模块在访问规模较大的内容集时,重要的是能够以增量方式运行,这样每次运行时就可以只访问针对上次运行后内容有改变的文档。(下一步根据获取的内容来建立小数据块,也称为文档(Document))
  • (2)建立文档。获取原始内容后,就需要对这些内容进行索引,必须首先将这些内容转换成部件(通常称之为文档 即 Document),以供搜索引擎使用。文档(Document)主要包括几个带值的域(Field),(域)比如 标题(title)域 / 正文(body)域 / 摘要(abstract)域 / 作者(author)域 和链接(url)域。必须仔细设计好如何将原始内容分割成合适的文档(Document)和域(Field),以及如何计算其中每个域的值。通常的做法是这样的:一个电子邮件信息作为一个文档,一个 PDF 文档或一个网页作为一个文档。但是这个做法有时候也存在问题:该如何处理电子邮件的附件?是将附件中提取的所有文本合并后统一写入某个文档,还是为各个附件分别创建文档并以某种方式将这些文档和附件关联起来?
    设计完方案后,就需要将原始内容中的文本提取出来写入各个文档(Document)。如果原始内容本来就是文本格式的,并且是使用现成的文本编码格式的话,做法就简单了。但目前的文本格式大多是二进制格式的(PDF 文件 / Microsoft Office 文件 / Open Office 文件 / 视频流和音频多媒体文件)或者包含一些在索引操作前必须去除的固定标记(XML / HTML)。这样,必须使用文档过滤器从这些原始内容中提取文本格式信息,便于后期建立搜索引擎文档(Document)。
    — 在该步骤中,由于复杂的商业逻辑,可能需要创建额外的域(Field)。例如,对于‘正文文本’域(Field)比较大,可以运行语义分析器从中提取出诸如名称 / 地点 / 日期 / 时间 / 位置等信息,再将它们分别作为单独的域(Field)写入文档。对于建立文档(Document)的操作来说,还有一部分常见的内容就是向单个的文档或域中插入加权值,如果这些文档和域比较重要的话。
    — Lucene 提供了一个 API 来建立域(Field)和文档(Document),但不提供任何建立它们的程序逻辑,这些逻辑完全由调用 API 的应用程序根据具体情况完成。Lucene 也不提供任何文档过滤器,但它在 Apache 还有一个姊妹项目叫做 Tika,能很好的实现文档过滤。
    — 至此,文档(Document)中文本格式的域(Field)还不能用于搜索引擎的索引操作。为了进行索引操作,首先需要对文本进行分析
  • (3)文档分析搜索引擎不能直接对文本进行索引:确切地说,我们必须将文本分割成一系列被称为语汇单元的原子元素。这就是在文档分析(Analysis)这一步要做的工作。每一个语汇单元能大致与语言中的 “单词” 对应起来,而这个步骤即 决定文档(Document)中的 [ - 文本域(Field)如何分割成语汇单元系列 - ]
    — Lucene 提供了大量内嵌的分析器能够轻松控制这步操作。也可以搭建自己的分析器,或者联合 Lucene 的语汇单元化工具和语汇单元过滤器来创建自定义的分析链,来定制语汇单元的创建方式。
  • (4)文档索引。在索引步骤中,文档将被加入到索引列表。Lucene 为本步骤提供了所有必要的支持,并且通过一个异常简单的 API 就魔术似的完成了索引操作。

  1. 搜索组件
  • 搜索处理过程就是从索引中查找单词,从而找到包含该单词的文档(Document)。搜索质量主要由 “查准率” 和 “查全率” 来衡量。查全率 用来衡量搜索系统查找相关文档的能力,而 查准率 用来衡量搜索系统过滤非相关文档的能力。
  • (1)用户搜索界面。用户搜索界面是用户与搜索程序交互的可视界面。Lucene 不提供默认的用户搜索界面,该界面完全由你的程序构建。当用户用你的搜索界面进行搜索交换时,他们会提交一个搜索请求,该请求需首先转换成合适的查询(Query)对象格式,以便搜索引擎使用。
  • (2)建立查询(Build Query)。用户提交的查询请求必须被转换成搜索引擎使用的查询(Query)对象格式,这称为建立查询步骤。
    — 查询对象(Query)可能很简单,也可能很复杂。Lucene 提供了一个称之为 “查询解析器”(QueryParser)的强大开发包,它可以根据通用查询语法将用户输入的文本处理成查询对象(Query)。查询语句可以包含布尔运算 / 短语查询 / 通配符查询 等。
    — 对于搜索程序来说,Lucene 默认的查询解析器一般够用。还可以定制查询解析器。
  • (3)搜索查询(Search Query)。搜索查询是这样一个过程:查询搜索索引并返回与查询语句匹配的文档,结果返回时按照查询请求来排序。搜索查询组件涵盖了搜索引擎内部复杂的工作机制。
  • (4)展现结果。一旦获得匹配查询语句并排好序的文档结果集,接下来就可以用直观的 / 经济的方式为用户展现结果。

  1. 索引过程的核心类
    Lucene简介(一)
  • IndexWriter 类。(写索引)是索引过程的核心组件。这个类负责创建新索引或者打开已有索引,以及向索引中添加 / 删除 或 更新被索引文档的信息,提供针对索引文件的写入操作。IndexWriter 需要开辟一定空间来存储索引,该功能可以由 Directory 完成。
  • Directory 类。描述了 Lucene 索引的存放位置。它是一个抽象类,它的子类负责具体指定索引的存储路径。
  • Analyzer 类。(IndexWriter 不能直接索引文本,需要先由 Analyzer 将文本分割成独立的单词才行)文本文件在被索引之前,需要经过 Analyzer(分析器)处理。Analyzer 是由 IndexWriter 的构造方法来指定,它负责从被索引文本文件中提取语汇单元,并剔除剩下的无用信息。如果被索引内容不是纯文本文件,则需要先将其转换成文本文档。
    — 分析器的分析对象为文档,该文档包含一些分离的能被索引的域
  • Document 类。该对象代表一些域(Field)的集合。文档的域代表文档或者和文档相关的一些元数据。文档的数据源对于 Lucene 来说是无关紧要的,Lucene 只处理从二进制文档中提取的以 Field 实例形式出现的文本。元数据(例如 作者 / 标题 / 主题 / 修改日期 等)都可以作为文档的不同域单独存储并被索引
    — 我们可以为每一个检索到的文件创建一个 Document 实例,并向实例中添加各个域,然后将 Document 对象添加到索引中,这样就完成了文档的索引操作。但是在你自己的搜索程序中,你得仔细设计如何建立 Lucene 文档(Document)和域(Field)使之能满足数据源处理要求和程序设计要求
    — Document 对象的结构比较简单,为一个包含多个 Field 对象的容器,Field 是指包含能被索引的文本内容的类
  • Field 类。索引中的每个文档都包含一个或多个不同命名的域,这些域包含在 Field 类中。每个域都有一个域名和对于的域值,以及一组选项来精确控制 Lucene 索引操作的各个域值。文档可能拥有不止一个同名的域。在这种情况下,域的值就按照索引操作顺序添加进去。在搜索时,所有域的文本就好像连接在一起,作为一个文本域来处理。
    — 可是直接创建 Field 类的实例,也可以使用它丰富的子类来实例化

  1. 搜索过程的核心类
  • IndexSearcher 类。IndexSearcher 类用于搜索由 IndexWriter 类创建的索引。可以将 IndexSearcher 类看做一个以只读方式打开索引的类。它需要利用 Directory 实例来掌控前期创建的索引,然后才能提供大量的搜索方法。最简单的搜索方法是将单个 Query 对象和 int topN 计数作为该方法的参数,并返回一个 TopDocs 对象。
  • Term 类。Term 对象是搜索功能的基本单元。与 Field 对象类似,Term 对象包含一对字符串元素:域名和单词。Term 对象还与索引操作有关。由于 Term 对象是由 Lucene 内部创建的,我们并不需要在索引阶段详细了解它们。在搜索过程中可以创建 Term 对象,并和 TermQuery 对象一起使用:
Query q = new TermQuery(new Term("title", "lucene"));
TopDocs hits = searcher.search(q, 10);

以上代码,命令 Lucene 寻找标题(title)域中包含单词 lucene 的前 10 个文档,并按照降序排列这 10 个文档。

  • Query 类。Lucene 含有许多具体的 Query(查询) 子类。例如:TermQuery / BooleanQuery / PhraseQuery 等等。
  • TermQuery 类。TermQuery 是 Lucene 提供的最基本的查询类型,也是简单查询类型之一。它用来匹配指定域中包含特定项的文档。
  • TopDocs 类。TopDocs 类是一个简单的指针容器,指针一般指向前 N 个排名的搜索结果,搜索结果即 匹配查询条件的文档(Document)。TopDocs 会记录前 N 个结果中每个结果的 int docID(可以用来恢复文档 即 Document) 和 浮点型分数。

1. 构建索引
  1. 文档和域
  • 文档(Document)是 Lucene 索引和搜索的原子单位。文档为包含一个或多个域的容器,而域则依次包含 “真正的” 被搜索内容。每个域都有一个标识名称,该名称为一个文本值或二进制值。当将文档加入到索引中时,可以通过一系列选项来控制 Lucene 的行为。在对原始数据进行索引操作时,首先将数据转换成 Lucene 所能识别的文档和域。在随后的搜索过程中,被搜索对象则为域值。例如,用户在输入搜索内容 “title:lucene” 时,搜索结果则为标题(title)域中值包含单词 “lucene“ 的所有文档
  • 如何 将包含各类信息的原始数据转换成 Lucene 文档和域呢? 这一般需要将搜索程序设计成递归处理方式来完成。Lucene 并不知道搜索程序使用哪些域,以及对应的域名称等。文档一般包含多个域,比如 标题 / 作者 / 日期 / 摘要 / 正文 / URL 和 关键字等。有时还需要使用杂项域,即包含所有文本的一个独立域以供搜索。一旦建立好文档,并将它加入到索引后,就可以在随后的搜索过程中检索那些匹配查询条件的文档,并将读取到的文档对应域值作为搜索结果展现给用户。
  • 当搜索程序通过索引检索文档时,-------- 只有被存储的域才会被作为搜索结果展现。例如,被索引但未被存储于文档的域是不会被作为搜索结果展现的 --------。
  1. 域选项(具体选项查看 Field 及 FieldType 类)(重点,如果使用错误选项,则可能导致无法搜索即 搜索结果为空,此时可能并不是没有匹配的选项,而是索引本身出了问题
  • Field 类也许是在文档索引期间最重要的类了:该类在事实上控制着被索引的域值。当创建好一个域值时,可以指定多个 域选项(域选项有:索引选项 / 存储选项 / 项向量使用选项) 来控制 Lucene 在将文档添加进索引后针对该域的行为。
  • 域选项 可查看 FieldType 类。该类用于描述/配置一个 Field 的属性。