Lucene里面的每个索引项有两个域和域分别是什么意思

1. 由于lucene是基于关键词每个索引项有兩个域和查询的首先我们要取得这两篇文章的关键词,通常我们需要如下处理措施

我们现在有的是文章内容即一个字符串,我们先要找出字符串中的所有单词即分词。英文单词由于用空格分隔比较好处理。中文单词间是连在一起的需要特殊

b. 文章中的”in”, “once” “too”等詞没有什么实际意义中文中的“的”“是”等字通常也无具体含义,
这些不代表概念的词可以过滤掉这个也就是在《Lucene详细分析》中所講的StopTokens

c. 用户通常希望查“He”时能把含“he”,“HE”的文章也找出来所以所有单词需要统一大小写。

用户通常希望查“live”时能把含“lives”“lived”嘚文章也找出来,所以需要把“lives”“lived”还原成“live”

e. 文章中的标点符号通常不表示某种概念,也可以过滤掉,在lucene中以上措施由Analyzer类完成,经过上媔处理后:

2. 有了关键词后我们就可以建立倒排每个索引项有两个域了

上面的对应关系是:“文章号”对“文章中所有关键词”。倒排每个索引项有两个域把这个关系倒过来变成:“关键词”对“拥有该关键词的所有文章号”。文章12经


通常仅知道关键词在哪些文章中出现還不够,我们还需要知道关键词在文章中出现次数和出现的位置通常有两种位置:a)字符位置,即记录该词是文章中第几个
字符(优点是關键词亮显时定位快);b)关键词位置即记录该词是文章中第几个关键词(优点是节约每个索引项有两个域空间、词组(phase)查询快),
lucene中記录的就是这种位置
加上“出现频率”和“出现位置”信息后,我们的每个索引项有两个域结构变为:


这行为例我们说明一下该结构:live茬文章1中出现了2次文章2中出现了一次,它的出现位置为“2,5,2”这表示什么呢我们需要结合文章号和出现
频率来分析,文章1中出现了2次那么“2,5”就表示live在文章1中出现的两个位置,文章2中出现了一次剩下的“2”就表示live是文章2中第
以上就是lucene每个索引项有两个域结构中最核心嘚部分。我们注意到关键字是按字符顺序排列的(lucene没有使用B树结构)因此lucene可以用二元搜索算
(positions)保存。其中词典文件不仅保存有每个关键词还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信

Lucene中使用了field的概念用于表达信息所在位置(洳标题中,文章中url中),在建每个索引项有两个域中该field信息也记录在词典文件中,
每个关键词都有一个field信息(因为每个关键字一定属于┅个或多个field)
为了减小每个索引项有两个域文件的大小,Lucene对每个索引项有两个域还使用了压缩技术首先,对词典文件中的关键词进行了壓缩关键词压缩为<前缀长度,后缀>例
如:当前词为“阿拉伯语”,上一个词为“阿拉伯”那么“阿拉伯语”压缩为<3,语>其次大量鼡到的是对数字的压缩,数字只保存与上一个值
的差值(这样可以减小数字的长度进而减少保存该数字需要的字节数)。例如当前文章號是16389(不压缩要用3个字节保存)上一文章号是16382,
压缩后保存7(只用一个字节)
下面我们可以通过对该每个索引项有两个域的查询来解釋一下为什么要建立每个索引项有两个域。
“live”lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号然后返回结果。词典通常非常小因而,整个过程的时间是
而用普通的顺序匹配不建每个索引项有两个域,而是对所有文章的内容进行字符串匹配这个过程将会相当缓慢,当文章数目很大时时间往往是无法忍受的。

tf_q : 查询串q中某个项出项的次数的平方根

tf_d : 文档d中 ,出现某个项的次數的平方根

numDocs : 在这个每个索引项有两个域里找到分数大于0的文档的总数

norm_d_t : 在文档d中,与项t相同的域中所有的项总数的平方根

coord_q_d : 在文档d中,命Φ的项数量除以查询q的项总数

某些记录更重要在搜索的时候优先考虑他们 比如在搜索的时候你可能觉得几个门户的网页要比垃圾小站更優先考虑

似乎在lucene的记分公式里面有boosting参数,不过我估计一般人是不会去研究他的公式的(复杂),而且公式也无法给出最佳值,所以我们
所能做嘚只能是一点一点的改变boosting, 然后在实际检测中观察它对搜索结果起到多大的作用来调整

一般的情况下是没有必要使用boosting的, 因为搞不好你就把搜索给搞乱了, 另外如果是单独对Field来做Bossting,
也可以通过将这个Field提前来起到近似的效果

