Lucene(一)

  1. Lucene概述

    Lucene是一款高性能的、可扩展的信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作。

  2. 索引过程:

    ①获取内容

    ②建立文档 
    获取原始内容后,就需要对这些内容进行索引,必须首先将这些内容转换成部件(通常称为文档),以供搜索引擎使用。文档主要包括几个带值的域,比如标题、正文、摘要、作者和链接。

    ③文档分析 
    搜索引擎不能直接对文本进行索引:确切地说,必须将文本分割成一系列被称为语汇单元的独立的原子元素。每一个语汇单元大致与语言中的“单词”对应起来。

    ④文档索引 
    在索引步骤中,文档被加入到索引列表。

  3. 搜索组件

    搜索处理过程就是从索引中查找单词,从而找到包含该单词的文档。搜索质量主要由查准率和查全率来衡量。查全率用来衡量搜索系统查找相关文档的能力;而查准率用来衡量搜索系统过滤非相关文档的能力。 
    ①用户搜索界面 
    Lucene不提供默认的用户搜索界面,需要自己开发。 
    ②建立查询 
    用户从搜索界面提交一个搜索请求,通常以HTML表单或者Ajax请求的形式由浏览器提交到你的搜索引擎服务器。然后将这个请求转换成搜索引擎使用的查询对象格式,这称为建立查询。 
    ③搜索查询 
    查询检索索引并返回与查询语句匹配的文档,结果返回时按照查询请求来排序。 
    ④展现结果 
    一旦获得匹配查询语句并排好序的文档结果集,接下来就得用直观的、经济的方式为用户展现结果。

  4. 索引过程的核心类

    ·IndexWriter 
    ·Directory 
    ·Analyzer 
    ·Document 
    ·Field

    ①IndexWriter 
    索引过程的核心组件。这个类负责创建新索引或者打开已有索引,以及向索引中添加、删除或更新被索引文档的信息。可以把IndexWriter看作这样一个对象:它为你提供针对索引文件的写入操作,但不能用于读取或搜索索引。IndexWriter需要开辟一定空间来存储索引,该功能可以由Directory完成。

    ②Directory 
    该类描述了Lucene索引的存放位置。它是一个抽象类,它的子类负责具体指定索引的存储路径。用FSDirectory.open方法来获取真实文件在文件系统的存储路径,然后将它们一次传递给IndexWriter类构造方法。IndexWriter不能直接索引文本,这需要先由Analyzer将文本分割成独立的单词才行。

    ③Analyzer 
    文本文件在被索引之前,需要经过Analyzer(分析器)处理。Analyzer是由IndexWriter的构造方法来指定的,它负责从被索引文本文件中提取语汇单元,并提出剩下的无用信息。如果被索引内容不是纯文本文件,那就需要先将其转换为文本文档。对于要将Lucene集成到应用程序的开发人员来说,选择什么样Analyzer是程序设计中非常关键的一步。分析器的分析对象为文档,该文档包含一些分离的能被索引的域。

    ④Document 
    Document对象代表一些域(Field)的集合。文档的域代表文档或者文档相关的一些元数据。元数据(如作者、标题、主题和修改日期等)都作为文档的不同域单独存储并被索引。Document对象的结构比较简单,为一个包含多个Filed对象容器;Field是指包含能被索引的文本内容的类。

    ⑤Field 
    索引中的每个文档都包含一个或多个不同命名的域,这些域包含在Field类中。每个域都有一个域名和对应的域值,以及一组选项来精确控制Lucene索引操作各个域值。

  5. 搜索过程中的核心类

    ·IndexSearcher 
    ·Term 
    ·Query 
    ·TermQuery 
    ·TopDocs

    Lucene(一) 
    ①IndexSearcher 
    该类用于搜索由IndexWriter类创建的索引,它是连接索引的中心环节。可以将IndexSearcher类看作是一个以只读方式打开索引的类。它需要利用Directory实例来掌控前期创建的索引,然后才能提供大量的搜索方法。 
    ②Term 
    Term对象是搜索功能的基本单元。Term对象包含一对字符串元素:域名和单词(或域名文本值)。 
    ③Query 
    包含了一些非常有用的方法,TermQuery是它的一个子类。 
    ④TermQuery 
    该类提供最基本的查询,用来匹配指定域中包含特定项的文档。 
    ⑤TopDocs 
    该类是一个简单的指针容器,指针一般指向前N个排名的搜索结果,搜索结果即匹配查询条件的文档。

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

    ①文档和域 
    文档是Lucene索引和搜索的原子单位。文档为包含一个或多个域的容器,而域则依次包含“真正的”被搜索内容。每个域都有一个标识名称,该名称为一个文本值或二进制值。如:用户在输入搜索内容“title:lucene”时,搜索结果则为标题域值包含单词“lucene”的所有文档。

    Lucene可以针对域进行3种操作: 
    ·域值可以被索引。如果需要搜索一个域,则必须首先对它进行索引。被索引的域值必须是文本格式的(二进制格式的域值只能被存储而不能被索引)。在索引一个域时,需要首先使用分析过程将域值转换为语汇单元,然后将语汇单元加入到索引中。 
    ·域被索引后,还可以选择性地存储项向量,后者可以看作该域的一个小型反向索引集合,通过该向量能够检索该域的所有语汇单元。这个机制有助于实现一些高级功能,比如搜索与当前文档相似的文档。 
    ·域值可以被单独存储,即是说被分析前的域值备份也可以写进索引中,以便后续的检索。这个机制可以使你将原始值展现给用户,比如文档的标题或摘要。

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

    ②灵活的架构 
    与数据库不同的是,Lucene没有一个确定的全局模式;Lucene要求在进行索引操作时简单化或反向规格化原始数据。

  7. 理解索引过程

    Lucene(一) 
    在索引操作期间,文本首先从原始数据中提取出来,并用于创建对应的Document实例,该实例包含多个Field实例,它们都用来保存原始数据信息。随后的分析过程将域文本处理成大量语汇单元。最后将语汇单元加入到段结构中。 
    ①提取文本和创建文档 
    使用Lucene索引数据时,必须先从数据中提取纯文本格式信息,以便Lucene识别该文本并建立对应的Lucene文档。 
    ②分析文档 
    一旦建立其Lucene文档和域,就可以调用IndexWriter对象的addDocument方法将数据传递给Lucene进行索引操作了。在索引操作时,Lucene首先分析文本,将文本数据分割成语汇单元串,然后对它们执行一些可选操作。 
    ③向索引添加文档 
    对输入数据分析完毕后,就可以将分析结果写入索引文件中。Lucene将输入数据以一种倒排索引的数据结构进行存储。在进行关键字快速查找时,这种数据结构能够有效利用磁盘空间。Lucene使用倒排数据结构的原因是:把文档中提取出的语汇单元作为查询关键字,而不是将文档作为中心实体,这种思想很像书籍的索引与页码的对应关系。

    –索引段 
    Lucene索引都包含一个或多个段。每个段都是一个独立的索引,它包含整个文档索引的一个子集。每当writer刷新缓冲区增加的文档,以及挂起目录删除操作时,索引文件都会建立一个新段。在搜索索引时,每个段都是单独访问的,但搜索结果是合并后返回的。 
    每个段文件都包含多个文件,文件格式为_X.,这里X代表段名称,为扩展名,用来标识该文件对应索引的某个部分。如果使用混合文件格式(这是Lucene默认的处理方式,可以通过IndexWriter.setUseCompoundFile方法进行修改),那么上述索引文件都会被压缩成一个单一的文件:_X.cfs。这种方式能在搜索期间减少打开的文件数量。 
    还有一个特殊文件,名叫段文件,用段_标识,该文件指向索引**的段。Lucene会首先打开该文件,然后打开它所指向的其他文件。IndexWriter类会周期性地选择一些段,然后将它们合并到一个新段中,然后删除老的段。被合并段的选取策略由一个独立的MergePolicy类主导。

    Lucene(一)

  8. 基本索引操作

    ①向索引添加文档 
    添加文档的方法有两个: 
    ——addDocument(Document),使用默认分析器添加文档,该分析器在创建IndexWriter对象时指定,用于语汇单元操作 
    ——addDocument(Document , Analyzer),使用指定的分析器添加文档和语汇单元操作。

    ②删除文档 
    ——deleteDocuments(Term)负责删除包含项的所有文档 
    ——deleteDocuments(Term[]) 
    ——deleteDocuments(Query) 
    ——deleteDocuments(Query[]) 
    ——deleteAll() 
    通过Term类删除单个文档,需要确认在每个文档中都已索引过对应的Field类,还需要确认所有域值都是以唯一的,这样才能将这个文档单独找出来删除。 
    注意,如果碰巧指定了一个错误的Term对象(例如,由一个普通的被索引的域文本创建的Term对象,而不是由唯一ID值创建的域),那么Lucene将很容易地快速删除索引中的大量文档。删除操作不会马上执行,而是放入内存缓冲区,Lucene周期性刷新文档目录来执行该操作。不过即使删除操作已完成,存储该文档的磁盘空间也不会马上释放,Lucene只是将该文档标记为“删除”。

    ③更新文档 
    Lucene更新文档通过先删除整个旧文档,然后向索引中添加新文档来完成。 
    ——updateDocument(Term,Document)首先删除包含Term变量的所有文档,然后使用writer的默认分析器添加新文档。 
    ——updateDocument(Term,Document,Analyzer)同上,区别是可以指定分析器。由于updateDocument方法要在后台调用deleteDocuments方法,因此注意:要确认被更新文档的Term标识的唯一性。

  9. 域选项

    ①域索引选项(Field.Index.*) 
    域索引选项通过倒排索引来控制域文本是否可被搜索。 
    ——Index.ANALYZED,使用分析器将域值分解成独立的语汇单元流,并使每个语汇单元能被搜索。该选项适用于普通文本域(如正文、标题、摘要等) 
    ——Index.NOT_ANALYZED,对域进行索引,但不对String值进行分析。该操作实际上将域值作为单一语汇单元并使之能被搜索。该选项适用于索引那些不能被分解的域值,如URL、文件路径、日期、人名等。 
    ——Index.ANALYZED_NO_NORMS,这是Index.ANALYZED的变体,它不会在索引中存储norms信息。norms记录了索引中的index-time boost信息,但是当你进行搜索时可能会比较耗费内存。 
    ——Index.NOT_ANALYZED_NO_NORMS。该选项常用语在搜索期间节省索引空间和减少内存耗费,因为single-token域并不需要norms信息,除非它们已被进行加权操作。 
    ——Index.NO,使对应的域值不被搜索。 
    当Lucene建立起倒排索引后,默认情况下它会保存所有必要信息以实施VectorSpace Model。该Model需要计算文档中出现的term数,以及它们出现的位置。但有时候这些域只是在布尔搜索时用到,它们并不为相关评分做贡献,一个常见的例子是,域只是被用作过滤,如权限过滤和日期过滤。这种情况下,可以通过调用Field.setOmitTermFreqAndPositions(true)方法让Lucene跳过该项的出现频率和出现位置的索引。该方法可以节省一些索引在磁盘上的存储空间,还可以加速搜索和过滤过程,但会阻止需要位置信息的搜索,如阻止PhraseQuery和SpanQuery类的运行。

    ②域存储选项(Field.Store.*) 
    用来确定是否需要存储域的真实值,以便后续搜索时能恢复这个值: 
    ——Store.YES,指定存储域值。原始的字符串全部被保存在索引中,并可以由IndexReader类恢复。该选项对于需要展示搜索结果的一些域很有用(如URL、标题或数据库主键)。如果索引的大小在搜索程序考虑之列的化,不要存储太大的文本域值,因为这些域值会消耗掉索引的存储空间。 
    ——Store.NO,指定不存储域值 
    Lucene包含一个工具类CompressionTools,提供静态方法压缩和解压字节数组。可以在存储域值之前对它进行压缩。注意,尽管该方法可以为索引节省一些空间,但节省的幅度跟域值的可被压缩程度有关,而且该方法会降低索引和搜索速度。这样其实就是通过消耗更多的CPU计算能力来换取更多的磁盘空间。如果域值所占空间很小,建议少使用压缩。

    ③域的项向量选项 
    它是介于索引域和存储域的一个中间结构。

    ④Reader、TokenStream和byte[]域值 
    Field对象还有其他几个初始化方法,允许传入除String以外的其他参数: 
    ·Field (String name, Reader value, TermVector termVector)方法使用 Reader而不是String对象来表示域值。在这种情况下,域值是不能被存储的。(域存储选项被硬编码成 Store. NO ), 并且该域会一直用于分析和索引(Index.ANALYZED ). 如果在内存中保存 String 代价较高或者不太方便时,如存储的域值较大时,使用这个初始化方法则比较有效. 
    ·Field(String name,Reader value), 与前述方法类似, 使用 Reader 而不是 String 对象来表示域值,但使用该方法时,默认的 termVector 为 TermVector.NO 
    • Field (String name, TokenStream tokenStream, termVector termVector )允许程序对域值进行预分析并生成 TokenStream 对象.此外,这个域不会被存储并将一直用于分析和索引. 
    • Field(String name,TokenStream tokenStream), 与前一个方法类似,允许程序对域值进行预分析并生成 TokenStream 对象,但使用该方法时默认的termVector 为 TermVector.NO. 
    • Field (String name, byte [] value, Store store)方法可以用来存储二进制域, 如用不参与索引的域( Index.NO )和没有项向量的域( TermVector. NO ). 其中 store 参数必须设置为 Store.YES. 
    • Field (String name, byte[] value, int offset, int length, Stroe store) 与前一个方法类似,能够对二进制域进行索引,区别在于该方法允许你对这个二进制的部分片段进行引用,该片段的起始位置可以用 offset 参数表示,处理长度可以用参数 length 对应的字节数来表示. 
    Field 类是一个非常复杂的类, 它提供了大量的初始化选项,以向 Lucene 传达精确的域值处理指令。

    ⑤域选项组合

    Lucene(一)