Lucene3.5自学4--建索引相关知识总结

Lucene简单介绍(该部分摘自网络)

Lucene是一个高效的,基于Java的全文检索库。

所以在了解Lucene之前要费一番工夫了解一下全文检索。

那么什么叫做全文检索呢?这要从我们生活中的数据说起。

我们生活中的数据总体分为两种:结构化数据和非结构化数据。

  • 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。
  • 非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等。

当然有的地方还会提到第三种,半结构化数据,如XMLHTML等,当根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。

非结构化数据又一种叫法叫全文数据。

 

按照数据的分类,搜索也分为两种:

  • 对结构化数据的搜索:如对数据库的搜索,用SQL语句。再如对元数据的搜索,如利用windows搜索对文件名,类型,修改时间进行搜索等。
  • 对非结构化数据的搜索:如利用windows的搜索也可以搜索文件内容,Linux下的grep命令,再如用Google和百度可以搜索大量内容数据。

对非结构化数据也即对全文数据的搜索主要有两种方法:

一种是顺序扫描法(Serial Scanning):所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。如果你有一个80G硬盘,如果想在上面找到一个内容包含某字符串的文件,不花他几个小时,怕是做不到。Linux下的grep命令也是这一种方式。大家可能觉得这种方法比较原始,但对于小数据量的文件,这种方法还是最直接,最方便的。但是对于大量的文件,这种方法就很慢了。

有人可能会说,对非结构化数据顺序扫描很慢,对结构化数据的搜索却相对较快(由于结构化数据有一定的结构可以采取一定的搜索算法加快速度),那么把我们的非结构化数据想办法弄得有一定结构不就行了吗?

这种想法很天然,却构成了全文检索的基本思路,也即将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。

这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。

这种说法比较抽象,举几个例子就很容易明白,比如字典,字典的拼音表和部首检字表就相当于字典的索引,对每一个字的解释是非结构化的,如果字典没有音节表和部首检字表,在茫茫辞海中找一个字只能顺序扫描。然而字的某些信息可以提取出来进行结构化处理,比如读音,就比较结构化,分声母和韵母,分别只有几种可以一一列举,于是将读音拿出来按一定的顺序排列,每一项读音都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到读音,然后按其指向的页数,便可找到我们的非结构化数据——也即对字的解释。

这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)

 

下面这幅图来自《Lucene in action》,但却不仅仅描述了Lucene的检索过程,而是描述了全文检索的一般过程。

  

全文检索大体分两个过程,索引创建(Indexing)和搜索索引(Search)

  • 索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
  • 搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程

详情可以参考http://forfuture1978.iteye.com/blog/546771 

Lucene3.5 建立索引总结

Lucene的索引结构是有层次结构的,主要分以下几个层次:

  • 索引(Index)
    • Lucene中一个索引是放在一个文件夹中的。
    • 如上图,同一文件夹中的所有的文件构成一个Lucene索引。
  • (Segment)
    • 一个索引可以包含多个段,段与段之间是独立的,添加新文档可以生成新的段,不同的段可以合并。
    • 如上图,具有相同前缀文件的属同一个段,图中共两个段 "_0" 和 "_1"
    • segments.gen和segments_5是段的元数据文件,也即它们保存了段的属性信息。
  • 文档(Document)
    • 文档是我们建索引的基本单位,不同的文档是保存在不同的段中的,一个段可以包含多篇文档。
    • 新添加的文档是单独保存在一个新生成的段中,随着段的合并,不同的文档合并到同一个段中。
  • (Field)
    • 一篇文档包含不同类型的信息,可以分开索引,比如标题,时间,正文,作者等,都可以保存在不同的域里。
    • 不同域的索引方式可以不同,在真正解析域的存储的时候,我们会详细解读。
  • (Term)
    • 词是索引的最小单位,是经过词法分析和语言处理后的字符串。

(一)Field相当于数据库表的字段