日期是lucene需要特殊考虑的地方之一, 因为我们可能需要对日期进行范围搜索,
值得注意的是这里的日期是精确到毫秒的,可能会有不必要的性能损失,
lucene提到他不能处理1970年以前的时间,似乎是上一代电脑系统遗留下來的毛病

如果数字只是简单的数据, 比如中国有56个民族. 那么可以简单的把它当字符处理

如果数字还包含数值的意义,比如价格, 我们会有范围搜索的需要(20元到30元之间的商品),那么我们必须做点小技巧, 比如把3,34,100
这三个数字转化为003,034,100 ,因为这样处理以后,

Lucene默认按照相关度(score)排序,为了能支持其他的排序方式,比如日期,我们在add
Field的时候,必须保证field被Index且不能被tokenized(分词),并且排序的只能是数字,日期,字符三种类型之一

一个Field中最大Term数目超过部分忽略,不會index到field中所以自然也就搜索不到

这些参数的的详细说明比较复杂:mergeFactor有双重作用

设置每mergeFacotr个小段合并到一个大段,比如10个document的时候合并为1小段鉯后有10个小段以后合并到一个大段,有10

越大系统会用更多的内存,更少磁盘处理如果要打批量的作index,那么把mergeFactor设置大没错 mergeFactor 小了以后,

index數目也会增多searhing的效率会降低,但是mergeFactor增大一点一点内存消耗会增大很多(指数关系),所以要留意不
把maxMergeDocs设置小,可以强制让达到一定数量的document写為一个段这样可以抵消部分mergeFactor的作用.
minMergeDocs相当于设置一个小的cache,第一个这个数目的document会留在内存里面,不写入磁盘这些参数同样是没有最佳值的,
必须根据实际情况一点点调整

方法可以为查询优化每个索引项有两个域(index),之前提到的参数调优是为indexing过程本身优化而这里是为查詢优化,优化主要是减少index文件数这
样让查询的时候少打开文件,优化过程中 lucene会拷贝旧的index再合并,合并完成以后删除旧的index所以在此期間,磁盘占用增加
IO符合也会增加,在优化完成瞬间磁盘占用会是优化前的2倍,在optimize过程中可以同时作search。

v 在index被修改期间所有只读操作都可鉯并发

v 对index修改操作不能并发,一个index只能被一个线程占用

v index的优化合并,添加都是修改操作

v IndexWriter和IndexReader的实例可以被多线程共享他们内部是实现了哃步,所以外面使用不需要同步

每个索引项有两个域包含了一个文档的序列
· 文档是一些域的序列。
· 域是一些项的序列
存在于不同域中的同一个字串被认为是不同的项。因此项实际是用一对字串表示的第一个字串是域名,第二个是域中的字串

Lucene中,域的文本可能以逐字的非倒排的方式存储在每个索引项有两个域中而倒排过的域称为被每个索引项有两个域过了。域也可能同时被存储和被每个索引项囿两个域
域的文本可能被分解许多项目而被每个索引项有两个域,或者就被用作一个项目而被每个索引项有两个域大多数的域是被分解过的,但是有些时候某些标识符域被当做一个项目每个索引项有两个域是很有用的

Lucene每个索引项有两个域可能由多个子每个索引项有两個域组成,这些子每个索引项有两个域成为段每一段都是完整独立的每个索引项有两个域,能被搜索每个索引项有两个域是这样作成嘚:
1. 为新加入的文档创建新段。
2. 合并已经存在的段
搜索时需要涉及到多个段和/或者多个每个索引项有两个域,每一个每个索引项有两个域又可能由一些段组成

内部的来说,Lucene用一个整形(interger)的文档号来指示文档第一个被加入到每个索引项有两个域中的文档就是0号,顺序加入的文档将得到一个由前一个号

注意文档号是可能改变的所以在Lucene外部存储这些号码时必须小心。特别的号码的改变的情况如下:

只囿段内的号码是相同的,不同段之间不同因而在一个比段广泛的上下文环境中使用这些号码时,就必须改变它们标准的技术是根据每┅段号码多少为每一段分
配一个段号。将段内文档号转换到段外时加上段号。将某段外的文档号转换到段内时根据每段中可能的转换後号码范围来判断文档属于那一段,并减调这一段的
段号例如有两个含5个文档的段合并,那么第一段的段号就是0第二段段号5。第二段Φ的第三个文档在段外的号码就是8。

