﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-gaich-随笔分类-J2EE应用</title><link>http://www2.blogjava.net/gaich/category/1917.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 19:28:38 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 19:28:38 GMT</pubDate><ttl>60</ttl><item><title>lucene结构中文说明</title><link>http://www.blogjava.net/gaich/archive/2005/06/30/6933.html</link><dc:creator>gaich</dc:creator><author>gaich</author><pubDate>Thu, 30 Jun 2005 03:52:00 GMT</pubDate><guid>http://www.blogjava.net/gaich/archive/2005/06/30/6933.html</guid><wfw:comment>http://www.blogjava.net/gaich/comments/6933.html</wfw:comment><comments>http://www.blogjava.net/gaich/archive/2005/06/30/6933.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gaich/comments/commentRss/6933.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gaich/services/trackbacks/6933.html</trackback:ping><description><![CDATA[<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">原文:<A href="http://www.matrix.org.cn/resource/article/34.html">http://www.matrix.org.cn/resource/article/34.html</A><BR><BR>论坛:<A href="http://www.matrix.org.cn/topic.shtml?forumId=32">http://www.matrix.org.cn/topic.shtml?forumId=32</A><BR>索引文件格式</SPAN><SPAN lang=EN-US> <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">本文定义了</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（版本</SPAN><SPAN lang=EN-US>1.3</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）用到的索引文件的格式。</SPAN><SPAN lang=EN-US><BR><BR>Jakarta Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是用</SPAN><SPAN lang=EN-US>Java</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">写成的，同时有很多团体正在默默的用其他的程序语言来改写它。如果这些新的版本想和</SPAN><SPAN lang=EN-US>Jakarta Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">兼容，就需要一个与具体语言无关的</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引文件格式。本文正是试图提供一个完整的与语言无关的</SPAN><SPAN lang=EN-US>Jakarta Lucene 1.3</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引文件格式的规格定义。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">随着</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不断发展，本文也应该更新。不同语言写成的</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现版本应当尽力遵守文件格式，也必须产生本文的新版本。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">本文同时提供兼容性批注，描述文件格式上与前一版本不同的地方。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">定义</SPAN><SPAN lang=EN-US> <BR><BR>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中最基础的概念是索引（</SPAN><SPAN lang=EN-US>index</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），文档（</SPAN><SPAN lang=EN-US>document</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），域（</SPAN><SPAN lang=EN-US>field</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和项（</SPAN><SPAN lang=EN-US>term</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引包含了一个文档的序列。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文档是一些域的序列。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域是一些项的序列。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项就是一个字串。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">存在于不同域中的同一个字串被认为是不同的项。因此项实际是用一对字串表示的，第一个字串是域名，第二个是域中的字串。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">倒排索引</SPAN><SPAN lang=EN-US> <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为了使得基于项的搜索更有效率，索引中项是静态存储的。</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的索引属于索引方式中的倒排索引，因为对于一个项这种索引可以列出包含它的文档。这刚好是文档与项自然联系的倒置。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域的类型</SPAN><SPAN lang=EN-US> <BR><BR>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中，域的文本可能以逐字的非倒排的方式存储在索引中。而倒排过的域称为被索引过了。域也可能同时被存储和被索引。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域的文本可能被分解许多项目而被索引，或者就被用作一个项目而被索引。大多数的域是被分解过的，但是有些时候某些标识符域被当做一个项目索引是很有用的。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">段（</SPAN><SPAN lang=EN-US>Segment</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US> <BR><BR>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引可能由多个子索引组成，这些子索引成为段。每一段都是完整独立的索引，能被搜索。索引是这样作成的：</SPAN><SPAN lang=EN-US><BR><BR>1. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为新加入的文档创建新段。</SPAN><SPAN lang=EN-US><BR><BR>2. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">合并已经存在的段。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">搜索时需要涉及到多个段和</SPAN><SPAN lang=EN-US>/</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或者多个索引，每一个索引又可能由一些段组成。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文档号（</SPAN><SPAN lang=EN-US>Document Number</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US> <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">内部的来说，</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">用一个整形（</SPAN><SPAN lang=EN-US>interger</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）的文档号来指示文档。第一个被加入到索引中的文档就是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">号，顺序加入的文档将得到一个由前一个号码递增而来的号码。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">注意文档号是可能改变的，所以在</SPAN><SPAN lang=EN-US>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">外部存储这些号码时必须小心。特别的，号码的改变的情况如下：</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">只有段内的号码是相同的，不同段之间不同，因而在一个比段广泛的上下文环境中使用这些号码时，就必须改变它们。标准的技术是根据每一段号码多少为每一段分配一个段号。将段内文档号转换到段外时，加上段号。将某段外的文档号转换到段内时，根据每段中可能的转换后号码范围来判断文档属于那一段，并减调这一段的段号。例如有两个含</SPAN><SPAN lang=EN-US>5</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个文档的段合并，那么第一段的段号就是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，第二段段号</SPAN><SPAN lang=EN-US>5</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。第二段中的第三个文档，在段外的号码就是</SPAN><SPAN lang=EN-US>8</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文档删除后，连续的号码就出现了间断。这可以通过合并索引来解决，段合并时删除的文档相应也删掉了，新合并而成的段并没有号码间断。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">绪论</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引段维护着以下的信息：</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域集合。包含了索引中用到的所有的域。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域值存储表。每一个文档都含有一个</SPAN><SPAN lang=EN-US>“</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">属性－值</SPAN><SPAN lang=EN-US>”</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对的列表，属性即为域名。这个列表用来存储文档的一些附加信息，如标题，</SPAN><SPAN lang=EN-US>url</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或者访问数据库的一个</SPAN><SPAN lang=EN-US>ID</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。在搜索时存储域的集合可以被返回。这个表以文档号标识。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项字典。这个字典含有所有文档的所有域中使用过的的项，同时含有使用过它的文档的文档号，以及指向使用频数信息和位置信息的指针。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项频数信息。对于项字典中的每个项，这些信息包含含有这个项的文档的总数，以及每个文档中使用的次数。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项位置信息。对于项字典中的每个项，都存有在每个文档中出现的各个位置。</SPAN><SPAN lang=EN-US><BR><BR>· Normalization factors. For each field in each document, a value is stored that is multiplied into the score for hits on that field. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">标准化因子。对于文档中的每一个域，存有一个值，用来以后乘以这个这个域的命中数（</SPAN><SPAN lang=EN-US>hits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">被删除的文档信息。这是一个可选文件，用来表明那些文档已经删除了。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接下来的各部分部分详细描述这些信息。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件的命名（</SPAN><SPAN lang=EN-US>File Naming</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">同属于一个段的文件拥有相同的文件名，不同的扩展名。扩展名由以下讨论的各种文件格式确定。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">一般来说，一个索引存放一个目录，其所有段都存放在这个目录里，尽管我们不要求您这样做。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">基本数据类型（</SPAN><SPAN lang=EN-US>Primitive Types</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR>Byte <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最基本的数据类型就是字节（</SPAN><SPAN lang=EN-US>byte</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</SPAN><SPAN lang=EN-US>8</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位）。文件就是按字节顺序访问的。其它的一些数据类型也定义为字节的序列，文件的格式具有字节意义上的独立性。</SPAN><SPAN lang=EN-US><BR><BR>UInt32 <BR><BR>32</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位无符号整数，由四个字节组成，高位优先。</SPAN><SPAN lang=EN-US><BR><BR>UInt32 --&gt; &lt;Byte&gt;4 <BR><BR>Uint64 <BR><BR>64</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位无符号整数，由八字节组成，高位优先。</SPAN><SPAN lang=EN-US><BR><BR>UInt64 --&gt; &lt;Byte&gt;8 <BR><BR>VInt <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可变长的正整数类型，每字节的最高位表明还剩多少字节。每字节的低七位表明整数的值。因此单字节的值从</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">到</SPAN><SPAN lang=EN-US>127</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，两字节值从</SPAN><SPAN lang=EN-US>128</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">到</SPAN><SPAN lang=EN-US>16,383</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，等等。</SPAN><SPAN lang=EN-US><BR><BR>VInt </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">编码示例</SPAN><SPAN lang=EN-US><BR><BR>Value <BR>First byte <BR>Second byte <BR>Third byte <BR><BR>0 <BR>00000000 <BR><BR><BR><BR>1 <BR>00000001 <BR><BR><BR><BR>2 <BR>00000010 <BR><BR><BR><BR>... <BR><BR><BR><BR><BR>127 <BR>01111111 <BR><BR><BR><BR>128 <BR>10000000 <BR>00000001 <BR><BR><BR>129 <BR>10000001 <BR>00000001 <BR><BR><BR>130 <BR>10000010 <BR>00000001 <BR><BR><BR>... <BR><BR><BR><BR><BR>16,383 <BR>11111111 <BR>01111111 <BR><BR><BR>16,384 <BR>10000000 <BR>10000000 <BR>00000001 <BR><BR>16,385 <BR>10000001 <BR>10000000 <BR>00000001 <BR><BR>... <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这种编码提供了一种在高效率解码时压缩数据的方法。</SPAN><SPAN lang=EN-US><BR><BR>Chars <BR><BR>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">输出</SPAN><SPAN lang=EN-US>UNICODE</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">字符序列，使用标准</SPAN><SPAN lang=EN-US>UTF-8</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">编码。</SPAN><SPAN lang=EN-US><BR><BR>String <BR><BR>Lucene</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">输出由</SPAN><SPAN lang=EN-US>VINT</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和字符串组成的字串，</SPAN><SPAN lang=EN-US>VINT</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示字串长，字符串紧接其后。</SPAN><SPAN lang=EN-US><BR><BR>String --&gt; VInt, Chars <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引包含的文件（</SPAN><SPAN lang=EN-US>Per-Index Files</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这部分介绍每个索引包含的文件。</SPAN><SPAN lang=EN-US><BR><BR>Segments</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件</SPAN><SPAN lang=EN-US> <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">索引中活动的段存储在</SPAN><SPAN lang=EN-US>Segments</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中。每个索引只能含有一个这样的文件，名为</SPAN><SPAN lang=EN-US>"segments".</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这个文件依次列出每个段的名字和每个段的大小。</SPAN><SPAN lang=EN-US><BR><BR>Segments --&gt; SegCount, &lt;SegName, SegSize&gt;SegCount <BR><BR>SegCount, SegSize --&gt; UInt32 <BR><BR>SegName --&gt; String <BR><BR>SegName</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示该</SPAN><SPAN lang=EN-US>segment</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的名字，同时作为索引其他文件的前缀。</SPAN><SPAN lang=EN-US><BR><BR>SegSize</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是段索引中含有的文档数。</SPAN><SPAN lang=EN-US><BR><BR>Lock</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">有一些文件用来表示另一个进程在使用索引。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果存在</SPAN><SPAN lang=EN-US>"commit.lock"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件，表示有进程在写</SPAN><SPAN lang=EN-US>"segments"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件和删除无用的段索引文件，或者表示有进程在读</SPAN><SPAN lang=EN-US>"segments"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件和打开某些段的文件。在一个进程在读取</SPAN><SPAN lang=EN-US>"segments"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件段信息后，还没来得及打开所有该段的文件前，这个</SPAN><SPAN lang=EN-US>Lock</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件可以防止另一个进程删除这些文件。</SPAN><SPAN lang=EN-US><BR><BR>· </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果存在</SPAN><SPAN lang=EN-US>"index.lock"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件，表示有进程在向索引中加入文档，或者是从索引中删除文档。这个文件防止很多文件同时修改一个索引。</SPAN><SPAN lang=EN-US><BR><BR>Deleteable</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">名为</SPAN><SPAN lang=EN-US>"deletetable"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的文件包含了索引不再使用的文件的名字，这些文件可能并没有被实际的删除。这种情况只存在与</SPAN><SPAN lang=EN-US>Win32</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">平台下，因为</SPAN><SPAN lang=EN-US>Win32</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下文件仍打开时并不能删除。</SPAN><SPAN lang=EN-US><BR><BR>Deleteable --&gt; DelableCount, &lt;DelableName&gt;DelableCount <BR><BR>DelableCount --&gt; UInt32 <BR><BR>DelableName --&gt; String <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">段包含的文件（</SPAN><SPAN lang=EN-US>Per-Segment Files</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">剩下的文件是每段中包含的文件，因此由后缀来区分。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域（</SPAN><SPAN lang=EN-US>Field</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域集合信息（</SPAN><SPAN lang=EN-US>Field Info</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US> <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">所有域名都存储在这个文件的域集合信息中，这个文件以后缀</SPAN><SPAN lang=EN-US>.fnm</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">结尾。</SPAN><SPAN lang=EN-US><BR><BR>FieldInfos (.fnm) --&gt; FieldsCount, &lt;FieldName, FieldBits&gt;FieldsCount <BR><BR>FieldsCount --&gt; VInt <BR><BR>FieldName --&gt; String <BR><BR>FieldBits --&gt; Byte <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">目前情况下，</SPAN><SPAN lang=EN-US>FieldBits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">只有使用低位，对于已索引的域值为</SPAN><SPAN lang=EN-US>1</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，对未索引的域值为</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的域根据它们的次序编号。因此域</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是文件中的第一个域，域</SPAN><SPAN lang=EN-US>1</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是接下来的，等等。这个和文档号的编号方式相同。</SPAN><SPAN lang=EN-US> <BR><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域值存储表（</SPAN><SPAN lang=EN-US>Stored Fields</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域值存储表使用两个文件表示：</SPAN><SPAN lang=EN-US><BR><BR>1. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域索引（</SPAN><SPAN lang=EN-US>.fdx</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如下，对于每个文档这个文件包含指向域值的指针：</SPAN><SPAN lang=EN-US><BR><BR>FieldIndex (.fdx) --&gt; &lt;FieldValuesPosition&gt;SegSize <BR><BR>FieldValuesPosition --&gt; Uint64 <BR><BR>FieldValuesPosition</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指示的是某一文档的某域的域值在域值文件中的位置。因为域值文件含有定长的数据信息，因而很容易随机访问。在域值文件中，文档</SPAN><SPAN lang=EN-US>n</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的域值信息就存在</SPAN><SPAN lang=EN-US>n*8</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位置处（</SPAN><SPAN lang=EN-US>The position of document n's field data is the Uint64 at n*8 in this file.</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）。</SPAN><SPAN lang=EN-US><BR><BR>2. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">域值（</SPAN><SPAN lang=EN-US>.fdt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如下，每个文档的域值信息包含：</SPAN><SPAN lang=EN-US><BR><BR>FieldData (.fdt) --&gt; &lt;DocFieldData&gt;SegSize <BR><BR>DocFieldData --&gt; FieldCount, &lt;FieldNum, Bits, Value&gt;FieldCount <BR><BR>FieldCount --&gt; VInt <BR><BR>FieldNum --&gt; VInt <BR><BR>Bits --&gt; Byte <BR><BR>Value --&gt; String <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">目前情况下，</SPAN><SPAN lang=EN-US>Bits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">只有低位被使用，值为</SPAN><SPAN lang=EN-US>1</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示域名被分解过，值为</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示未分解过。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项字典（</SPAN><SPAN lang=EN-US>Term Dictionary</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项字典用以下两个文件表示：</SPAN><SPAN lang=EN-US><BR><BR>1. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项信息（</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件）。</SPAN><SPAN lang=EN-US><BR><BR>TermInfoFile (.tis)--&gt; TermCount, TermInfos <BR><BR>TermCount --&gt; UInt32 <BR><BR>TermInfos --&gt; &lt;TermInfo&gt;TermCount <BR><BR>TermInfo --&gt; &lt;Term, DocFreq, FreqDelta, ProxDelta&gt; <BR><BR>Term --&gt; &lt;PrefixLength, Suffix, FieldNum&gt; <BR><BR>Suffix --&gt; String <BR><BR>PrefixLength, DocFreq, FreqDelta, ProxDelta<BR>--&gt; VInt <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项信息按项排序。项信息排序时先按项所属的域的文字顺序排序，然后按照项的字串的文字顺序排序。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项的字前缀往往是共同的，与字的后缀组成字。</SPAN><SPAN lang=EN-US>PrefixLength</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变量就是表示与前一项相同的前缀的字数。因此，如果前一个项的字是</SPAN><SPAN lang=EN-US>"bone"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，后一个是</SPAN><SPAN lang=EN-US>"boy"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的话，</SPAN><SPAN lang=EN-US>PrefixLength</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">值为</SPAN><SPAN lang=EN-US>2</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</SPAN><SPAN lang=EN-US>Suffix</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">值为</SPAN><SPAN lang=EN-US>"y"</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</SPAN><SPAN lang=EN-US><BR><BR>FieldNum</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指明了项属于的域号，而域名存储在</SPAN><SPAN lang=EN-US>.fdt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中。</SPAN><SPAN lang=EN-US><BR><BR>DocFreg</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示的是含有该项的文档的数量。</SPAN><SPAN lang=EN-US><BR><BR>FreqDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指明了项所属</SPAN><SPAN lang=EN-US>TermFreq</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变量在</SPAN><SPAN lang=EN-US>.frq</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的位置。详细的说，就是指相对于前一个项的数据的位置偏移量（或者是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，表示文件中第一个项）。</SPAN><SPAN lang=EN-US><BR><BR>ProxDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指明了项所属的</SPAN><SPAN lang=EN-US>TermPosition</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变量在</SPAN><SPAN lang=EN-US>.prx</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的位置。详细的说，就是指相对于前一个项的数据的位置偏移量（或者是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，表示文件中第一个项）。</SPAN><SPAN lang=EN-US><BR><BR>2. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项信息索引（</SPAN><SPAN lang=EN-US>.tii</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">每个项信息索引文件包含</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的</SPAN><SPAN lang=EN-US>128</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个条目，依照条目在</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的顺序。这样设计是为了一次将索引信息读入内存能，然后使用它来随机的访问</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这个文件的结构和</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件非常类似，只在每个条目记录上增加了一个变量</SPAN><SPAN lang=EN-US>IndexDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</SPAN><SPAN lang=EN-US><BR><BR>TermInfoIndex (.tii)--&gt; IndexTermCount, TermIndices <BR><BR>IndexTermCount --&gt; UInt32 <BR><BR>TermIndices --&gt; &lt;TermInfo, IndexDelta&gt;IndexTermCount <BR><BR>IndexDelta --&gt; VInt <BR><BR>IndexDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示该项的</SPAN><SPAN lang=EN-US>TermInfo</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变量值在</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的位置。详细的讲，就是指相对于前一个条目的偏移量（或者是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，对于文件中第一个项）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项频数（</SPAN><SPAN lang=EN-US>Frequencies</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US> <BR><BR>.frq</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件包含每一项的文档的列表，还有该项在对应文档中出现的频数。</SPAN><SPAN lang=EN-US><BR><BR>FreqFile (.frq) --&gt; &lt;TermFreqs&gt;TermCount <BR><BR>TermFreqs --&gt; &lt;TermFreq&gt;DocFreq <BR><BR>TermFreq --&gt; DocDelta, Freq? <BR><BR>DocDelta,Freq --&gt; VInt <BR><BR>TermFreqs</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">序列按照项来排序（依据于</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的项，即项是隐含存在的）。</SPAN><SPAN lang=EN-US><BR><BR>TermFreq</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">元组按照文档号升序排列。</SPAN><SPAN lang=EN-US><BR><BR>DocDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">决定了文档号和频数。详细的说，</SPAN><SPAN lang=EN-US>DocDelta/2</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示相对于前一文档号的偏移量（或者是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，表示这是</SPAN><SPAN lang=EN-US>TermFreqs</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">里面的第一项）。当</SPAN><SPAN lang=EN-US>DocDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是奇数时表示在该文档中频数为</SPAN><SPAN lang=EN-US>1</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，当</SPAN><SPAN lang=EN-US>DocDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是偶数时，另一个</SPAN><SPAN lang=EN-US>VInt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US>Freq</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）就表示在该文档中出现的频数。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">例如，假设某一项在文档</SPAN><SPAN lang=EN-US>7</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中出现一次，在文档</SPAN><SPAN lang=EN-US>11</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中出现了</SPAN><SPAN lang=EN-US>3</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">次，在</SPAN><SPAN lang=EN-US>TermFreqs</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中就存在如下的</SPAN><SPAN lang=EN-US>VInts</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">序列：</SPAN><SPAN lang=EN-US><BR><BR>15, 22, 3 <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项位置（</SPAN><SPAN lang=EN-US>Position</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR>.prx</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件包含了某文档中某项出现的位置信息的列表。</SPAN><SPAN lang=EN-US><BR><BR>ProxFile (.prx) --&gt; &lt;TermPositions&gt;TermCount <BR><BR>TermPositions --&gt; &lt;Positions&gt;DocFreq <BR><BR>Positions --&gt; &lt;PositionDelta&gt;Freq <BR><BR>PositionDelta --&gt; VInt <BR><BR>TermPositions</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">按照项来排序（依据于</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中的项，即项是隐含存在的）。</SPAN><SPAN lang=EN-US><BR><BR>Positions</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">元组按照文档号升序排列。</SPAN><SPAN lang=EN-US><BR><BR>PositionDelta</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是相对于前一个出现位置的偏移位置（或者为</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，表示这是第一次在这个文档中出现）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">例如，假设某一项在某文档第</SPAN><SPAN lang=EN-US>4</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项出现，在另一个文档中第</SPAN><SPAN lang=EN-US>5</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项和第</SPAN><SPAN lang=EN-US>9</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项出现，将存在如下的</SPAN><SPAN lang=EN-US>VInt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">序列：</SPAN><SPAN lang=EN-US><BR><BR>4, 5, 4 <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">标准化因子（</SPAN><SPAN lang=EN-US>Normalization Factor</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US> <BR><BR>.nrm</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件包含了每个文档的标准化因子，标准化因子用来以后乘以这个这个域的命中数。</SPAN><SPAN lang=EN-US><BR><BR>Norms (.nrm) --&gt; &lt;Byte&gt;SegSize <BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">每个字节记录一个浮点数。位</SPAN><SPAN lang=EN-US>0-2</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包含了</SPAN><SPAN lang=EN-US>3</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位的尾数部分，位</SPAN><SPAN lang=EN-US>3-8</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包含了</SPAN><SPAN lang=EN-US>5</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位的指数部分。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">按如下规则可将这些字节转换为</SPAN><SPAN lang=EN-US>IEEE</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">标准单精度浮点数：</SPAN><SPAN lang=EN-US><BR><BR>1. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果该字节是</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，就是浮点</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">；</SPAN><SPAN lang=EN-US><BR><BR>2. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">否则，设置新浮点数的标志位为</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">；</SPAN><SPAN lang=EN-US><BR><BR>3. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">将字节中的指数加上</SPAN><SPAN lang=EN-US>48</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">后作为新的浮点数的指数；</SPAN><SPAN lang=EN-US><BR><BR>4. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">将字节中的尾数映射到新浮点数尾数的高</SPAN><SPAN lang=EN-US>3</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位；并且</SPAN><SPAN lang=EN-US><BR><BR>5. </SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">设置新浮点数尾数的低</SPAN><SPAN lang=EN-US>21</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位为</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">被删除的文档（</SPAN><SPAN lang=EN-US>Deleted Document</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR>.del</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件是可选的，只有在某段中存在删除操作后才存在：</SPAN><SPAN lang=EN-US><BR><BR>Deletions (.del) --&gt; ByteCount,BitCount,Bits <BR><BR>ByteSize,BitCount --&gt; Uint32 <BR><BR>Bits --&gt; &lt;Byte&gt;ByteCount <BR><BR>ByteCount</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示的是</SPAN><SPAN lang=EN-US>Bits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">列表中</SPAN><SPAN lang=EN-US>Byte</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的数量。典型的，它等于（</SPAN><SPAN lang=EN-US>SegSize/8</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US>+1</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</SPAN><SPAN lang=EN-US><BR><BR>BitCount</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表示</SPAN><SPAN lang=EN-US>Bits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">列表中多少个已经被设置过了。</SPAN><SPAN lang=EN-US><BR><BR>Bits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">列表包含了一些位（</SPAN><SPAN lang=EN-US>bit</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），顺序表示一个文档。当对应于文档号的位被设置了，就标志着这个文档已经被删除了。位的顺序是从低到高。因此，如果</SPAN><SPAN lang=EN-US>Bits</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包含两个字节，</SPAN><SPAN lang=EN-US>0x00</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</SPAN><SPAN lang=EN-US>0x02</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，那么表示文档</SPAN><SPAN lang=EN-US>9</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">已经删除了。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">局限性（</SPAN><SPAN lang=EN-US>Limitations</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在以上的文件格式中，好几处都有限制项和文档的最大个数为</SPAN><SPAN lang=EN-US>32</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位数的极限，即接近于</SPAN><SPAN lang=EN-US>40</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">亿。今天看来，这不会造成问题，但是，长远的看，可能造成问题。因此，这些极限应该或者换为</SPAN><SPAN lang=EN-US>UInt64</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型的值，或者更好的，换为</SPAN><SPAN lang=EN-US>VInt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型的值（</SPAN><SPAN lang=EN-US>VInt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">值没有上限）。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">有两处地方的代码要求必须是定长的值，他们是：</SPAN><SPAN lang=EN-US><BR><BR>1. FieldValuesPosition</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变量（存储于域索引文件中，</SPAN><SPAN lang=EN-US>.fdx</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件）。它已经是一个</SPAN><SPAN lang=EN-US>UInt64</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">型，所以不会有问题。</SPAN><SPAN lang=EN-US><BR><BR>2. TermCount</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">变量（存储于项信息文件中，</SPAN><SPAN lang=EN-US>.tis</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件）。这是最后输出到文件中的，但是最先被读取，因此是存储于文件的最前端</SPAN> <SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。索引代码先在这里写入一个</SPAN><SPAN lang=EN-US>0</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">值，然后在其他文件输出完毕后覆盖这个值。所以无论它存储在什么地方，它都必须是一个定长的值，它应该被变成</SPAN><SPAN lang=EN-US>UInt64</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">型。</SPAN><SPAN lang=EN-US><BR><BR></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">除此之外，所有的</SPAN><SPAN lang=EN-US>UInt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">值都可以换成</SPAN><SPAN lang=EN-US>VInt</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">型以去掉限制。</SPAN></P><img src ="http://www.blogjava.net/gaich/aggbug/6933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gaich/" target="_blank">gaich</a> 2005-06-30 11:52 <a href="http://www.blogjava.net/gaich/archive/2005/06/30/6933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>竹笋炒肉 --- Lucene学习笔记</title><link>http://www.blogjava.net/gaich/archive/2005/06/30/6931.html</link><dc:creator>gaich</dc:creator><author>gaich</author><pubDate>Thu, 30 Jun 2005 03:33:00 GMT</pubDate><guid>http://www.blogjava.net/gaich/archive/2005/06/30/6931.html</guid><wfw:comment>http://www.blogjava.net/gaich/comments/6931.html</wfw:comment><comments>http://www.blogjava.net/gaich/archive/2005/06/30/6931.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gaich/comments/commentRss/6931.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gaich/services/trackbacks/6931.html</trackback:ping><description><![CDATA[原文:<A href="http://hedong.3322.org/archives/000206.html">http://hedong.3322.org/archives/000206.html</A><BR><BR>Lucene是一套全文检索的API，对其介绍的文章和应用的案例都多，可参考<A title=我的一篇lucene备忘 href="http://www.infomall.cn/cgi-bin/mallgate/20031008/http://hedong.3322.org/archives/000131.html">lucene</A>及本文的参考文献。<BR>　　此次学习，以实用为主，一是简单应用，二是Web应用，三是汉化，四相关应用（Lucene主页上在SandBox中）。<A name=more></A>
<P>0、准备工作<BR>　　去<A title="lucene is a apache project" href="http://www.infomall.cn/cgi-bin/mallgate/20031008/http://jakarta.apache.org/lucene/">Lucene的主页</A>下载目前的稳定版本lucene-1.2.tar.gz，解压缩，将lucene-1.2目录下的两个jar文件lucene-1.2.jar和lucene-demo2-1.2.jar放到适当的目录下后，并将其加入到CLASSPATH环境变量中。 
<DIV class=code>tar zxvf lucene-1.2.tar.gz &lt;----解压缩<BR>cd lucene-1.2<BR>cp *.jar $DP<INS>&lt;---存放jar文件的目录，根据具体工作要求用实际目录替换</INS><BR>CLASSPATH=$CLASSPATH:$DP/lucene-1.2.jar:$DP/lucene-demos-1.2.jar; export CLASSPATH</DIV><A href="http://www.infomall.cn/cgi-bin/mallgate/20031008/http://hedong.3322.org/archives/pics/winclasspath.gif"><IMG height=414 alt=winclasspath.gif src="http://hedong.3322.org/archives/pics/winclasspath-thumb.gif" width=300 align=right border=0></A>如果不想在每次都登录后，可以编辑/etc/profile或自己目录下的.profile，将上面的最后一行加到文件的最后一行。Windows的设置，右击桌面的“我的电脑”，选“高级”－＞“环境变量”－＞选中CLASSPATH－＞“编辑”，在输入框中加入两个jar文件的全路径名，注意分隔符是分号(;)。参见右图。 
<P></P>
<P>1、运行demo<BR>
<DIV class=code>$ java org.apache.lucene.demo.IndexFiles /usr/local/man/man1/ <INS>&lt;--对man文件建立索引</INS><BR>adding /usr/local/man/man1/mysql.1<BR>...........<BR>adding /usr/local/man/man1/cvs.1<BR>1614 total milliseconds<BR>$ java org.apache.lucene.demo.SearchFiles <INS>&lt;--进行检索</INS><BR>Query: password<BR>Searching for: password<BR>7 total matching documents<BR>0. /usr/local/man/man1/mysql.1<BR>......<BR>6. /usr/local/man/man1/mysqlshow.1<BR>Query: </DIV>OK!Lucene自待的demo运行成功<BR>这个demo程序调用的主要API函数: 
<DIV class=code>/*关于索引的主要函数*/<BR>File file=new File(argv[]);<BR>IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true); 
<P></P>
<P>Document doc = new Document();<BR>doc.add(Field.Text("path", file.getPath()));<BR>doc.add(Field.Keyword("modified",DateField.timeToString(file.lastModified())));<BR>FileInputStream is = new FileInputStream(f);<BR>Reader reader = new BufferedReader(new InputStreamReader(is));<BR>doc.add(Field.Text("contents", reader));</P>
<P>writer.addDocument(doc);</P>
<P>writer.optimize();<BR>writer.close();</P>
<P>/*关于检索的主要函数*/<BR>Searcher searcher = new IndexSearcher("index");<BR>Analyzer analyzer = new StandardAnalyzer();<BR>Query query = QueryParser.parse(lineforsearch, "contents", analyzer);<BR>Hits hits = searcher.search(query);<BR>for (int i = start; i &lt; hits.length(); i++) {<BR>Document doc = hits.doc(i);<BR>String path = doc.get("path");<BR>System.out.println(i + ". " + path);<BR>}</P></DIV>
<P></P>
<P><BR>3、运行LuceneWeb<BR>　　假定tomcat装在$TOMCATHOME目录下，具体应用时用真实的目录替换$TOMCATHOME。 
<DIV class=code>cd $TOMCATHOME/webapps<BR>mkdir lucenedb<BR>cd lucenedb<BR>java org.apache.lucene.demo.IndexHTML -create -index $TOMCAT/webapps/lucenedb ../examples<INS>&lt;--用相对路径“..”，一来指明被索引的文件的位置，二来用来显示被索引文件的URL，因为检索的jsp程序在luceneweb子目录下.examples可用其它的真实应用的目录名来替换</INS><BR>cd ..<BR>cp ~/lucene-1.2/luceneweb.war . <INS>&lt;--luceneweb.war在你解压缩生成的lucene-1.2目录下</INS><BR>../bin/shudown.sh<BR>../bin/startup.sh</DIV><BR>然后通过客户端访问http://yourdomain.com:8080/luceneweb,如果顺利浏览器应出现右边所示的内容。<IMG height=151 alt=luceneweb-1.gif src="http://hedong.3322.org/archives/pics/luceneweb-1.gif" width=499 align=right border=0>.再到服务器端 
<DIV class=code>cd luceneweb<BR>vi configuration.jsp <INS>&lt;--将indexLocation 的值改为 "$TOMCATHOME/webapps/lucenedb";</INS><BR>cd ..<BR>jar -ur luceneweb.war luceneweb</DIV>再到客户端，刷新刚才的页面，然后就可以输入单词进行检索了。遗憾的是，此是只能检索英文单词。且如果命中的html页面的title是汉字的话，显示也有问题。如图。<BR><IMG height=256 alt=luceneweb-2.gif src="http://hedong.3322.org/archives/pics/luceneweb-2.gif" width=742 border=0><BR>　　此处的IndexHTML,可以对htm、html和txt类型的文件进行索引，用的是一个HTMLParser，除此以外与上一例基本相同。<img src ="http://www.blogjava.net/gaich/aggbug/6931.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gaich/" target="_blank">gaich</a> 2005-06-30 11:33 <a href="http://www.blogjava.net/gaich/archive/2005/06/30/6931.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于Java的全文索引引擎Lucene简介 </title><link>http://www.blogjava.net/gaich/archive/2005/06/30/6928.html</link><dc:creator>gaich</dc:creator><author>gaich</author><pubDate>Thu, 30 Jun 2005 03:19:00 GMT</pubDate><guid>http://www.blogjava.net/gaich/archive/2005/06/30/6928.html</guid><wfw:comment>http://www.blogjava.net/gaich/comments/6928.html</wfw:comment><comments>http://www.blogjava.net/gaich/archive/2005/06/30/6928.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gaich/comments/commentRss/6928.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gaich/services/trackbacks/6928.html</trackback:ping><description><![CDATA[<H1><FONT size=4>关键词：Lucene java full-text search engine&nbsp;Chinese&nbsp;word segment</FONT></H1>
<H1><SPAN style="FONT-WEIGHT: bold"></SPAN><FONT size=4>内容摘要：</FONT></H1>
<H1><FONT size=4>Lucene是一个基于Java的全文索引工具包。</FONT></H1>
<P>作者： 车东 Email: chedongATbigfoot.com/chedongATchedong.com</P>
<P>写于：2002/08 最后更新：
<SCRIPT language=Javascript src="http://www.chedong.com/referer.js"></SCRIPT>
 03/16/2005 16:27:52</P>