1.其中的Field(字段或域)的构造方法有以下几种

Field(String name, boolean internName, String value, Field.Store store, Field.Index index, Field.TermVector termVector) 常用的

Field(String name, byte[] value) 

Field(String name, byte[] value, int offset, int length) 

Field(String name, Reader reader) 

Field(String name, Reader reader, Field.TermVector termVector) 该Reader当在值是从文件中读取很长的数据时用,单身磁盘I/O操作太多效率太低了

Field(String name, String value, Field.Store store, Field.Index index)

Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector) 

Field(String name, TokenStream tokenStream) 

Field(String name, TokenStream tokenStream, Field.TermVector termVector) 

l 其中 Field.Store表示是否存储,值分别为YES ,NO

l  Field.Index 表示是否索引(允许检索),它已经包括了是否分词,其值有以下几种

ANALYZED 分词并索引
          Index the tokens produced by running the field's value through an Analyzer.

ANALYZED_NO_NORMS 分词索引 但是禁用存储规范
          Expert: Index the tokens produced by running the field's value through an Analyzer, and also separately disable the storing of norms.

NO  不索引,当然肯定不会去分词
          Do not index the field value.

NOT_ANALYZED 索引但不会分词
          Index the field's value without using an Analyzer, so it can be searched.

NOT_ANALYZED_NO_NORMS 不索引 同时禁用存储规范

 

l Field.TermVector 表示对Field词条向量是否进行存储,(具体什么意思我也还没弄明白)其值有以下几种

NO  不存储词条向量
          Do not store term vectors.

WITH_OFFSETS 存储词条和其偏移量
          Store the term vector + Token offset information

WITH_POSITIONS 存储词条和其位置
          Store the term vector + token position information

WITH_POSITIONS_OFFSETS 存储词条和 词条偏移量及其位置
          Store the term vector + Token position and offset information

YES 存储每个document的词条向量

       Store the term vectors of each document.

2.document:简单的说就像数据库中的一条记录,它的每个字段就是Field。注意:数据库中你建立了字段,所有的记录具有相同记录,即数据库是以字段为主导的;而Lucene是以document为主导的,同一个索引(相当于数据库中一张表)中不同的document是可以不一样的。下面介绍docuemnt常用几种方法:

add(Fieldable field) 将一个Field 加入document,注意Field是实现了Fieldable接口的类

get(String name) 返回当前docuement指定Field名的Field

getBoost() 返回索引时设置的加权因子(具体到底是什么没搞明白)
          Returns, at indexing time, the boost factor as set by setBoost(float).

getFieldable(String name) 看英语吧,这个很简单
          Returns a field with the given name if any exist in this document, or null.

getFieldables(String name)  看英语吧,这个很简单,只不过返回的是多个
          Returns an array of Fieldables with the given name.

getFields() 
          Returns a List of all the fields in a document.

getFields(String name) 
          Deprecated. use getFieldable(java.lang.String) instead and cast depending on data type.

getValues(String name) 返回指定Field名的Field值的数组
          Returns an array of values of the field specified as the method parameter.

removeField(String name) 移除当前document的指定的Field 
          Removes field with the specified name from the document.

removeFields(String name) 移除多个Field
          Removes all fields with the given name from the document.

setBoost(float boost) 
          Sets a boost factor for hits(词条) on any field of this document.

toString() 
          Prints the fields of a document for human consumption.

(三)、AnalyzerLucene的分析工具,无论是建立索引还是搜索过程,该类是一个抽象类,所以网上常用适合Lucene的分词器都是它的继承,例如用Lucene自带的StandardAnalyzer;如 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_35) 

 

(四)IndexWriter 他是建立索引的类,至于该类如何实现建立索引请参考前面的文章

IndexWriter.setMaxFieldLength(int)表示索引数据时对数据源的最大长度进行现在,如设置为100,则表示 取数据源前100个长度进行索引分析,当然存储是全都存储,当然长度不足100的就全都索引。