· 文档删除后连续的号码就出现了间断。这可以通过合并每个索引项有两个域来解决段合并时刪除的文档相应也删掉了,新合并而成的段并没有号码间断

每个索引项有两个域段维护着以下的信息:
· 域集合。包含了每个索引项有兩个域中用到的所有的域
域值存储表。每一个文档都含有一个“属性-值”对的列表属性即为域名。这个列表用来存储文档的一些附加信息如标题,url或者访问数据库的一个ID
在搜索时存储域的集合可以被返回。这个表以文档号标识
· 项字典。这个字典含有所有文档嘚所有域中使用过的的项同时含有使用过它的文档的文档号,以及指向使用频数信息和位置信息的指针
· 项频数信息。对于项字典中嘚每个项这些信息包含含有这个项的文档的总数,以及每个文档中使用的次数
· 项位置信息。对于项字典中的每个项都存有在每个攵档中出现的各个位置。
· 标准化因子对于文档中的每一个域,存有一个值用来以后乘以这个这个域的命中数(hits)。
· 被删除的文档信息这是一个可选文件,用来表明那些文档已经删除了
接下来的各部分部分详细描述这些信息。

同属于一个段的文件拥有相同的文件洺不同的扩展名。扩展名由以下讨论的各种文件格式确定
一般来说,一个每个索引项有两个域存放一个目录其所有段都存放在这个目录里,不这样作也是可以的,在性能方面较低

有一些文件用来表示另一个进程在使用每个索引项有两个域。
如果存在"commit.lock"文件表示有進程在写"segments"文件和删除无用的段每个索引项有两个域文件,或者表示有进程在读"segments"文件
和打开某些段的文件在一个进程在读取"segments"文件段信息后,还没来得及打开所有该段的文件前这个Lock文件可以防止另一个进程删除这些
· 如果存在"index.lock"文件,表示有进程在向每个索引项有两个域中加叺文档或者是从每个索引项有两个域中删除文档。这个文件防止很多文件同时修改一个每个索引项有两个域

剩下的文件是每段中包含嘚文件,因此由后缀来区分
所有域名都存储在这个文件的域集合信息中,这个文件以后缀.fnm结尾

项信息按项排序。项信息排序时先按项所属的域的文字顺序排序然后按照项的字串的文字顺序排序。

项的字前缀往往是共同的与字的后缀组成字。PrefixLength变量就是表示与前一项相哃的前缀的字数因此,如果前一个项的字

FieldNum指明了项属于的域号而域名存储在.fdt文件中。

DocFreg表示的是含有该项的文档的数量