<OL>
<LI><A href="http://www.chedong.com/tech/lucene.html#intro">基于Java的全文索引引擎Lucene简介：关于作者和Lucene的历史</A> 
<LI><A href="http://www.chedong.com/tech/lucene.html#compare">全文检索的实现：Luene全文索引和数据库索引的比较</A> 
<LI><A href="http://www.chedong.com/tech/lucene.html#segment">中文切分词机制简介：基于词库和自动切分词算法的比较</A> 
<LI><A href="http://www.chedong.com/tech/lucene.html#demo">具体的安装和使用简介：系统结构介绍和演示</A> 
<LI><A href="http://www.chedong.com/tech/lucene.html#hacking">Hacking Lucene：简化的查询分析器，删除的实现，定制的排序，应用接口的扩展</A> 
<LI><A href="http://www.chedong.com/tech/lucene.html#learn">从Lucene我们还可以学到什么</A> </LI></OL>
<P><A name=intro><B>基于Java的全文索引/检索引擎——Lucene</B></A></P>
<P>Lucene不是一个完整的全文索引应用，而是是一个用Java写的全文索引引擎工具包，它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。</P>
<P>Lucene的作者：Lucene的贡献者<A href="http://www.nutch.org/blog/cutting.html">Doug Cutting</A>是一位资深全文索引/检索专家，曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者，后在Excite担任高级系统架构设计师，目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。</P>
<P>Lucene的发展历程：早先发布在作者自己的<A href="http://www.lucene.com/">www.lucene.com</A>，后来发布在<A href="http://sourceforge.net/projects/lucene/">SourceForge</A>，2001年年底成为APACHE基金会jakarta的一个子项目：<A href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/</A></P>
<P>已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎，比较著名的有：</P>
<UL>
<LI><A href="http://www.jivesoftware.com/">J</A><A href="http://www.jivesoftware.com/">ive</A>：WEB论坛系统； 
<LI><A href="http://eyebrowse.tigris.org/">Eyebrows</A>：邮件列表HTML归档/浏览/查询系统，本文的主要参考文档“<A href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html">TheLucene search engine: Powerful, flexible, and free</A>”作者就是EyeBrows系统的主要开发者之一，而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。 
<LI><A href="http://xml.apache.org/cocoon/index.html">Cocoon</A>:基于XML的web发布框架，全文检索部分使用了Lucene 
<LI>
<P align=left><A href="http://www.eclipse.org/">Eclipse</A>:基于Java的开放开发平台，帮助部分的全文索引使用了Lucene</P></LI></UL>
<P>对于中文用户来说，最关心的问题是其是否支持中文的全文检索。但通过后面对于Lucene的结构的介绍，你会了解到由于Lucene良好架构设计，对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。</P>
<P><B><A name=compare>全文检索的实现机制</A></B></P>
<P>Lucene的API接口设计的比较通用，输入输出结构都很像数据库的表==&gt;记录==&gt;字段，所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看：可以先把<B>Lucene当成一个支持全文索引的数据库系统</B>。</P>
<P>比较一下Lucene和数据库：</P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD align=middle width="50%">Lucene</TD>
<TD align=middle width="50%">数据库</TD></TR>
<TR>
<TD width="50%"><PRE>索引数据源：doc(field1,field2...) doc(field1,field2...)<BR>                  \  indexer /<BR>                 _____________<BR>                | Lucene Index|<BR>                --------------<BR>                 / searcher \<BR> 结果输出：Hits(doc(field1,field2) doc(field1...))</PRE></TD>
<TD width="50%"><PRE> 索引数据源：record(field1,field2...) record(field1..)<BR>              \  SQL: insert/<BR>               _____________<BR>              | DB  Index   |<BR>               -------------<BR>              / SQL: select \<BR>结果输出：results(record(field1,field2..) record(field1...))</PRE></TD></TR>
<TR>
<TD width="50%">Document：一个需要进行索引的“单元”<BR>一个Document由多个字段组成</TD>
<TD width="50%">Record：记录，包含多个字段</TD></TR>
<TR>
<TD width="50%">Field：字段</TD>
<TD width="50%">Field：字段</TD></TR>
<TR>
<TD width="50%">Hits：查询结果集，由匹配的Document组成</TD>
<TD width="50%">RecordSet：查询结果集，由多个Record组成</TD></TR></TBODY></TABLE>
<P><B>全文检索 ≠ like "%keyword%"</B></P>
<P>通常比较厚的书籍后面常常附关键词索引表（比如：北京：12, 34页，上海：3,77页……），它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样，想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高，另外一个原因是它是排好序的。<B>对于检索系统来说核心是一个排序问题</B>。</P>
<P align=left>由于数据库索引不是为全文索引设计的，因此，<B>使用like "%keyword%"时，数据库索引是不起作用的</B>，在使用like查询时，搜索过程又变成类似于一页页翻书的遍历过程了，所以对于含有模糊查询的数据库服务来说，LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配：like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。</P>
<P>所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制，将数据源（比如多篇文章）排序顺序存储的同时，有另外一个排好序的关键词列表，用于存储关键词==&gt;文章映射关系，利用这样的映射关系索引：[关键词==&gt;出现关键词的文章编号，出现次数（甚至包括位置：起始偏移量，结束偏移量），出现频率]，检索过程就是把<B>模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程</B>。从而大大提高了多关键词查询的效率，所以，全文检索问题归结到最后是一个排序问题。</P>
<P>由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题，这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制，并提供了扩展接口，以方便针对不同应用的定制。</P>
<P>可以通过一下表格对比一下数据库的模糊查询：</P>
<TABLE height=283 width="100%" border=1>
<TBODY>
<TR>
<TD align=middle width="9%" height=16>　</TD>
<TD align=middle width="47%" height=16>Lucene全文索引引擎</TD>
<TD align=middle width="40%" height=16>数据库</TD></TR>
<TR>
<TD width="9%" height=48>索引</TD>
<TD width="47%" height=48>将数据源中的数据都通过全文索引一一建立反向索引</TD>
<TD width="40%" height=48>对于LIKE查询来说，数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配，比有索引的搜索速度要有多个数量级的下降。</TD></TR>
<TR>
<TD width="9%" height=49>匹配效果</TD>
<TD width="47%" height=49>通过词元(term)进行匹配，通过语言分析接口的实现，可以实现对中文等非英语的支持。</TD>
<TD width="40%" height=49>使用：like "%net%" 会把netherlands也匹配出来，<BR>多个关键词的模糊匹配：使用like "%com%net%"：就不能匹配词序颠倒的xxx.net..xxx.com</TD></TR>
<TR>
<TD width="9%" height=32>匹配度</TD>
<TD width="47%" height=32>有匹配度算法，将匹配程度（相似度）比较高的结果排在前面。</TD>
<TD width="40%" height=32>没有匹配程度的控制：比如有记录中net出现5词和出现1次的，结果是一样的。</TD></TR>
<TR>
<TD width="9%" height=32>结果输出</TD>
<TD width="47%" height=32>通过特别的算法，将最匹配度最高的头100条结果输出，结果集是缓冲式的小批量读取的。</TD>
<TD width="40%" height=32>返回所有的结果集，在匹配条目非常多的时候（比如上万条）需要大量的内存存放这些临时结果集。</TD></TR>
<TR>
<TD width="9%" height=32>可定制性</TD>
<TD width="47%" height=32>通过不同的语言分析接口实现，可以方便的定制出符合应用需要的索引规则（包括对中文的支持）</TD>
<TD width="40%" height=32>没有接口或接口复杂，无法定制</TD></TR>
<TR>
<TD width="9%" height=32>结论</TD>
<TD width="47%" height=32>高负载的模糊查询应用，需要负责的模糊查询的规则，索引的资料量比较大</TD>
<TD width="40%" height=32>使用率低，模糊匹配规则简单或者需要模糊查询的资料量少</TD></TR></TBODY></TABLE>
<P><SPAN style="FONT-WEIGHT: bold">全文检索和数据库应用最大的不同在于：让</SPAN><SPAN style="FONT-WEIGHT: bold">最相关的</SPAN><SPAN style="FONT-WEIGHT: bold">头100条结果满足98%以上用户的需求<BR></SPAN><BR>Lucene的创新之处：</P>
<P>大部分的搜索（数据库）引擎都是用B树结构来维护索引，索引的更新会导致大量的IO操作，Lucene在实现中，对此稍微有所改进：不是维护一个索引文件，而是在扩展索引的时候不断创建新的索引文件，然后定期的把这些新的小索引文件合并到原先的大索引中（针对不同的更新策略，批次的大小可以调整），这样在不影响检索的效率的前提下，提高了索引的效率。</P>
<P>Lucene和其他一些全文检索系统/应用的比较：</P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD align=middle width="18%">　</TD>
<TD align=middle width="45%">Lucene</TD>
<TD align=middle width="37%">其他开源全文检索系统</TD></TR>
<TR>
<TD width="18%">增量索引和批量索引</TD>
<TD width="45%">可以进行增量的索引(Append)，可以对于大量数据进行批量索引，并且接口设计用于优化批量索引和小批量的增量索引。</TD>
<TD width="37%">很多系统只支持批量的索引，有时数据源有一点增加也需要重建索引。</TD></TR>
<TR>
<TD width="18%">数据源</TD>
<TD width="45%">Lucene没有定义具体的数据源，而是一个文档的结构，因此可以非常灵活的适应各种应用（只要前端有合适的转换器把数据源转换成相应结构），</TD>
<TD width="37%">很多系统只针对网页，缺乏其他格式文档的灵活性。</TD></TR>
<TR>
<TD width="18%">索引内容抓取</TD>
<TD width="45%">Lucene的文档是由多个字段组成的，甚至可以控制那些字段需要进行索引，那些字段不需要索引，近一步索引的字段也分为需要分词和不需要分词的类型：<BR>&nbsp;&nbsp; 需要进行分词的索引，比如：标题，文章内容字段<BR>&nbsp;&nbsp; 不需要进行分词的索引，比如：作者/日期字段</TD>
<TD width="37%">缺乏通用性，往往将文档整个索引了</TD></TR>
<TR>
<TD width="18%">语言分析</TD>
<TD width="45%">通过语言分析器的不同扩展实现：<BR>可以过滤掉不需要的词：an the of 等，<BR>西文语法分析：将jumps jumped jumper都归结成jump进行索引/检索<BR>非英文支持：对亚洲语言，阿拉伯语言的索引支持</TD>
<TD width="37%">缺乏通用接口实现</TD></TR>
<TR>
<TD width="18%">查询分析</TD>
<TD width="45%">通过查询分析接口的实现，可以定制自己的查询语法规则：<BR>比如： 多个关键词之间的 + - and or关系等</TD>
<TD width="37%">　</TD></TR>
<TR>
<TD width="18%">并发访问</TD>
<TD width="45%">能够支持多用户的使用</TD>
<TD width="37%">　</TD></TR></TBODY></TABLE>
<P>　</P>
<P><B><A name=segment>关于亚洲语言的的切分词问题(Word Segment)</A></B></P>
<P>对于中文来说，全文索引首先还要解决一个语言分析的问题，对于英文来说，语句中单词之间是天然通过空格分开的，但亚洲语言的中日韩文语句中的字是一个字挨一个，所有，首先要把语句中按“词”进行索引的话，这个词如何切分出来就是一个很大的问题。</P>
<P>首先，肯定不能用单个字符作(si-gram)为索引单元，否则查“上海”时，不能让含有“海上”也匹配。</P>
<P>但一句话：“北京天安门”，计算机如何按照中文的语言习惯进行切分呢？<BR>“北京 天安门” 还是“北 京 天安门”？让计算机能够按照语言习惯进行切分，往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。</P>
<P>另外一个解决的办法是采用自动切分算法：将单词按照2元语法(bigram)方式切分出来，比如：<BR>"北京天安门" ==&gt; "北京 京天 天安 安门"。</P>
<P>这样，在查询的时候，无论是查询"北京" 还是查询"天安门"，将查询词组按同样的规则进行切分："北京"，"天安安门"，多个关键词之间按与"and"的关系组合，同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言：韩文，日文都是通用的。</P>
<P>基于自动切分的最大优点是没有词表维护成本，实现简单，缺点是索引效率低，但对于中小型应用来说，基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多，而对于英文，索引文件一般只有原文件的30%-40%不同，</P>
<TABLE height=68 width="100%" border=1>
<TBODY>
<TR>
<TD align=middle width="11%" height=18><BR></TD>
<TD align=middle width="39%" height=18>自动切分</TD>
<TD align=middle width="50%" height=18>词表切分</TD></TR>
<TR>
<TD width="11%" height=16>实现</TD>
<TD width="39%" height=16>实现非常简单</TD>
<TD width="50%" height=16>实现复杂</TD></TR>
<TR>
<TD width="11%" height=16>查询</TD>
<TD width="39%" height=16>增加了查询分析的复杂程度，</TD>
<TD width="50%" height=16>适于实现比较复杂的查询语法规则</TD></TR>
<TR>
<TD width="11%" height=16>存储效率</TD>
<TD width="39%" height=16>索引冗余大，索引几乎和原文一样大</TD>
<TD width="50%" height=16>索引效率高，为原文大小的30％左右</TD></TR>
<TR>
<TD width="11%" height=16>维护成本</TD>
<TD width="39%" height=16>无词表维护成本</TD>
<TD width="50%" height=16>词表维护成本非常高：中日韩等语言需要分别维护。<BR>还需要包括词频统计等内容</TD></TR>
<TR>
<TD width="11%" height=16>适用领域</TD>
<TD width="39%" height=16>嵌入式系统：运行环境资源有限<BR>分布式系统：无词表同步问题<BR>多语言环境：无词表维护成本</TD>
<TD width="50%" height=16>对查询和存储效率要求高的专业搜索引擎<BR></TD></TR></TBODY></TABLE>
<P>目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法，大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。</P>
<P><A name=demo><B>安装和使用</B></A></P>
<P>下载：<A href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/</A></P>
<P>注意：Lucene中的一些比较复杂的词法分析是用JavaCC生成的（JavaCC：JavaCompilerCompiler，纯Java的词法分析生成器），所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器，还需要从<A href="https://javacc.dev.java.net/">https://javacc.dev.java.net/</A>下载javacc。</P>
<P>lucene的组成结构：对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口</P>
<TABLE width="100%" border=1>
<TBODY>
<TR>
<TD width="27%">org.apache.Lucene.search/</TD>
<TD width="73%">搜索入口</TD></TR>
<TR>
<TD width="27%">org.apache.Lucene.index/</TD>
<TD width="73%">索引入口</TD></TR>
<TR>
<TD width="27%">org.apache.Lucene.analysis/</TD>
<TD width="73%">语言分析器</TD></TR>
<TR>
<TD width="27%">org.apache.Lucene.queryParser/</TD>
<TD width="73%">查询分析器</TD></TR>
<TR>
<TD width="27%">org.apache.Lucene.document/</TD>
<TD width="73%">存储结构</TD></TR>
<TR>
<TD width="27%">org.apache.Lucene.store/&nbsp;</TD>
<TD width="73%">底层IO/存储结构</TD></TR>
<TR>
<TD width="27%">org.apache.Lucene.util/</TD>
<TD width="73%">一些公用的数据结构</TD></TR></TBODY></TABLE>
<P>简单的例子演示一下Lucene的使用方法：</P>索引过程：从命令行读取文件名（多个），将文件分路径(path字段)和内容(body字段)2个字段进行存储，并对内容进行全文索引：索引的单位是Document对象，每个Document对象包含多个字段Field对象，针对不同的字段属性和数据输出的需求，对字段还可以选择不同的索引/存储字段规则，列表如下： 
<TABLE border=1>
<TBODY>
<TR>
<TH>方法</TH>
<TH>切词</TH>
<TH>索引</TH>
<TH>存储</TH>
<TH>用途</TH></TR>
<TR>
<TD>Field.Text(String name, String value)</TD>
<TD>Yes</TD>
<TD>Yes</TD>
<TD>Yes</TD>
<TD vAlign=top>切分词索引并存储，比如：标题，内容字段</TD></TR>
<TR>
<TD>Field.Text(String name, Reader value)</TD>
<TD>Yes</TD>
<TD>Yes</TD>
<TD>No</TD>
<TD vAlign=top>切分词索引不存储，比如：META信息，<BR>不用于返回显示，但需要进行检索内容</TD></TR>
<TR>
<TD>Field.Keyword(String name, String value)</TD>
<TD>No</TD>
<TD>Yes</TD>
<TD>Yes</TD>
<TD vAlign=top>不切分索引并存储，比如：日期字段</TD></TR>
<TR>
<TD>Field.UnIndexed(String name, String value)</TD>
<TD>No</TD>
<TD>No</TD>
<TD>Yes</TD>
<TD vAlign=top>不索引，只存储，比如：文件路径</TD></TR>
<TR>
<TD>Field.UnStored(String name, String value)</TD>
<TD>Yes</TD>
<TD>Yes</TD>
<TD>No</TD>
<TD vAlign=top>只全文索引，不存储</TD></TR></TBODY></TABLE><PRE>public class IndexFiles { <BR>  //使用方法：: IndexFiles [索引输出目录] [索引的文件列表] ... <BR>  public static void main(String[] args) throws Exception {<BR>    String indexPath = args[0];<BR>    IndexWriter writer;<BR>    //用指定的语言分析器构造一个新的写索引器（第3个参数表示是否为追加索引）<BR>    writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<BR><BR>    for (int i=1; i&lt;args.length; i++) {<BR>      System.out.println("Indexing file " + args[i]);<BR>      InputStream is = new FileInputStream(args[i]);<BR><BR>      //构造包含2个字段Field的Document对象<BR>      //一个是路径path字段，不索引，只存储<BR>      //一个是内容body字段，进行全文索引，并存储<BR>      Document doc = new Document();<BR>      doc.add(Field.UnIndexed("path", args[i]));<BR>      doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<BR>      //将文档写入索引<BR>      writer.addDocument(doc);<BR>      is.close();<BR>    };<BR>    //关闭写索引器<BR>    writer.close();<BR>  }<BR>}<BR>　</PRE>
<P>索引过程中可以看到：</P>
<UL>
<LI>语言分析器提供了抽象的接口，因此语言分析(Analyser)是可以定制的，虽然lucene缺省提供了2个比较通用的分析器SimpleAnalyser和StandardAnalyser，这2个分析器缺省都不支持中文，所以要加入对中文语言的切分规则，需要修改这2个分析器。 
<LI>Lucene并没有规定数据源的格式，而只提供了一个通用的结构（Document对象）来接受索引的输入，因此输入的数据源可以是：数据库，WORD文档，PDF文档，HTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。 
<LI>对于大批量的数据索引，还可以通过调整IndexerWrite的文件合并频率属性（mergeFactor）来提高批量索引的效率。 </LI></UL>
<P>检索过程和结果显示：</P>
<P>搜索结果返回的是Hits对象，可以通过它再访问Document==&gt;Field中的内容。</P>
<P>假设根据body字段进行全文检索，可以将查询结果的path字段和相应查询的匹配度(score)打印出来，</P><PRE>public class Search { <BR>  public static void main(String[] args) throws Exception {<BR>    String indexPath = args[0], queryString = args[1];<BR>    //指向索引目录的搜索器<BR>    Searcher searcher = new IndexSearcher(indexPath);<BR>    //查询解析器：使用和索引同样的语言分析器<BR>    Query query = QueryParser.parse(queryString, "body", <BR>                              new SimpleAnalyzer());<BR>    //搜索结果使用Hits存储<BR>    Hits hits = searcher.search(query);<BR>    //通过hits可以访问到相应字段的数据和查询的匹配度<BR>    for (int i=0; i&lt;hits.length(); i++) {<BR>      System.out.println(hits.doc(i).get("path") + "; Score: " + <BR>                         hits.score(i));<BR>    };<BR>  }<BR>}</PRE>在整个检索过程中，语言分析器，查询分析器，甚至搜索器（Searcher）都是提供了抽象的接口，可以根据需要进行定制。 
<P><B><A name=hacking>Hacking Lucene</A></B></P>
<P><B>简化的查询分析器</B></P>
<P>个人感觉lucene成为JAKARTA项目后，画在了太多的时间用于调试日趋复杂QueryParser，而其中大部分是大多数用户并不很熟悉的，目前LUCENE支持的语法：</P>
<P>Query ::= ( Clause )*<BR>Clause ::= ["+", "-"] [&lt;TERM&gt; ":"] ( &lt;TERM&gt; | "(" Query ")")</P>
<P>中间的逻辑包括：and or + - &amp;&amp;||等符号，而且还有"短语查询"和针对西文的前缀/模糊查询等，个人感觉对于一般应用来说，这些功能有一些华而不实，其实能够实现目前类似于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以，Lucene早期版本的QueryParser仍是比较好的选择。</P>
<P><B>添加修改删除指定记录（Document）</B></P>
<P>Lucene提供了索引的扩展机制，因此索引的动态扩展应该是没有问题的，而指定记录的修改也似乎只能通过记录的删除，然后重新加入实现。如何删除指定的记录呢？删除的方法也很简单，只是需要在索引时根据数据源中的记录ID专门另建索引，然后利用IndexReader.delete(Termterm)方法通过这个记录ID删除相应的Document。</P>
<P><B>根据某个字段值的排序功能</B></P>
<P>lucene缺省是按照自己的相关度算法（score）进行结果排序的，但能够根据其他字段进行结果排序是一个在LUCENE的开发邮件列表中经常提到的问题，很多原先基于数据库应用都需要除了基于匹配度（score）以外的排序功能。而从全文检索的原理我们可以了解到，任何不基于索引的搜索过程效率都会导致效率非常的低，如果基于其他字段的排序需要在搜索过程中访问存储字段，速度回大大降低，因此非常是不可取的。</P>
<P>但这里也有一个折中的解决方法：在搜索过程中能够影响排序结果的只有索引中已经存储的docID和score这2个参数，所以，基于score以外的排序，其实可以通过将数据源预先排好序，然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访问不在索引中的某个字段值。</P>
<P>这里需要修改的是IndexSearcher中的HitCollector过程：</P><PRE>...<BR>　scorer.score(new HitCollector() {<BR>	private float minScore = 0.0f;<BR>	public final void collect(int doc, float score) {<BR>	  if (score &gt; 0.0f &amp;&amp;			  // ignore zeroed buckets<BR>	      (bits==null || bits.get(doc))) {	  // skip docs not in bits<BR>	    totalHits[0]++;<BR>	    if (score &gt;= minScore) {<BR>              /* 原先：Lucene将docID和相应的匹配度score例入结果命中列表中：<BR>	       * hq.put(new ScoreDoc(doc, score));	  // update hit queue<BR>               * 如果用doc 或 1/doc 代替 score，就实现了根据docID顺排或逆排<BR>               * 假设数据源索引时已经按照某个字段排好了序，而结果根据docID排序也就实现了<BR>               * 针对某个字段的排序，甚至可以实现更复杂的score和docID的拟合。<BR>               */<BR>              hq.put(new ScoreDoc(doc, (float) 1/doc )); <BR>	      if (hq.size() &gt; nDocs) {		  // if hit queue overfull<BR>		hq.pop();			  // remove lowest in hit queue<BR>		minScore = ((ScoreDoc)hq.top()).score; // reset minScore<BR>	      }<BR>	    }<BR>	  }<BR>	}<BR>      }, reader.maxDoc());</PRE>
<P><B>更通用的输入输出接口</B></P>
<P>虽然lucene没有定义一个确定的输入文档格式，但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口，然后其他数据，比如PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主，类似实现已经不下4，5个：</P><PRE>数据源: WORD       PDF     HTML    DB       other<BR>         \          |       |      |         /<BR>                       XML中间格式<BR>                            |<BR>                     Lucene INDEX</PRE>
<P>目前还没有针对MSWord文档的解析器，因为Word文档和基于ASCII的RTF文档不同，需要使用COM对象机制解析。这个是我在Google上查的相关资料：<A href="http://www.intrinsyc.com/products/enterprise_applications.asp">http://www.intrinsyc.com/products/enterprise_applications.asp</A><BR>另外一个办法就是把Word文档转换成text：<A href="http://www.winfield.demon.nl/index.html">http://www.winfield.demon.nl/index.html</A><BR></P>
<P><BR><B>索引过程优化</B></P>
<P>索引一般分2种情况，一种是小批量的索引扩展，一种是大批量的索引重建。在索引过程中，并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作（文件I/O是一件非常消耗资源的事情）。</P>
<P>Lucene先在内存中进行索引操作，并根据一定的批量进行文件的写入。这个批次的间隔越大，文件的写入次数越少，但占用内存会很多。反之占用内存少，但文件IO操作频繁，索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件的操作。根据我的使用经验：缺省Indexer是每20条记录索引后写入一次，每将MERGE_FACTOR增加50倍，索引速度可以提高1倍左右。<BR></P>
<P><SPAN style="FONT-WEIGHT: bold">搜索过程优化<BR></SPAN></P>
<P><SPAN style="FONT-WEIGHT: bold"></SPAN>lucene支持内存索引：这样的搜索比基于文件的I/O有数量级的速度提升。<BR><A href="http://www.onjava.com/lpt/a/3273">http://www.onjava.com/lpt/a/3273</A><BR>而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。<BR><SPAN style="FONT-WEIGHT: bold"></SPAN></P>
<P><B></B></P>
<P>Lucene面向全文检索的优化在于首次索引检索后，并不把所有的记录（Document）具体内容读取出来，而起只将所有结果中匹配度最高的头100条结果（TopDocs）的ID放到结果集缓存中并返回，这里可以比较一下数据库检索：如果是一个10,000条的数据库检索结果集，数据库是一定要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多，Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是用不到这么多的结果的，头100条已经可以满足90%以上的检索需求。<BR></P>
<P>如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存，并再重新向后抓取。所以如果构造一个Searcher去查1－120条结果，Searcher其实是进行了2次搜索过程：头100条取完后，缓存结果用完，Searcher重新检索再构造一个200条的结果缓存，依此类推，400条缓存，800条缓存。由于每次Searcher对象消失后，这些缓存也访问那不到了，你有可能想将结果记录缓存下来，缓存数尽量保证在100以下以充分利用首次的结果缓存，不让Lucene浪费多次检索，而且可以分级进行结果缓存。<BR></P>
<P>Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。</P>
<P><A href="http://sourceforge.net/projects/weblucene/">我的一些尝试</A>：</P>
<UL>
<LI>支持中文的Tokenizer：这里有2个版本，一个是通过JavaCC生成的，对CJK部分按一个字符一个TOKEN索引，另外一个是从SimpleTokenizer改写的，对英文支持数字和字母TOKEN，对中文按迭代索引。 
<LI>基于XML数据源的索引器：XMLIndexer，因此所有数据源只要能够按照DTD转换成指定的XML，就可以用XMLIndxer进行索引了。 
<LI>根据某个字段排序：按记录索引顺序排序结果的搜索器：IndexOrderSearcher，因此如果需要让搜索结果根据某个字段排序，可以让数据源先按某个字段排好序（比如：PriceField），这样索引后，然后在利用这个按记录的ID顺序检索的搜索器，结果就是相当于是那个字段排序的结果了。 </LI></UL>
<P><A name=learn><B>从Lucene学到更多</B></A></P>
<P>Luene的确是一个面对对象设计的典范</P>
<UL>
<LI>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要； 
<LI>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务； 
<LI>所有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修改其他模块。&nbsp; 
<LI>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。 </LI></UL>
<P>这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。</P>
<P>此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</P>
<UL>
<LI>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。 
<LI>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的， 
<LI>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。 
<LI>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的操作。<BR></LI></UL>
<P>参考资料：</P>
<P>Apache: Lucene Project<BR><A href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/<BR></A>Lucene开发/用户邮件列表归档<BR><A href="http://www.mail-archive.com/lucene-dev@jakarta.apache.org/">Lucene-dev@jakarta.apache.org</A><BR><A href="http://www.mail-archive.com/lucene-user@jakarta.apache.org/">Lucene-user@jakarta.apache.org</A></P>
<P>The Lucene search engine: Powerful, flexible, and free<BR><A href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html">http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html</A></P>
<P>Lucene Tutorial<BR><A href="http://www.darksleep.com/puff/lucene/lucene.html">http://www.darksleep.com/puff/lucene/lucene.html</A></P>
<P>Notes on distributed searching with Lucene<BR><A href="http://home.clara.net/markharwood/lucene/">http://home.clara.net/markharwood/lucene/</A></P>
<P>中文语言的切分词<BR><A href="http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment">http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment</A></P>
<P>搜索引擎工具介绍<A href="http://searchtools.com/"><BR>http://searchtools.com/</A></P>
<P>Lucene作者Cutting的几篇论文和专利<BR><A href="http://lucene.sourceforge.net/publications.html">http://lucene.sourceforge.net/publications.html</A>&nbsp;</P>
<P>Lucene的.NET实现：dotLucene<BR><A href="http://sourceforge.net/projects/dotlucene/">http://sourceforge.net/projects/dotlucene/<BR></A></P>
<P>Lucene作者Cutting的另外一个项目：基于Java的搜索引擎Nutch<BR><A href="http://www.nutch.org/">http://www.nutch.org/</A> &nbsp; <A href="http://sourceforge.net/projects/nutch/">http://sourceforge.net/projects/nutch/<BR></A></P>
<P>关于基于词表和N-Gram的切分词比较<BR><A href="http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html">http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html</A><BR><BR>2005-01-08 <A href="http://lucene.sourceforge.net/talks/pisa/">Cutting在Pisa大学做的关于Lucene的讲座：非常详细的Lucene架构解说</A> </P><img src ="http://www.blogjava.net/gaich/aggbug/6928.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gaich/" target="_blank">gaich</a> 2005-06-30 11:19 <a href="http://www.blogjava.net/gaich/archive/2005/06/30/6928.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>