l 、一个索引可以有多个segment(段),但一个索引目录里只有一个segments(该文件记录了所有的segmet);一个segment有多个document(可以设置最大值),同一个segment在磁盘上各种物理的文件的前缀是相同的:如图


其中 

l .frq 记录的是词条频率信息,

l .prx记录的词条的未知信息,

l .fnm记录了所有Field的信息,

l .fdt/.fdx 

  • 域数据文件(fdt):
    • 真正保存存储域(stored field)信息的是fdt文件
    • 在一个段(segment)中总共有segment size篇文档,所以fdt文件中共有segment size个项,每一项保存一篇文档的域的信息
    • 对于每一篇文档,一开始是一个fieldcount,也即此文档包含的域的数目,接下来是fieldcount个项,每一项保存一个域的信息。
    • 对于每一个域,fieldnum是域号,接着是一个8位的byte,最低一位表示此域是否分词(tokenized),倒数第二位表示此域是保存字符串数据还是二进制数据,倒数第三位表示此域是否被压缩,再接下来就是存储域的值,比如new Field("title", "lucene in action", Field.Store.Yes, ),则此处存放的就是"lucene in action"这个字符串。
  • 域索引文件(fdx)
    • 由域数据文件格式我们知道,每篇文档包含的域的个数,每个存储域的值都是不一样的,因而域数据文件中segment size篇文档,每篇文档占用的大小也是不一样的,那么如何在fdt中辨别每一篇文档的起始地址和终止地址呢,如何能够更快的找到第n篇文档的存储域的信息呢?就是要借助域索引文件。
    • 域索引文件也总共有segment size个项,每篇文档都有一个项,每一项都是一个long,大小固定,每一项都是对应的文档在fdt文件中的起始地址的偏移量,这样如果我们想找到第n篇文档的存储域的信息,只要在fdx中找到第n项,然后按照取出的long作为偏移量,就可以在fdt文件中找到对应的存储域的信息。

l 词典文件(tis)

  • TermCount:词典中包含的总的词数
  • IndexInterval:为了加快对词的查找速度,也应用类似跳跃表的结构,假设IndexInterval4,则在词典索引(tii)文件中保存第4个,第8个,第12个词,这样可以加快在词典文件中查找词的速度。
  • SkipInterval:倒排表无论是文档号及词频,还是位置信息,都是以跳跃表的结构存在的,SkipInterval是跳跃的步数。
  • MaxSkipLevels:跳跃表是多层的,这个值指的是跳跃表的最大层数。
  • TermCount个项的数组,每一项代表一个词,对于每一个词,以前缀后缀规则存放词的文本信息(PrefixLength + Suffix),词属于的域的域号(FieldNum),有多少篇文档包含此词(DocFreq),此词的倒排表在frqprx中的偏移量(FreqDelta, ProxDelta),此词的倒排表的跳跃表在frq中的偏移量(SkipDelta),这里之所以用Delta,是应用差值规则。

l 词典索引文件(tii)

  • 词典索引文件是为了加快对词典文件中词的查找速度,保存每隔IndexInterval个词。
  • 词典索引文件是会被全部加载到内存中去的。
  • IndexTermCount = TermCount / IndexInterval:词典索引文件中包含的词数。
  • IndexInterval同词典文件中的IndexInterval
  • SkipInterval同词典文件中的SkipInterval
  • MaxSkipLevels同词典文件中的MaxSkipLevels
  • IndexTermCount个项的数组,每一项代表一个词,每一项包括两部分,第一部分是词本身(TermInfo),第二部分是在词典文件中的偏移量(IndexDelta)。假设IndexInterval4,此数组中保存第4个,第8个,第12个词...

l 标准化因子文件(nrm)