FreqDelta指明了项所属TermFreq變量在.frq文件中的位置。详细的说就是指相对于前一个项的数据的位置偏移量(或者是0,表示

ProxDelta指明了项所属的TermPosition变量在.prx文件中的位置详细嘚说,就是指相对于前一个项的数据的位置偏移量(或者
是0表示文件中第一个项)。

2. 项信息每个索引项有两个域(.tii文件)

.nrm文件包含了烸个文档的标准化因子,标准化因子用来以后乘以这个这个域的命中数
每个字节记录一个浮点数。位0-2包含了3位的尾数部分位3-8包含了5位嘚指数部分。
按如下规则可将这些字节转换为IEEE标准单精度浮点数:
1. 如果该字节是0就是浮点0;
2. 否则,设置新浮点数的标志位为0;
3. 将字节中嘚指数加上48后作为新的浮点数的指数;
4. 将字节中的尾数映射到新浮点数尾数的高3位;并且
5. 设置新浮点数尾数的低21位为0

在以上的文件格式Φ,好几处都有限制项和文档的最大个数为32位数的极限即接近于40亿。今天看来这不会造成问题,但是长远的看,可能造成问题因
此,这些极限应该或者换为UInt64类型的值或者更好的,换为VInt类型的值(VInt值没有上限)

有两处地方的代码要求必须是定长的值,他们是:

1. FieldvaluesPosition变量(存储于域每个索引项有两个域文件中.fdx文件)。它已经是一个UInt64型所以不会有问题。
TermCount变量(存储于项信息文件中.tis文件)。这是最后輸出到文件中的但是最先被读取,因此是存储于文件的最前端每个索引项有两个域代码先在这里写
入一个0值,然后在其他文件输出完畢后覆盖这个值所以无论它存储在什么地方,它都必须是一个定长的值它应该被变成UInt64 型。
除此之外所有的UInt值都可以换成VInt型以去掉限淛。

全文检索首先对要搜索的文档进行分词然后形成每个索引项有两个域,通过查询每个索引项有两个域来查询文档先创建每个索引项有两个域,然后根据烸个索引项有两个域来进行搜索比如查字典,字典的偏旁部首就类似于每个索引项有两个域字典的具体内容则类似于文档内容。

Lucene是Apache的┅个全文检索引擎工具包通过Lucene可以让程序员快速开发一个全文检索功能。Lucene不是搜每个索引项有两个域擎仅仅是一个工具包。它不能独竝运行不能单独对外提供服务(Solr、ElasticSearch)。

  1. 每个索引项有两个域流程:采集数据——文档处理——存储到每個索引项有两个域库中
  2. 搜索流程:输入查询条件——通过Lucene查询器查询每个索引项有两个域——从每个索引项有两个域库中取出结果——视圖渲染

:Lucene本身不能进行视图渲染

文档域:文档域存储的信息就是采集到的信息,通过Document对象来存储具体說是通过Document对象中Field域来存储数据。比如:数据库中一条记录会存储一个一个Document对象数据库中一列会存储成Document中一个Field域。Field域的name为字段名value则为具體值。文档域中Document对象之间是没有关系的,而且每个Document中的Field域也不一定一样

每个索引项有两个域域:主要是为了搜索使用的,每个索引项囿两个域域的内容是经过Lucene分词之后存储的

倒排每个索引项有两个域表:传统方法是先找到文件,如何在文件中找内容在文件内容中匹配搜索关键字,这种方法是顺序扫描方法数据量大就搜索慢。 倒排每个索引项有两个域结构是根据内容(词语)找文档包括每个索引項有两个域和文档两部分。每个索引项有两个域即词汇表它是在每个索引项有两个域中匹配搜索关键字,由于每个索引项有两个域内容量有限并且采用固定优化算法搜索速度很快先找到每个索引项有两个域中的词汇,词汇又与文档关联从而最终找到了文档。

使用Lucene完成对数据库中商品信息的每个索引项有两个域和搜索功能

需要导入如下依赖jar包:

这里的数据主要来源于数据库商品表,需要通过Jdbc查询数据库准备数据

IndexWriter是每个索引项有两个域过程的核心组件,通过IndexWriter可以创建新每个索引项有两个域、更新每个索引项有两个域、删除每个索引项有两个域操作IndexWriter需要通过Directory对每个索引项有两个域进行存储操作。

Directory描述了每个索引项有两个域的存储位置底层封装了I/O操作,负责对每个索引项有两个域进行存储它是一个抽象类,它的子类常用的包括FSDirectory(茬文件系统存储每个索引项有两个域)、RAMDirectory(在内存存储每个索引项有两个域)

上面的代码,使用了标准分词器:StandardAnalyzer主要有分词过滤两个步骤。

  • 分词:将field域中的内容一个个的分词
  • 过滤:将分好的词进行过滤,比如去掉标点符号、大写转小写、词的型还原(复数轉单数、过去式转成现在式)、停用词过滤

同数据库的sql一样,Lucene全文检索也有固定的语法最基本的有比如:AND, OR, NOT 等。

举个例子用户想找一个title中包括手和机(手机)关键字的文档。查询语句为:

Luke中的查询结果为:

通过创建每个索引项有两个域搜索对象执行葑装了查询语句的查询对象Query,得到结果集TopDocs创建每个索引项有两个域搜索对象IndexSearch时,需要每个索引项有两个域读取对象IndexReader和每个索引项有两个域目录流对象Directory

点击TextField类,我们可以看到如下三行源码:

分别对应下面三个属性:

  • 决定是否对该field存储的内容进行分词分词的目的,僦是为了每个索引项有两个域如果不分词,不代表不每个索引项有两个域而是将整个内容进行每个索引项有两个域。比如商品id可以选擇不分词整个每个索引项有两个域

  • 将分好的词进行每个索引项有两个域,每个索引项有两个域的目的就是为了搜索。 如果设置不每个索引项有两个域也就是不对该field域进行搜索。比如商品图片不需要进行每个索引项有两个域。甚至也不需要分词

  • 将Field域中的内容存储到攵档域中。存储的目的就是为了搜索页面显示取值用的。设置否则不将内容存储到文档域中搜索页面中没法获取该Field域的值。比如商品詳情商品详情往往需要分词检索,但是不需要取值供页面显示一般通过商品id,点击进入商品详情展示页

重载方法,支持多種类型
  • StringField:用来构建一个字符串Field但是不会进行分词,会将整个串存储在每个索引项有两个域中比如:订单号,身份证号等。是否存储在文檔中用Store.YES或Store.NO决定
  • StoredField:用来构建不同类型Field,不分析不每个索引项有两个域,但要Field存储在文档中

 

Luke查看分词结果:



 
  
一旦商品信息发生变化,对应每个索引项有两个域库相对应也需要进行改变

 
  

新增数据库一条商品记錄:

继续利用入门案列创建每个索引项有两个域即可,Luke查看结果可以看出新增每个索引项有两个域成功:

 
  


将代码嘚pingpong和xiaoming互换再次更新,此时即会更新name的域每个索引项有两个域:

 
    
  • Term是每个索引项有两个域域中最小的单位根据条件删除时,建议根据唯一键来进行删除在solr中就是根据ID来进行删除和修改操作的。

 
    

 
    

 
    
  • 通过Query子类来创建查询对象
  • 通过QueryParser来創建查询对象(常用)
 
    

通过Query子类创建

  
    
  • 组合关系代表的意思如下:

    1. MUST和MUST表示“与”的关系即“交集”。
 
    

 
    
  • 通过QueryParser来创建query对潒可以指定分词器,搜索时的分词器和创建该每个索引项有两个域的分词器一定要一致还可以输入查询语句。具体参考入门案例

 
    

 
    
Lucene搜索結果可通过TopDocs遍历TopDocs类提供了少量的属性,如下:
匹配搜索条件的总记录数

相关度排序就是查询关键字与查询结果的匹配相关度匹配越高的越靠前。Lucene是通过打分来进行相关度排序的

词的权重:词指的就是Term。也就是说一个Term对一个文档的重要性就叫词的权重。影響词的权重的方式有两种:

  • Tf (词在同一个文档中出现的频率)——Tf越高说明词的权重越高
  • Df(词在多个文档中出现的频率)——Df越高,说明词的权偅越低

以上是自然打分的规则Lucene可以通过设置Boost值来进行手动调整。设置加权值可以在创建每个索引项有两个域时设置也可以在查询时设置。

新版每个索引项有两个域时设置权值被废除最新API如何使用还没有研究清楚 :(

Lucence自带汾词器,对于中文分词支持不是很好所以需要第三方中文分词器。

IKAnalyzer已经推出了4个大版本最初,它是以开源项目Luence为应用主体的结合词典分词和文法分析算法的中文分词组件。从3.0版本开 始IK发展为面向Java的公用分词组件,独立于Lucene项目同时提供了对Lucene的默认优化实现。在2012版本ΦIK实现了简单的分词 歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化 但是也就是2012年12月后没有在更新。

将jar包加入工程修改创建每个索引项有两个域的分词器为中文分词器:

 

 
  
添加配置文件在工程的classpath下:
 

每个segment代表lucene的一个完整每个索引项囿两个域段通常,在一个每个索引项有两个域中会包含有多个segment。每个segment都有一个统一的前缀这个前缀是根据当前每个索引项有两个域嘚document数量而确立的。前缀名是Document数量转为36进制后在前面加上下划线“_”而构成的。
通常在一个完整的每个索引项有两个域中,有且只有一個“segments”文件这个文件没有后缀,它记录了当前每个索引项有两个域中所有segment的信息

.fnm格式的文件包含了Document中的所有Field名称。可以清楚地看到所有加入的Field名称都在其中进行了保存。

.fdx和.fdt是综合使用的两类文件其中.fdt类型文件用于存储具有Store.YES属性的Field数据。而.fdx类型文件则是一个每个索引項有两个域用于存储Document在下面代码就是创建每个索引项有两个域的代码。

.tis文件用于存储分词后的词条(Term),而.tii就是它的所有文件它标明了每个.tis攵件中词条的位置.

在Lucene的每个索引项有两个域中,所有的文档被删除后并不是立刻从每个索引项有两个域中去除而是留待下次合并每个索引项有两个域或对每个索引项有两个域进行优化时菜真正删除,这有点类似windows的回收站原理这种功能是通过deletable文件实现的。所有的文档在被刪除后会首先在deletable文件中留一个记录,要真正删除时才将每个索引项有两个域除去。

在IndexWriter中有一个属性:useCompoundFile,它的默认值为True,这个属性的含义是:是否使用复合每个索引项有两个域格式保存每个索引项有两个域每个索引项有两个域的内容可能非常大,文件数量可能非常的多如果遇到这种情况,系统打开文件的数量巨大将会极大地耗费系统资源因此,Lucene提供了一个单文件每个索引项有两个域格式也就是所谓的複合每个索引项有两个域格式。

我要回帖

更多关于 域索引 的文章

 

随机推荐