为什么会有标准化因子呢?从第一章中的描述,我们知道,在搜索过程中,搜索出的文档要按与查询语句的相关性排序,相关性大的打分(score)高,从而排在前面。相关性打分(score)使用向量空间模型(Vector Space Model),在计算相关性之前,要计算Term Weight,也即某Term相对于某Document的重要性。在计算Term Weight时,主要有两个影响因素,一个是此Term在此文档中出现的次数,一个是此Term的普通程度。显然此Term在此文档中出现的次数越多,此Term在此文档中越重要。

这种Term Weight的计算方法是最普通的,然而存在以下几个问题:

  • 不同的文档重要性不同。有的文档重要些,有的文档相对不重要,比如对于做软件的,在索引书籍的时候,我想让计算机方面的书更容易搜到,而文学方面的书籍搜索时排名靠后。
  • 不同的域重要性不同。有的域重要一些,如关键字,如标题,有的域不重要一些,如附件等。同样一个词(Term),出现在关键字中应该比出现在附件中打分要高。
  • 根据词(Term)在文档中出现的绝对次数来决定此词对文档的重要性,有不合理的地方。比如长的文档词在文档中出现的次数相对较多,这样短的文档比较吃亏。比如一个词在一本砖头书中出现了10次,在另外一篇不足100字的文章中出现了9次,就说明砖头书应该排在前面码?不应该,显然此词在不足100字的文章中能出现9次,可见其对此文章的重要性。

由于以上原因,Lucene在计算Term Weight时,都会乘上一个标准化因子(Normalization Factor),来减少上面三个问题的影响。

标准化因子(Normalization Factor)是会影响随后打分(score)的计算的,Lucene的打分计算一部分发生在索引过程中,一般是与查询语句无关的参数如标准化因子,大部分发生在搜索过程中,会在搜索过程的代码分析中详述。

标准化因子(Normalization Factor)在索引过程总的计算如下:

 

它包括三个参数:

  • Document boost:此值越大,说明此文档越重要。
  • Field boost:此域越大,说明此域越重要。
  • lengthNorm(field) = (1.0 / Math.sqrt(numTerms)):一个域中包含的Term总数越多,也即文档越长,此值越小,文档越短,此值越大。

从上面的公式,我们知道,一个词(Term)出现在不同的文档或不同的域中,标准化因子不同。比如有两个文档,每个文档有两个域,如果不考虑文档长度,就有四种排列组合,在重要文档的重要域中,在重要文档的非重要域中,在非重要文档的重要域中,在非重要文档的非重要域中,四种组合,每种有不同的标准化因子。

(五)索引优化,合并策略,以前的版本中通过合并因子Mergefactor以及maxMergegeDocs都是通过IndexWriter来设置的,在3.5版本中这些方法都过时了,现在这些优化方法都集成到MergePolicy(一个抽象类),所以索引优化时可以用如下方法来设置
MergePolicy me = new LogMergePolicy()该类继承MergePolicy; 然后通过IndexWriterConfig调用setMergePolicy(MergePolicy mergePolicy)  来设置,然后通过3.5版本中IndexWriter的唯一推荐的构造方法IndexWriter(Directory d, IndexWriterConfig conf)  来设置,其他构造方法都过时了。
(六)、IndexReader类,该类主要负责索引的操作,如读取,修改,删除操作,具体可以参考http://xiaozu.renren.com/xiaozu/258210/336375170 ,很代码已经详细说明了。

转载于:https://www.cnblogs.com/qingfeideyi/archive/2012/03/04/2379526.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/475269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 734. 句子相似性(哈希)

文章目录1. 题目2. 解题1. 题目 给定两个句子 words1, words2 (每个用字符串数组表示),和一个相似单词对的列表 pairs ,判断是否两个句子是相似的。 例如,当相似单词对是 pairs [["great", "fine&qu…

LeetCode 758. 字符串中的加粗单词(Trie树)

文章目录1. 题目2. 解题1. 题目 给定一个关键词集合 words 和一个字符串 S&#xff0c;将所有 S 中出现的关键词加粗。所有在标签 <b> 和 </b> 中的字母都会加粗。 返回的字符串需要使用尽可能少的标签&#xff0c;当然标签应形成有效的组合。 例如&#xff0c;…

博客园配置windows live writer,实现本地代码高亮

你是否还在为只能用浏览器在博客后台写文章而苦恼&#xff1f; 你是否还在纠结于live writer没有代码高亮而苦恼&#xff1f; 现在一切都能解决&#xff1a; 其实博客园支持SyntaxHighlighter代码着色&#xff0c;可以用相应的Live Writer代码着色插件进行代码着色。他们推荐的…

LeetCode 246. 中心对称数(哈希)

文章目录1. 题目2. 解题1. 题目 中心对称数是指一个数字在旋转了 180 度之后看起来依旧相同的数字&#xff08;或者上下颠倒地看&#xff09;。 请写一个函数来判断该数字是否是中心对称数&#xff0c;其输入将会以一个字符串的形式来表达数字。 示例 1: 输入: "69&qu…

LeetCode 276. 栅栏涂色(DP)

文章目录1. 题目2. 解题2.1 DP超时解2.2 DP解1. 题目 有 k 种颜色的涂料和一个包含 n 个栅栏柱的栅栏&#xff0c;每个栅栏柱可以用其中一种颜色进行上色。 你需要给所有栅栏柱上色&#xff0c;并且保证其中相邻的栅栏柱 最多连续两个 颜色相同。然后&#xff0c;返回所有有效…

LeetCode 1176. 健身计划评估(滑动窗口)

文章目录1. 题目2. 解题1. 题目 你的好友是一位健身爱好者。前段日子&#xff0c;他给自己制定了一份健身计划。现在想请你帮他评估一下这份计划是否合理。 他会有一份计划消耗的卡路里表&#xff0c;其中 calories[i] 给出了你的这位好友在第 i 天需要消耗的卡路里总量。 为…

为什么苹果不再需要谷歌地图?

苹果和谷歌之间的专利大战正是如火如荼&#xff0c;在这种两军对垒的时候&#xff0c;iOS设备上的那个明晃晃的谷歌地图图标就像眼中钉肉中刺一样让苹果始终不得安宁&#xff0c;让对手的核心服务扎根到自己的核心平台中&#xff0c;这感觉肯定不好。在最新版本的iPhoto中&…

LeetCode 422. 有效的单词方块

文章目录1. 题目2. 解题1. 题目 给你一个单词序列&#xff0c;判断其是否形成了一个有效的单词方块。 有效的单词方块是指此由单词序列组成的文字方块的 第 k 行 和 第 k 列 (0 ≤ k < max(行数, 列数)) 所显示的字符串完全相同。 注意&#xff1a; 给定的单词数大于等于…

SVG-不是图片的图片

SVG-不是图片的图片 导语&#xff1a; 可缩放矢量图形&#xff08;Scalable Vector Graphics&#xff0c;SVG&#xff09;是基于可扩展标记语言&#xff08;XML&#xff09;&#xff0c;用于描述二维矢量图形的一种图形格式。SVG由W3C制定&#xff0c;是一个开放标准。另SVG还是…

LeetCode 1490. 克隆 N 叉树(DFS/BFS)

文章目录1. 题目2. 解题2.1 DFS2.2 BFS1. 题目 给定一棵 N 叉树的根节点 root &#xff0c;返回该树的深拷贝&#xff08;克隆&#xff09;。 N 叉树的每个节点都包含一个值&#xff08; int &#xff09;和子节点的列表&#xff08; List[Node] &#xff09;。 class Node …

奇数阶幻方 java_N(奇数)阶幻方-java实现代码

看完最强大脑&#xff0c;有一期是说N阶幻立方的&#xff0c;作为一个程序员&#xff0c;我的第一反应时我可以用程序实现&#xff0c;在此公布N(奇数)阶幻方的java实现代码&#xff1a;package com.lzugis.test;public class Practice {public static int[][] magicOdd(int n)…

LeetCode 1265. 逆序打印不可变链表(递归)

文章目录1. 题目2. 解题1. 题目 给您一个不可变的链表&#xff0c;使用下列接口逆序打印每个节点的值&#xff1a; ImmutableListNode: 描述不可变链表的接口&#xff0c;链表的头节点已给出。 您需要使用以下函数来访问此链表&#xff08;您 不能 直接访问 ImmutableListNo…

LeetCode 408. 有效单词缩写

文章目录1. 题目2. 解题1. 题目 给一个 非空 字符串 s 和一个单词缩写 abbr &#xff0c;判断这个缩写是否可以是给定单词的缩写。 字符串 “word” 的所有有效缩写为&#xff1a; ["word", "1ord", "w1rd", "wo1d", "wor1&qu…

LeetCode 604. 迭代压缩字符串

文章目录1. 题目2. 解题1. 题目 对于一个压缩字符串&#xff0c;设计一个数据结构&#xff0c;它支持如下两种操作&#xff1a; next 和 hasNext。 给定的压缩字符串格式为&#xff1a;每个字母后面紧跟一个正整数&#xff0c;这个整数表示该字母在解压后的字符串里连续出现的…

LeetCode 1056. 易混淆数(哈希)

文章目录1. 题目2. 解题1. 题目 给定一个数字 N&#xff0c;当它满足以下条件的时候返回 true&#xff1a; 原数字旋转 180 以后可以得到新的数字。 如 0, 1, 6, 8, 9 旋转 180 以后&#xff0c;得到了新的数字 0, 1, 9, 8, 6 。 2, 3, 4, 5, 7 旋转 180 后&#xff0c;得到…

LeetCode 624. 数组列表中的最大距离

文章目录1. 题目2. 解题2.1 暴力超时2.2 优化1. 题目 给定 m 个数组&#xff0c;每个数组都已经按照升序排好序了。 现在你需要从两个不同的数组中选择两个整数&#xff08;每个数组选一个&#xff09;并且计算它们的距离。 两个整数 a 和 b 之间的距离定义为它们差的绝对值 |…

strcpy()函数一个简单那程序来了解一下它。。

View Code #include <stdio.h>#include <string.h>int main() { char a[5] "ABCD"; char b[8] "abcdefg"; char c[8] "abcdefg"; char d[5] "ABCD";strcpy(a,b); //长的复制给短的strcpy(c,d);//短的复制给长的print…

LeetCode 156. 上下翻转二叉树(DFS)*

文章目录1. 题目2. 解题1. 题目 给定一个二叉树&#xff0c;其中所有的右节点要么是具有兄弟节点&#xff08;拥有相同父节点的左节点&#xff09;的叶节点&#xff0c;要么为空 将此二叉树上下翻转并将它变成一棵树&#xff0c; 原来的右节点将转换成左叶节点。返回新的根。 …

LeetCode 311. 稀疏矩阵的乘法

文章目录1. 题目2. 解题2.1 暴力求解2.2 选取都不为0的行和列相乘1. 题目 给你两个 稀疏矩阵 A 和 B&#xff0c;请你返回 AB 的结果。 你可以默认 A 的列数等于 B 的行数。 请仔细阅读下面的示例。 示例&#xff1a; 输入&#xff1a; A [[ 1, 0, 0],[-1, 0, 3] ] B [[ 7…

创业产品经理需要懂技术吗?

作为一位工程师&#xff0c;和一位在不断追求更好产品的设计人员。个人认为&#xff0c;产品经理最好是这样的&#xff1a;一、 精通技术。技术很容易框住人的思想&#xff0c;要不特别精通&#xff0c;能随时跳出技术的束缚&#xff0c;带给产品真正的提升。一般这种产品经理会…