<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Information Retrieval Blog &#187; lucene</title>
	<atom:link href="http://blog.zye.me/tag/lucene/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.zye.me</link>
	<description>REAL TIME DATA PROCESSING, DISTRIBUTED COMPUTING, PATTERN DISCOVERY</description>
	<lastBuildDate>Wed, 08 Feb 2012 17:33:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Solr Error: A SPI class of type org.apache.lucene.index.codecs.Codec with name &#8216;Lucene40&#8242; does not exist</title>
		<link>http://blog.zye.me/2011/11/55863.html</link>
		<comments>http://blog.zye.me/2011/11/55863.html#comments</comments>
		<pubDate>Mon, 14 Nov 2011 07:30:35 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[IR]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[Lucene40]]></category>
		<category><![CDATA[Solr]]></category>

		<guid isPermaLink="false">http://blog.zye.me/2011/11/55863.html</guid>
		<description><![CDATA[When you are testing Solr in Eclipse, you may encounter the problem that &#8220;A SPI class of type org.apache.lucene.index.codecs.Codec with name &#8216;Lucene40&#8242; does not exist. You need to add the corresponding JAR file supporting this SPI to your classpath.The current classpath supports the following names: []&#8221; The fast answer to tackle this problem is to <a href='http://blog.zye.me/2011/11/55863.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<p>When you are testing Solr in Eclipse, you may encounter the problem that &#8220;A SPI class of type org.apache.lucene.index.codecs.Codec with name &#8216;Lucene40&#8242; does not exist. You need to add the corresponding JAR file supporting this SPI to your classpath.The current classpath supports the following names: []&#8221;</p>
<p>The fast answer to tackle this problem is to add the lucene-core*.jar to the libaries of your project, and make sure that the file /META-INF/services/org.apache.lucene.index.codecs.Codec is included in lucene-core*.jar.&nbsp;</p>
<p>The problem is caused by the class of java.util.ServiceLoader used in&nbsp;NamedSPILoader of Solr. When ServiceLoader creates a new service provider, it will extract some information from&nbsp;/META-INF/services/org.apache.lucene.index.codecs.Codec. Therefore when you use build classes rather than the lucene-core*.jar file, the needed information can not be obtained. By now, I think you can handle this problem yourself.&nbsp;</p>
<p>For more information about How ServiceLoader work, please refer to:&nbsp;</p>
<p><a href="http://jcs.mobile-utopia.com/jcs/19273_ServiceLoader.java">http://jcs.mobile-utopia.com/jcs/19273_ServiceLoader.java</a>&nbsp;In English</p>
<p><a href="http://mxdxm.iteye.com/blog/1059300">http://mxdxm.iteye.com/blog/1059300</a>&nbsp;In Chinese</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/11/55863.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何读取Lucene索引数据1&#8211;整理中</title>
		<link>http://blog.zye.me/2011/09/3886.html</link>
		<comments>http://blog.zye.me/2011/09/3886.html#comments</comments>
		<pubDate>Sat, 10 Sep 2011 01:52:04 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[indexing]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[信息检索]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/articles/%e5%a6%82%e4%bd%95%e8%af%bb%e5%8f%96lucene%e7%b4%a2%e5%bc%95%e6%95%b0%e6%8d%ae1-%e6%95%b4%e7%90%86%e4%b8%ad.html</guid>
		<description><![CDATA[Lucene源码分析(1) &#8212; 如何读取Lucene索引数据 终于清楚如何用读Lucene的索引 。本文要介绍一下如何利用IndexReader获取信息。为什么要读索引呢？因为我需要实现这些功能： (1) 统计term在整个collection中的文档频度(document frequency, DF)； (2) 统计term在整个collection中出现的词次(term frequency in whole collection)； (3) 统计term在某个文档中出现的频度(term frequency, TF)； (4) 列出term在某文档中出现的位置(position)； (5) 整个collection中文档的个数； 那 么为什么要用到这些数据呢？这些数据是实现TR(Text Retrieval，文本检索)的必备的&#8221;原料&#8221;，而且是经过加工的。在检索之前，只有原始文本(raw data)；经过索引器(indexer)的处理之后，原始文本变成了一个一个的term(或者token)，然后被indexer纪录下来所在的位置、 出现的次数。有了这些数据，应用一些模型，就可以实现搜索引擎实现的功能――文本检索。 聪明的读 者您可能会说，这看起来似乎很好做，不过就是计数(count)么。不错，就是计数，或者说是统计。但是看似简单的过程，如果加上空间(内存容量)的限 制，就显得不那么简单了。假设如果每篇文档有100个term，每个term需要存储10字节信息，存1,000,000篇文档需要 10x100x10^6=10^9=2^30字节，也就是1GB。虽然现在1G内存不算什么，可是总不能把1GB的数据时时刻刻都放入内存吧。那么放入硬 盘好了，现在需要用数据的时候，再把1GB数据从硬盘搬到内存。OK，可以先去冲杯咖啡，回来在继续下面的操作。这是1,000,000的文档，如果更多 一点呢，现在没有任何辅助数据结构的方式，会导致很差的效率。 Lucene的索引会把数据分成 段，并且在需要的时候才读，不需要的时候就让数据乖乖地呆在硬盘上。Lucene本身是一个优秀的索引引擎，能够提供有效的索引和检索机制。文本的目的 是，介绍如用利用Lucene的API，如何从已经建好的索引的数据中读取需要的信息。至于Lucene如何使用，我会在后续的文章中逐渐介绍。 我们一步一步来看。这里建设已经有实现建好索引，存放在index目录下。好，要读索引，总得先生成一个读索引器(即Lucene中IndexReader的实例)。好，写下面的程序(程序为C#程序，本文使用DotLucene)。 IndexReader reader; 问 题出来了，IndexReader是一个abstract类，不能实例化。那好，换派生类试试看。找到IndexReader的两个孩子 ――SegmentReader和MultiReader。用哪个呢？无论是哪个都需要一大堆参数(我是颇费了周折才搞清楚它们的用途，后面再解释)，似 乎想用Lucene的索引数据不是那么容易啊。通过跟踪代码和查阅文档，我终于找到使用IndexReader的钥匙。原来IndexReader有一个 &#8220;工厂模式&#8221;的static interface――IndexReader.Open。定义如下： #0001 public static IndexReader Open(System.String path) #0002 public static <a href='http://blog.zye.me/2011/09/3886.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<p>Lucene源码分析(1) &#8212; 如何读取Lucene索引数据</p>
<p id="msgcns!3BB36966ED98D3E5!408" class="bvMsg"><font face="Verdana"></font><font size="2">终于清楚如何用读Lucene的索引 <img src='http://blog.zye.me/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> 。本文要介绍一下如何利用IndexReader获取信息。为什么要读索引呢？因为我需要实现这些功能：<br />
(1) 统计term在整个collection中的文档频度(document frequency, DF)；<br />
(2) 统计term在整个collection中出现的词次(term frequency in whole collection)；<br />
(3) 统计term在某个文档中出现的频度(term frequency, TF)；<br />
(4) 列出term在某文档中出现的位置(position)；<br />
(5) 整个collection中文档的个数；</font></p>
<p><font face="Verdana"></font><font size="2">那 么为什么要用到这些数据呢？这些数据是实现TR(Text Retrieval，文本检索)的必备的&#8221;原料&#8221;，而且是经过加工的。在检索之前，只有原始文本(raw data)；经过索引器(indexer)的处理之后，原始文本变成了一个一个的term(或者token)，然后被indexer纪录下来所在的位置、 出现的次数。有了这些数据，应用一些模型，就可以实现搜索引擎实现的功能――文本检索。</font></p>
<p><font face="Verdana"></font><font size="2">聪明的读 者您可能会说，这看起来似乎很好做，不过就是计数(count)么。不错，就是计数，或者说是统计。但是看似简单的过程，如果加上空间(内存容量)的限 制，就显得不那么简单了。假设如果每篇文档有100个term，每个term需要存储10字节信息，存1,000,000篇文档需要 10x100x10^6=10^9=2^30字节，也就是1GB。虽然现在1G内存不算什么，可是总不能把1GB的数据时时刻刻都放入内存吧。那么放入硬 盘好了，现在需要用数据的时候，再把1GB数据从硬盘搬到内存。OK，可以先去冲杯咖啡，回来在继续下面的操作。这是1,000,000的文档，如果更多 一点呢，现在没有任何辅助数据结构的方式，会导致很差的效率。</font></p>
<p><font face="Verdana"></font><font size="2">Lucene的索引会把数据分成 段，并且在需要的时候才读，不需要的时候就让数据乖乖地呆在硬盘上。Lucene本身是一个优秀的索引引擎，能够提供有效的索引和检索机制。文本的目的 是，介绍如用利用Lucene的API，如何从已经建好的索引的数据中读取需要的信息。至于Lucene如何使用，我会在后续的文章中逐渐介绍。</font></p>
<p><font face="Verdana"></font><font size="2">我们一步一步来看。这里建设已经有实现建好索引，存放在index目录下。好，要读索引，总得先生成一个读索引器(即Lucene中IndexReader的实例)。好，写下面的程序(程序为C#程序，本文使用DotLucene)。<br />
IndexReader reader;<br />
问 题出来了，IndexReader是一个abstract类，不能实例化。那好，换派生类试试看。找到IndexReader的两个孩子 ――SegmentReader和MultiReader。用哪个呢？无论是哪个都需要一大堆参数(我是颇费了周折才搞清楚它们的用途，后面再解释)，似 乎想用Lucene的索引数据不是那么容易啊。通过跟踪代码和查阅文档，我终于找到使用IndexReader的钥匙。原来IndexReader有一个 &#8220;工厂模式&#8221;的static interface――IndexReader.Open。定义如下：<br />
#0001 public static IndexReader Open(System.String path)<br />
#0002 public static IndexReader Open(System.IO.FileInfo path)<br />
#0003 public static IndexReader Open(Directory directory)<br />
#0004 private static IndexReader Open(Directory directory, bool closeDirectory)<br />
其中有三个是public的接口，可供调用。打开一个索引，就是这么简单：<br />
#0001 IndexReader reader = IndexReader.Open(index);</font></p>
<p><font face="Verdana"></font><font size="2">实际上，这个打开索引经历了这样的一个过程：<br />
#0001 SegmentInfos infos = new SegmentInfos();<br />
#0002 Directory directory = FSDirectory.GetDirectory(index, false);<br />
#0003 infos.Read(directory);<br />
#0004 bool closeDirectory = false;<br />
#0005 if (infos.Count == 1)<br />
#0006 {<br />
#0007 // index is optimized<br />
#0008 return new SegmentReader(infos, infos.Info(0), closeDirectory);<br />
#0009 }<br />
#0010 else<br />
#0011 {<br />
#0012 IndexReader[] readers = new IndexReader[infos.Count];<br />
#0013 for (int i = 0; i &lt; infos.Count; i++)<br />
#0014 readers[i] = new SegmentReader(infos.Info(i));<br />
#0015 return new MultiReader(directory, infos, closeDirectory, readers);<br />
#0016 }</font></p>
<p><font face="Verdana"></font><font size="2">首 先要读入索引的段信息(segment information, #0001~#0003)，然后看一下有几个段：如果只有一个，那么可能是优化过的，直接读取这一个段就可以(#0008)；否则需要一次读入各个段 (#0013~#0014)，然后再拼成一个MultiReader(#0015)。打开索引文件的过程就是这样。</font></p>
<p><font face="Verdana"></font><font size="2">接下来我们要看看如何读取信息了。用下面这段代码来说明。<br />
#0001 public static void PrintIndex(IndexReader reader)<br />
#0002 {<br />
#0003 //显示有多少个document<br />
#0004 System.Console.WriteLine(reader + &#8220;tNumDocs = &#8221; + reader.NumDocs());<br />
#0005 for (int i = 0; i &lt; reader.NumDocs(); i++)<br />
#0006 {<br />
#0007 System.Console.WriteLine(reader.Document(i));<br />
#0008 }<br />
#0009<br />
#0010 //枚举term，获得&lt;document, term freq, position* &gt;信息<br />
#0011 TermEnum termEnum = reader.Terms();<br />
#0012 while (termEnum.Next())<br />
#0013 {<br />
#0014 System.Console.Write(termEnum.Term());<br />
#0015 System.Console.WriteLine(&#8220;tDocFreq=&#8221; + termEnum.DocFreq());<br />
#0016<br />
#0017 TermPositions termPositions = reader.TermPositions(termEnum.Term());<br />
#0018 int i = 0;<br />
#0019 int j = 0;<br />
#0020 while (termPositions.Next())<br />
#0021 {<br />
#0022 System.Console.WriteLine((i++) + &#8220;-&gt;&#8221; + &#8221; DocNo:&#8221; + termPositions.Doc() + &#8220;, Freq:&#8221; + termPositions.Freq());<br />
#0023 for (j = 0; j &lt; termPositions.Freq(); j++)<br />
#0024 System.Console.Write(&#8220;[" + termPositions.NextPosition() + "]&#8220;);<br />
#0025 System.Console.WriteLine();<br />
#0026 }<br />
#0027<br />
#0028 //直接获取 &lt;term freq, document&gt; 的信息<br />
#0029 TermDocs termDocs = reader.TermDocs(termEnum.Term());<br />
#0030 while (termDocs.Next())<br />
#0031 {<br />
#0032 System.Console.WriteLine((i++) + &#8220;-&gt;&#8221; + &#8221; DocNo:&#8221; + termDocs.Doc() + &#8220;, Freq:&#8221; + termDocs.Freq());<br />
#0033 }<br />
#0034 }<br />
#0035<br />
#0036 // FieldInfos fieldInfos = reader.fieldInfos;<br />
#0037 // FieldInfo pathFieldInfo = fieldInfos.FieldInfo(&#8220;path&#8221;);<br />
#0038<br />
#0039 //显示 term frequency vector<br />
#0040 for (int i = 0; i &lt; reader.NumDocs(); i++)<br />
#0041 {<br />
#0042 //对contents的token之后的term存于了TermFreqVector<br />
#0043 TermFreqVector termFreqVector = reader.GetTermFreqVector(i, &#8220;contents&#8221;);<br />
#0044<br />
#0045 if (termFreqVector == null)<br />
#0046 {<br />
#0047 System.Console.WriteLine(&#8220;termFreqVector is null.&#8221;);<br />
#0048 continue;<br />
#0049 }<br />
#0050<br />
#0051 String fieldName = termFreqVector.GetField();<br />
#0052 String[] terms = termFreqVector.GetTerms();<br />
#0053 int[] frequences = termFreqVector.GetTermFrequencies();<br />
#0054<br />
#0055 System.Console.Write(&#8220;FieldName:&#8221; + fieldName);<br />
#0056 for (int j = 0; j &lt; terms.Length; j++)<br />
#0057 {<br />
#0058 System.Console.Write(&#8220;[" + terms[j] + &#8220;:&#8221; + frequences[j] + &#8220;]&#8221;);<br />
#0059 }<br />
#0060 System.Console.WriteLine();<br />
#0061 }<br />
#0062 System.Console.WriteLine();<br />
#0063 }</font></p>
<p><font face="Verdana"></font><font size="2">#0004 计算document的个数<br />
#0012~#0034 枚举collection中所有的term<br />
其中#0017~#0026 枚举每个term在出现的document中的所有位置(第几个词，从1开始计数)；#0029~#0033 计算每个term出现在哪些文档和相应的出现频度(即DF和TF)。<br />
#0036~#0037在reader是SegmentReader类型的情况下有效。<br />
#0040~#0061可以快速的读取某篇文档中出现的term和相应的频度。但是这部分需要在建索引时，设置storeTermVector为true。比如<br />
doc.Add(Field.Text(&#8220;contents&#8221;, reader, true));<br />
其中的第三项即是。默认为false。</font></p>
<p><font face="Verdana"></font><font size="2">有了这些数据，就可以统计我需要的数据了。以后我会介绍如何建立索引，如何应用Lucene。</font></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/09/3886.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Lucene同时进行查询和索引</title>
		<link>http://blog.zye.me/2011/07/36221.html</link>
		<comments>http://blog.zye.me/2011/07/36221.html#comments</comments>
		<pubDate>Sun, 17 Jul 2011 14:27:50 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[indexing]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[searching]]></category>
		<category><![CDATA[查询]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/2008/07/36221.html</guid>
		<description><![CDATA[Does Lucene allow searching and indexing simultaneously?Yes. However, an IndexReader only searches the index as of the &#8220;point in time&#8221; that it was opened. Lucene在用IndexReader打开索引的同时，允许用IndexWriter对该索引进行更新，但是IndexReader只能查询到open索引时所索引的文件或者说Document，要想查询新索引的Document，IndexReader必须调用reopen方法（该方法开销较小）。 IndexReader.isCurrent() 方法可以用于测试索引是否有更新。]]></description>
			<content:encoded><![CDATA[<p>Does Lucene allow searching and indexing simultaneously?Yes. However, an IndexReader only searches the index as of the &#8220;point in time&#8221; that it was opened.</p>
<p>Lucene在用IndexReader打开索引的同时，允许用IndexWriter对该索引进行更新，但是IndexReader只能查询到open索引时所索引的文件或者说Document，要想查询新索引的Document，IndexReader必须调用reopen方法（该方法开销较小）。</p>
<p>IndexReader.isCurrent() 方法可以用于测试索引是否有更新。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/07/36221.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lucene 在线查询</title>
		<link>http://blog.zye.me/2011/06/23889.html</link>
		<comments>http://blog.zye.me/2011/06/23889.html#comments</comments>
		<pubDate>Sun, 12 Jun 2011 14:27:35 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[信息检索]]></category>
		<category><![CDATA[在线查询]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/2008/03/23889.html</guid>
		<description><![CDATA[Lucene本身并不支持更新, 所以只能选择先删除再新增记录。 lucene本身支持两种删除模式 1,DeleteDocument(int docNum) //指定文档标号自动删除 2,DeleteDocuments(Term term) //删除所有出现该term的文档 一般使用的是第二种 IndexReader reader = IndexReader.Open(path)); int count=reader.DeleteDocuments(new Term(&#8220;FieldName&#8221;,&#8221;Txt&#8221;)); Lucene的删除也就是一次搜索的过程. 备注：需要匹配删除的字段存储时不要进切词. 我的疑问： 1. 用以上方式就行索引更新，特别是当更新的数据非常多的时候，如何保证更新的同时提高查询服务？optimize（）数据俩一大还是挺慢的，不能在短时间完成。 是否可以在更新的数据量很大时，直接生成新索引，然后更换查询的索引。 2. 用lucene实现在线查询，该如何做？ 是否可以在内存中用生成索引, 同时查两个索引。等内存中索引到一定大小时，写到硬盘或就行合并，但这样索引合并的代价比较大。 知道的朋友，劳烦告诉我，3qs！ &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 该问题已经解决，其实Lucene中自带此功能， 如果要实现在线索引感觉还是在内存中保持Index比较方便。 索引修改后，要想保持查询同步，只需使用reopen（）函数重新打开索引即可，这个函数实现速度较快。 下面是我的应用中一段程序，对应凑合看就行。 public void refreshSearcher() { synchronized (searcher) { try { int preNum = searcher.maxDoc(); IndexSearcher[] searchers = new IndexSearcher[ireaderList .size()]; ArrayList&#60;IndexReader&#62; list = new <a href='http://blog.zye.me/2011/06/23889.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<p>Lucene本身并不支持更新, 所以只能选择先删除再新增记录。<br />
 lucene本身支持两种删除模式<br />
 <span style="font-family: 'Courier New';">1,DeleteDocument(int docNum) //指定文档标号自动删除</span><br />
 <span style="font-family: 'Courier New';">2,DeleteDocuments(Term term) //删除所有出现该term的文档<br />
 一般使用的是第二种<br />
 </span><span style="font-family: 'Courier New';"> IndexReader reader = IndexReader.Open(path));<br />
 int count=reader.DeleteDocuments(new Term(&#8220;FieldName&#8221;,&#8221;Txt&#8221;)); </span></p>
<p><span style="font-family: 'Courier New';"> </span></p>
<p><span style="font-family: 'Courier New';">Lucene的删除也就是一次搜索的过程.<br />
 备注：需要匹配删除的字段存储时不要进切词.</span></p>
<p><strong><span style="font-family: 'Courier New';">我的疑问：</span></strong></p>
<p><span style="font-family: 'Courier New';">1. 用以上方式就行索引更新，特别是当更新的数据非常多的时候，如何保证更新的同时提高查询服务？optimize（）数据俩一大还是挺慢的，不能在短时间完成。 </span></p>
<p><span style="font-family: 'Courier New';">是否可以在更新的数据量很大时，直接生成新索引，然后更换查询的索引。</span></p>
<p><span style="font-family: 'Courier New';">2. 用lucene实现在线查询，该如何做？ 是否可以在内存中用生成索引, 同时查两个索引。等内存中索引到一定大小时，写到硬盘或就行合并，但这样索引合并的代价比较大。</span></p>
<p><br class="spacer_" /></p>
<p><span style="font-family: 'Courier New';">知道的朋友，劳烦告诉我，3qs！</span></p>
<p><br class="spacer_" /></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>该问题已经解决，其实Lucene中自带此功能， 如果要实现在线索引感觉还是在内存中保持Index比较方便。</p>
<p>索引修改后，要想保持查询同步，只需使用reopen（）函数重新打开索引即可，这个函数实现速度较快。</p>
<p>下面是我的应用中一段程序，对应凑合看就行。</p>
<p><br class="spacer_" /></p>
<p><span style="white-space: pre;"> </span>public void refreshSearcher() {</p>
<p><span style="white-space: pre;"> </span>synchronized (searcher) {</p>
<p><span style="white-space: pre;"> </span>try {</p>
<p><span style="white-space: pre;"> </span>int preNum = searcher.maxDoc();</p>
<p><span style="white-space: pre;"> </span>IndexSearcher[] searchers = new IndexSearcher[ireaderList</p>
<p><span style="white-space: pre;"> </span>.size()];</p>
<p><span style="white-space: pre;"> </span>ArrayList&lt;IndexReader&gt; list = new ArrayList&lt;IndexReader&gt;();</p>
<p><span style="white-space: pre;"> </span>for (int i = 0; i &lt; ireaderList.size(); i++) {</p>
<p><span style="white-space: pre;"> </span>IndexReader treader = ireaderList.get(i).reopen();</p>
<p><span style="white-space: pre;"> </span>searchers[i] = new IndexSearcher(treader);</p>
<p><span style="white-space: pre;"> </span>list.add(treader);</p>
<p><span style="white-space: pre;"> </span>}</p>
<p><span style="white-space: pre;"> </span>searcher = new MultiSearcher(searchers);</p>
<p><span style="white-space: pre;"> </span>ireaderList = list;</p>
<p><span style="white-space: pre;"> </span>int aftNum = searcher.maxDoc();</p>
<p><span style="white-space: pre;"> </span>LOG.info(&#8220;before refresh: &#8221; + preNum + &#8221; after refresh: &#8220;</p>
<p><span style="white-space: pre;"> </span>+ aftNum);</p>
<p><span style="white-space: pre;"> </span>} catch (CorruptIndexException e) {</p>
<p><span style="white-space: pre;"> </span>// TODO Auto-generated catch block</p>
<p><span style="white-space: pre;"> </span>e.printStackTrace();</p>
<p><span style="white-space: pre;"> </span>} catch (IOException e) {</p>
<p><span style="white-space: pre;"> </span>// TODO Auto-generated catch block</p>
<p><span style="white-space: pre;"> </span>e.printStackTrace();</p>
<p><span style="white-space: pre;"> </span>}</p>
<p><span style="white-space: pre;"> </span>}</p>
<p><span style="white-space: pre;"> </span>}</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/06/23889.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>如何在lucene检索结果中再检索？Java</title>
		<link>http://blog.zye.me/2011/06/20849.html</link>
		<comments>http://blog.zye.me/2011/06/20849.html#comments</comments>
		<pubDate>Tue, 07 Jun 2011 14:29:38 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[二次检索]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/2008/03/20849.html</guid>
		<description><![CDATA[如何在lucene检索结果中再检索？Java 1. 最容易实现的，把第一次和再次检索的关键词用BooleanQuery 并起来，这种简单方便，容易理解 2. 利用lucene的Filter，具体可以查看lucene的api中的org.apache.lucene.search.CachingWrapperFilter，它可以缓存上次的搜索结果，从而实现在结果中的搜索. 但注意的是DateFilter 和WrapperFilter 没有缓存功能，用CachingWrapperFilter包装废缓存过滤器（noncaching filter）才能实现缓存功能。下面是lucene in action 中的一段实例代码， 下面是简单的测试程序。当然在实际应用中可以做得比较复杂。 public void testCachingWrapper(){ Date jan1 = parserDate(&#8220;2004 jan 01&#8243;); Date dec31 = parserDate(&#8220;2004 Dec 31&#8243;); DateFilter dateFilter = new DateFilter(&#8220;modified&#8221;, jan1, dec31); cachingFilter = new CachingWrapperFilter(dateFilter); Hits hits = searcher.search(allbooks, cachingFilter); }]]></description>
			<content:encoded><![CDATA[<p>如何在lucene检索结果中再检索？Java</p>
<p>1. 最容易实现的，把第一次和再次检索的关键词用BooleanQuery 并起来，这种简单方便，容易理解</p>
<p>2. 利用lucene的Filter，具体可以查看lucene的api中的org.apache.lucene.search.CachingWrapperFilter，它可以缓存上次的搜索结果，从而实现在结果中的搜索. 但注意的是DateFilter 和WrapperFilter 没有缓存功能，用CachingWrapperFilter包装废缓存过滤器（noncaching filter）才能实现缓存功能。下面是lucene in action 中的一段实例代码，<br />
下面是简单的测试程序。当然在实际应用中可以做得比较复杂。</p>
<p>public void testCachingWrapper(){</p>
<p>Date jan1 = parserDate(&#8220;2004 jan 01&#8243;);</p>
<p>Date dec31 = parserDate(&#8220;2004 Dec 31&#8243;);</p>
<p>DateFilter dateFilter = new DateFilter(&#8220;modified&#8221;, jan1, dec31);</p>
<p>cachingFilter = new CachingWrapperFilter(dateFilter);</p>
<p>Hits hits = searcher.search(allbooks, cachingFilter);</p>
<p>}</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/06/20849.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Indri中的动态文档索引技术（转）</title>
		<link>http://blog.zye.me/2011/06/5150.html</link>
		<comments>http://blog.zye.me/2011/06/5150.html#comments</comments>
		<pubDate>Sat, 04 Jun 2011 02:29:04 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[indexing]]></category>
		<category><![CDATA[Indri]]></category>
		<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[信息检索]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/2007/02/5150.html</guid>
		<description><![CDATA[Indri中的动态文档索引技术 戴维译 摘要： Indri 动态文档索引的实现技术，支持在更新索引的同时处理用户在线查询请求。 文本搜索引擎曾被设计为针对固定的文档集合进行查询，对不少应用来说，这种机制工作得很好，然而对于诸于新闻，财经和桌面搜索而言，需要的是高效、经常性的更新索引。 以往支持动态文档集合的研究主要围绕增量索引方法，增量系统通过往已有的索引中追加大的文档集合来优化索引性能，但是不允许在增量索引的同时处理用户查询。 与以往的增量系统不同，Indri搜索引擎的最新版本支持动态文档集合，不需要通过加大文档集合大小来获取索引性能，同时Indri支持索引和查询的并发，允许用户在增量索引的同时进行查询。 1．介绍 尽管全文索引技术已经出现了几十年之久，但是直到互联网的出现，它才真正得到普及。现在，几乎每个互联网使用者都是搜索引擎用户，全文搜索技术被广泛地用于各种应用领域，如Web搜索，新闻搜索，以及时下流行的桌面搜索等。 搜索桌面（或硬盘）文件和e-mails对大多数信息检索系统而言是一个新的挑战。用户期望他们的e-mails即到即索引，文件在保存到磁盘的顷刻便被索引好。永远不要期望桌面搜索用户能忍受由于索引更新所带来的存储消耗。不管是祈祷还是咒骂，用户看到了全文搜索的好处，更为普遍的事实是人们越来越发现自己离不开搜索。 然后，更新一个全文索引是一个耗时的过程，现在的搜索引擎通过建立大量文档的倒排索引表来创建索引，一个倒排索引项包含一篇文章的无重复词语列表，以及这些词语的附加信息（如词语在文档中的位置，词性等）。一篇短小的包含100个左右不重复词语的文章，索引的时候需要更新100个倒排索引项。如果这些倒排索引是存储在磁盘上的话，更新操作将需要100次的磁盘寻址，在现有的硬件配置下，这需要一秒或更长的时间。当然，长文章耗时也越长。另外，一个随之而来的问题是，在更新索引的时候，搜索引擎是该让用户等待更新完成还是继续处理用户的查询请求呢？ Indri搜索引擎新的版本突破了上述限制。作为Lemur[1]项目研发的一部分，Indri支持结构化查询语言，采用语言建模方法[2]，同时为了满足问答系统的需要，Indri还支持对结构化文档不同域进行查询。Indri第一个版本没有加入对增量索引的支持，但在最新的版本中允许对单个文档进行真正的实时索引。 一个信息检索系统要处理动态的文档集合，需要解决三个关键问题。首先，要能快速地添加和删除集合中的文档，这里的快速取决于一个桌面搜索用户愿意花多长的时间来等待索引的完成，也就是说对于单个文档而言响应应该是瞬时的。其次，系统要允许查询在任何时候都能得到响应，即使是在新文档添加进文档集合的同时。另外，系统要实用，在索引和检索性能上比不支持动态文档集合的检索系统更具有竞争性。 在Indri最新版本中，我们通过引入如下设计原则来实现上述目标： 内存结构-为了避免读写磁盘，尽可能长地把数据调入内存。 加锁机制-系统在尽可能小的时间段内对数据进行加锁互斥。 只读结构-为了减少对互斥锁的依赖，系统引入只读数据结构。 后台I/O-系统采用后台线程来和低速设备进行交互，以提高索引操作性能。 多版本结构-如果一个耗时长的操作需要获取可能已经更新的数据，系统将维持此数据的多个版本来减少互斥锁的使用。 上述原则的具体运用将在文章后续部分进行详细介绍。 2．相关工作 数据库研究团体已经花了几十年时间来研究数据获取并发技术。Ramakrishnan和Gehrke提出了一个通用的数据库原则[3]，Gray和Reuter则进一步深入探讨了事务处理系统[4]。 尽管访问文档数据和访问数据库数据所遇到的问题类似，但它们之间仍然存在着显著的差别。在数据库系统中，用户特别关心的是数据的原子性，有一个经典得例子，一位银行顾客把d美元钱从一个储蓄户头转到一个支票账号，如果d美元先从储蓄户头减掉，那么这位顾客的总钱数就少了d美元；如果先往支票账号加入d美元，那么他的钱就凭空多了d美元。不管怎么样，在第一个账号改变的同时就会出现金额不一致的情况。于是，数据库中事件并发研究的一个主要任务就是确保数据库的一致性，即使是在系统操作失败的情况下也要如此。 在我们系统中可以确保文档的插入和删除是原子操作，没有用户会看到一个文档是部分被删除或者插入的。但我们不允许多个文档的插入和删除是原子的。既然文档之间很少像数据库中记录那样相互依赖，这就不会成为我们系统的主要限制。 虽然有上述的差异，我们仍然可以从数据库中得到借鉴。异步I/O和互斥技术被现代数据库系统广泛使用，我们也在索引系统中采用类似的多版本并发技术[5]。 信息检索研究团体没有忽略动态文档集合，他们把研究重点放在了增量系统上，这种系统通过一次性添加大批量文档到已有索引上来代替单个文档的高效添加。这种研究并没有考虑当更新索引时系统能否继续处理查询的问题。 Brown，Callan和Croft[6]研究了一种高效增量索引的方法。他们区别对待小于8k和大于8k的索引，当一个小的索引需要增大时，它将被拷贝到一个大的连续的倒排索引文件中。然而对于一个大的索引项则不需要移动，只需添加一个前向指针到新的存储段(segment)里面。这也使得倒排索引可以通过倒排索引文件串连起来。他们发现，当在7个簇中创建一个索引的时候，查询性能降低了6%。使用小的簇时代价偏高，在他们的模型中索引每簇大小为64M的文本所花的时间是索引每簇大小为1M的文本的8倍。 在最近的研究中，Lester，Zobel以及Williams[7]比较了三种索引策略：占位(in-place)，合并（re-merge）以及重构（rebuild）。除了没有对连接链表进行优化外，in-place策略类似Brown所采用的方法。所有的倒排索引连续存放，如果没有足够的空间写入新数据的时候，已有数据必须被拷贝到别的地方。在re-merge策略中，新的文档簇被创建到单独的索引中，然后和已经存在的索引进行合并。rebuild策略则对已经构建的索引弃之不顾，在原始文档的基础上重新构建索引。他们研究发现，re-merge策略是最高效的更新索引的方法。但是，他们没有像Brow，Callan和Croft所作的那样，对预分配(pre-allocation)策略之间和处理大索引策略之间的差异进行比较。 Lester，Zobel和Williams提到，在使用最小的文档簇(10个文档)的情况下，表现最好的索引策略(in-place策略)在大约7秒的时间内更新了1G的索引。相对于别的策略而言，这已经是非常快了，但是，对于单个文档的索引更新来说，这并不是个理想的策略。 本文描述的方法类似Lucene搜索引擎，正常情况下，就像传统的批量索引一样，Lucene以分段(segment)的方式把数据写入磁盘。一旦数据被写入段中，他们就可以被查询到，并且不需要进行段的合并。这和Brown，Callan和Croft的连接链表方法有点类似，只是，把数据写入簇(batch)中需要更多的开销。为了获得更好的性能，许多文档必须被写入磁盘的一个簇(batch)中。 如果需要快速的响应，Lucene提供一个RAMDirectory类在内存中创建索引。添加一个文档到RAMDirectory很快，因为不需要进行磁盘I/O操作。一个文档一加入RAMDirectory便可以通过一个叫做IndexWriter的对象进行查询。这也解决了文档簇对于小文档集合的问题。然而，对于大小大于机器内存的文档集合，内存索引方式将不再可用，数据必须被写入磁盘，并根据用户对索引数据的定位方式来决定，哪些数据需要驻留内存，哪些数据应该写入磁盘。 在我们的工作中，当需要对文档进行快速存取时，Indri使用内存索引而不是批量索引。当需要同时处理查询请求的时候，Indri会立即决定什么时候该合并索引，而什么时候该把数据写入磁盘。 3．策略 3.1 内存结构 Indri采用两种类型的索引：内存索引和磁盘索引。内存索引驻留内存，而磁盘索引则存储在磁盘上。两种索引都能够处理查询，但只有内存索引能添加新的文档。Indri的磁盘索引结构是固定不变的，可以删除，但不能修改。 大多数的信息检索系统在磁盘上为所有文档创建一个单独的索引，而Indri在创建索引的同时还会生成不同用途的索引文件，这里我们使用&#8221;索引库repository&#8220;来指代一个文档集合对应的索引及其相关数据结构。 当在文本集合上创建索引库的时候，Indri把当前文档索引到活动的内存索引中。只要一个索引库处在打开写模式，就存在一个活动的内存索引准备接收文档。对于小的文档集合，索引数据直到索引库需要关闭的时候才写入磁盘。数据写入的同时，一个新的内存索引被创建，作为新的活动索引使用。 用上述方法构建一个索引相当于多个检索系统同时工作。文档通常一次性加入内存索引结构，只有当达到了内存限制时才被写入磁盘。在批量系统中，磁盘和内存结构不能独立工作，索引需要经过后续处理才能用于系统查询。 因为构建许多小的索引比构建一个单独的大索引更加高效，所以在磁盘上维持许多单独的小索引要更加有利。大的索引只有在需要合并小索引的时候才会出现，为了尽可能快地向系统中添加文档，简单地生成小的索引更有优势[7]。 然而，从一个单独的大索引中查询比在众多小索引中寻觅要快的多，主要的原因在于磁盘寻址时间，查询所需要的大量的磁盘寻址和索引数目之间具有线性的关系。因此，重查询负载的情况下，Indri将通过合并索引的方式来减少磁盘索引数目。 3.2 加锁机制 为了满足系统快速响应的需求，Indri必须快速地处理查询和加入文档。Indri以前的版本已经是一个有效的批量系统，可以很容易地对数据结构进行加锁，但是这也容易使查询或文档插入被长时间阻塞。为了保持快速的响应，必须确保系统中互斥锁的使用是在很短的时间内。 我们通过只允许活动内存索引可变来减少互斥锁的加锁时间。除了一些caches外，内存索引是系统加锁时需要处理的唯一结构。而内存索引的大小是受可用内存大小限制的，它的全部内容都驻留在内存中，即算对于复杂的查询，内存索引的响应也相当快。当然，加锁时间也可以通过减少内存限制来降低。 互斥锁也需要向内存索引中添加新的文档，为了减少加锁时间，我们确保每个文档在上锁前是被解析过的，只有当单个文档被索引的时候才加锁，然后开锁以处理查询。大多数的网页和新闻文档可以在小于1/100秒的时间内索引好，查询处理需要等待的时间相应也就很短。 当处理查询的时候有新文档到来，系统又正好处于加锁状态，此时，系统将禁止磁盘I/O操作。这样可以显著减少主线程在持有互斥锁的情形下任务调度混乱。 3.3 只读结构 为了减少系统在处理大卷数据时互斥锁加锁的时间，我们设法让大部分数据保持不变，对于磁盘数据来说这是基本准则。如果磁盘数据不是只读的，那么线程之间就需要在进行磁盘I/O读写时进行加锁，这可能导致加锁时数据的高度不一致。 既然磁盘数据在写入后便不允许更改，对于读取数据来说就不用加锁。加锁策略让Indri可以充分利用多处理器系统来提高性能。大部分的查询代码路径(query code path)仅仅需要只读锁就可以了，也就是说查询处理是高度并行的，尽管它看起来似乎受到磁盘子系统的并行限制。另外，由于文本解析和索引可以同时进行，索引过程也具有可并行性。 3.4 <a href='http://blog.zye.me/2011/06/5150.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<p style="TEXT-ALIGN: center"><span style="FONT-SIZE: 16pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体">中的动态文档索引技术</span> <span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">戴维</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">译</span></p>
<p style="TEXT-ALIGN: center"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">摘要：</span> <span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">Indri</span> <span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">动态文档索引的实现技术，支持在更新索引的同时处理用户在线查询请求。</span> <span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">文本搜索引擎曾被设计为针对固定的文档集合进行查询，对不少应用来说，这种机制工作得很好，然而对于诸于新闻，财经和桌面搜索而言，需要的是高效、经常性的更新索引。</span> <span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">以往支持动态文档集合的研究主要围绕增量索引方法，增量系统通过往已有的索引中追加大的文档集合来优化索引性能，但是不允许在增量索引的同时处理用户查询。</span> <span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">与以往的增量系统不同，</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">Indri</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">搜索引擎的最新版本支持动态文档集合，不需要通过加大文档集合大小来获取索引性能，同时</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">Indri</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">支持索引和查询的并发，允许用户在增量索引的同时进行查询。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">1</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">．介绍</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">尽管全文索引技术已经出现了几十年之久，但是直到互联网的出现，它才真正得到普及。现在，几乎每个互联网使用者都是搜索引擎用户，全文搜索技术被广泛地用于各种应用领域，如</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Web</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">搜索，新闻搜索，以及时下流行的桌面搜索等。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">搜索桌面（或硬盘）文件和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">e-mails</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">对大多数信息检索系统而言是一个新的挑战。用户期望他们的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">e-mails</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">即到即索引，文件在保存到磁盘的顷刻便被索引好。永远不要期望桌面搜索用户能忍受由于索引更新所带来的存储消耗。不管是祈祷还是咒骂，用户看到了全文搜索的好处，更为普遍的事实是人们越来越发现自己离不开搜索。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">然后，更新一个全文索引是一个耗时的过程，现在的搜索引擎通过建立大量文档的倒排索引表来创建索引，一个倒排索引项包含一篇文章的无重复词语列表，以及这些词语的附加信息（如词语在文档中的位置，词性等）。一篇短小的包含</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">100</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">个左右不重复词语的文章，索引的时候需要更新</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">100</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">个倒排索引项。如果这些倒排索引是存储在磁盘上的话，更新操作将需要</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">100</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">次的磁盘寻址，在现有的硬件配置下，这需要一秒或更长的时间。当然，长文章耗时也越长。另外，一个随之而来的问题是，在更新索引的时候，搜索引擎是该让用户等待更新完成还是继续处理用户的查询请求呢？</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">搜索引擎新的版本突破了上述限制。作为</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Lemur[1]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">项目研发的一部分，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">支持结构化查询语言，采用语言建模方法</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">[2]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，同时为了满足问答系统的需要，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">还支持对结构化文档不同域进行查询。</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">第一个版本没有加入对增量索引的支持，但在最新的版本中允许对单个文档进行真正的实时索引。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">一个信息检索系统要处理动态的文档集合，需要解决三个关键问题。首先，要能快速地添加和删除集合中的文档，这里的快速取决于一个桌面搜索用户愿意花多长的时间来等待索引的完成，也就是说对于单个文档而言响应应该是瞬时的。其次，系统要允许查询在任何时候都能得到响应，即使是在新文档添加进文档集合的同时。另外，系统要实用，在索引和检索性能上比不支持动态文档集合的检索系统更具有竞争性。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">最新版本中，我们通过引入如下设计原则来实现上述目标：</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">内存结构-为了避免读写磁盘，尽可能长地把数据调入内存。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">加锁机制-系统在尽可能小的时间段内对数据进行加锁互斥。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">只读结构-为了减少对互斥锁的依赖，系统引入只读数据结构。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">后台</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">-系统采用后台线程来和低速设备进行交互，以提高索引操作性能。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">多版本结构-如果一个耗时长的操作需要获取可能已经更新的数据，系统将维持此数据的多个版本来减少互斥锁的使用。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">上述原则的具体运用将在文章后续部分进行详细介绍。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">2</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">．相关工作</span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">数据库研究团体已经花了几十年时间来研究数据获取并发技术。</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">Ramakrishnan</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">Gehrke</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">提出了一个通用的数据库原则</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">[3]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">Gray</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">Reuter</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">则进一步深入探讨了事务处理系统</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">[4]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。</span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">尽管访问文档数据和访问数据库数据所遇到的问题类似，但它们之间仍然存在着显著的差别。在数据库系统中，用户特别关心的是数据的原子性，有一个经典得例子，一位银行顾客把</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">d</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">美元钱从一个储蓄户头转到一个支票账号，如果</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">d</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">美元先从储蓄户头减掉，那么这位顾客的总钱数就少了</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">d</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">美元；如果先往支票账号加入</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">d</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">美元，那么他的钱就凭空多了</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">d</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">美元。不管怎么样，在第一个账号改变的同时就会出现金额不一致的情况。于是，数据库中事件并发研究的一个主要任务就是确保数据库的一致性，即使是在系统操作失败的情况下也要如此。</span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在我们系统中可以确保文档的插入和删除是原子操作，没有用户会看到一个文档是部分被删除或者插入的。但我们不允许多个文档的插入和删除是原子的。既然文档之间很少像数据库中记录那样相互依赖，这就不会成为我们系统的主要限制。</span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">虽然有上述的差异，我们仍然可以从数据库中得到借鉴。异步</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和互斥技术被现代数据库系统广泛使用，我们也在索引系统中采用类似的多版本并发技术</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: CMR10"><span style="FONT-FAMILY: Times New Roman">[5]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。</span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">信息检索研究团体没有忽略动态文档集合，他们把研究重点放在了增量系统上，这种系统通过一次性添加大批量文档到已有索引上来代替单个文档的高效添加。这种研究并没有考虑当更新索引时系统能否继续处理查询的问题。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Brown</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Callan</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Croft[6]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">研究了一种高效增量索引的方法。他们区别对待小于</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">8k</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和大于</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">8k</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的索引，当一个小的索引需要增大时，它将被拷贝到一个大的连续的倒排索引文件中。然而对于一个大的索引项则不需要移动，只需添加一个前向指针到新的存储段</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(segment)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">里面。这也使得倒排索引可以通过倒排索引文件串连起来。他们发现，当在</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">7</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">个簇中创建一个索引的时候，查询性能降低了</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">6%</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。使用小的簇时代价偏高，在他们的模型中索引每簇大小为</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">64M</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的文本所花的时间是索引每簇大小为</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">1M</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的文本的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">8</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">倍。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在最近的研究中，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Lester</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Zobel</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">以及</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Williams[7]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">比较了三种索引策略：占位</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(in-place)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，合并（</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">re-merge</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">）以及重构（</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">rebuild</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">）。除了没有对连接链表进行优化外，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">in-place</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">策略类似</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Brown</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">所采用的方法。所有的倒排索引连续存放，如果没有足够的空间写入新数据的时候，已有数据必须被拷贝到别的地方。在</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">re-merge</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">策略中，新的文档簇被创建到单独的索引中，然后和已经存在的索引进行合并。</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">rebuild</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">策略则对已经构建的索引弃之不顾，在原始文档的基础上重新构建索引。他们研究发现，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">re-merge</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">策略是最高效的更新索引的方法。但是，他们没有像</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Brow</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Callan</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Croft</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">所作的那样，对预分配</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(pre-allocation)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">策略之间和处理大索引策略之间的差异进行比较。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Lester</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Zobel</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Williams</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">提到，在使用最小的文档簇</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(10</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">个文档</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的情况下，表现最好的索引策略</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(in-place</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">策略</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在大约</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">7</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">秒的时间内更新了</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">1G</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的索引。相对于别的策略而言，这已经是非常快了，但是，对于单个文档的索引更新来说，这并不是个理想的策略。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">本文描述的方法类似</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Lucene</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">搜索引擎，正常情况下，就像传统的批量索引一样，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Lucene</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">以分段</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(segment)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的方式把数据写入磁盘。一旦数据被写入段中，他们就可以被查询到，并且不需要进行段的合并。这和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Brown</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Callan</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Croft</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的连接链表方法有点类似，只是，把数据写入簇</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(batch)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">中需要更多的开销</span><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: 宋体">。</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">为了获得更好的性能，许多文档必须被写入磁盘的一个簇</span></span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(batch)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">中。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">如果需要快速的响应，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Lucene</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">提供一个</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RAMDirectory</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">类在内存中创建索引。添加一个文档到</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RAMDirectory</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">很快，因为不需要进行磁盘</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">操作。一个文档一加入</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RAMDirectory</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">便可以通过一个叫做</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">IndexWriter</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的对象进行查询。这也解决了文档簇对于小文档集合的问题。然而，对于大小大于机器内存的文档集合，内存索引方式将不再可用，数据必须被写入磁盘，并根据用户对索引数据的定位方式来决定，哪些数据需要驻留内存，哪些数据应该写入磁盘。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在我们的工作中，当需要对文档进行快速存取时，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">使用内存索引而不是批量索引。当需要同时处理查询请求的时候，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">会立即决定什么时候该合并索引，而什么时候该把数据写入磁盘。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">3</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">．策略</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">3.1</span></span> <span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">内存结构</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">采用两种类型的索引：内存索引和磁盘索引。内存索引驻留内存，而磁盘索引则存储在磁盘上。两种索引都能够处理查询，但只有内存索引能添加新的文档。</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的磁盘索引结构是固定不变的，可以删除，但不能修改。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">大多数的信息检索系统在磁盘上为所有文档创建一个单独的索引，而</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在创建索引的同时还会生成不同用途的索引文件，这里我们使用&#8221;索引库</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">repository</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">&#8220;来指代一个文档集合对应的索引及其相关数据结构。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">当在文本集合上创建索引库的时候，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">把当前文档索引到活动的内存索引中。只要一个索引库处在打开写模式，就存在一个活动的内存索引准备接收文档。对于小的文档集合，索引数据直到索引库需要关闭的时候才写入磁盘。数据写入的同时，一个新的内存索引被创建，作为新的活动索引使用。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">用上述方法构建一个索引相当于多个检索系统同时工作。文档通常一次性加入内存索引结构，只有当达到了内存限制时才被写入磁盘。在批量系统中，磁盘和内存结构不能独立工作，索引需要经过后续处理才能用于系统查询。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">因为构建许多小的索引比构建一个单独的大索引更加高效，所以在磁盘上维持许多单独的小索引要更加有利。大的索引只有在需要合并小索引的时候才会出现，为了尽可能快地向系统中添加文档，简单地生成小的索引更有优势</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">[7]</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">然而，从一个单独的大索引中查询比在众多小索引中寻觅要快的多，主要的原因在于磁盘寻址时间，查询所需要的大量的磁盘寻址和索引数目之间具有线性的关系。因此，重查询负载的情况下，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">将通过合并索引的方式来减少磁盘索引数目。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">3.2</span></span> <span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">加锁机制</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">为了满足系统快速响应的需求，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">必须快速地处理查询和加入文档。</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">以前的版本已经是一个有效的批量系统，可以很容易地对数据结构进行加锁，但是这也容易使查询或文档插入被长时间阻塞。为了保持快速的响应，必须确保系统中互斥锁的使用是在很短的时间内。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">我们通过只允许活动内存索引可变来减少互斥锁的加锁时间。除了一些</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">caches</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">外，内存索引是系统加锁时需要处理的唯一结构。而内存索引的大小是受可用内存大小限制的，它的全部内容都驻留在内存中，即算对于复杂的查询，内存索引的响应也相当快。当然，加锁时间也可以通过减少内存限制来降低。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">互斥锁也需要向内存索引中添加新的文档，为了减少加锁时间，我们确保每个文档在上锁前是被解析过的，只有当单个文档被索引的时候才加锁，然后开锁以处理查询。大多数的网页和新闻文档可以在小于</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">1/100</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">秒的时间内索引好，查询处理需要等待的时间相应也就很短。</span></span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">当处理查询的时候有新文档到来，系统又正好处于加锁状态，此时，系统将禁止磁盘</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">操作。这样可以显著减少主线程在持有互斥锁的情形下任务调度混乱。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">3.3</span></span> <span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">只读结构</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">为了减少系统在处理大卷数据时互斥锁加锁的时间，我们设法让大部分数据保持不变，对于磁盘数据来说这是基本准则。如果磁盘数据不是只读的，那么线程之间就需要在进行磁盘</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">读写时进行加锁，这可能导致加锁时数据的高度不一致。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">既然磁盘数据在写入后便不允许更改，对于读取数据来说就不用加锁。加锁策略让</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">可以充分利用多处理器系统来提高性能。大部分的查询代码路径</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">(query code path)</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">仅仅需要只读锁就可以了，也就是说查询处理是高度并行的，尽管它看起来似乎受到磁盘子系统的并行限制。另外，由于文本解析和索引可以同时进行，索引过程也具有可并行性。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">3.4</span></span> <span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">后台</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">如果</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">操作</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在查询和索引时候不能执行，就需要引入异步</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I /O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">通过一直运行如下两个线程来实现异步</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I/O</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">操作：</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RepositoryMaintenanceThread</span></span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RepositoryLoadThread</span></span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">这两个线程和特定的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Repository</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">相关联，如果多于一个</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Repository</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">被打开，每个</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">repository</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">均需要对应的这样一对线程相关联。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> RepositoryLoadThread</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">执行两项任务。一是为查询和新加入的文档载入统计数据，这种数据载入和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Unix</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">进程载入有些类似，线程标明在过去的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">1</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">5</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">以及</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">15</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">分钟内处理的查询和添加的文档数目，以帮助系统决定何时该把内存数据写入磁盘。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> RepositoryLoadThread</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的另一项任务是检查系统的内存使用情况。如果系统使用的内存超过用户限制的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">25</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">％，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RepositoryLoadThread</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">将挂起所有文档索引线程直到内存占用降下来为止，这可以防止系统在大批量文档加入的时候崩溃。对于多数可能的实时程序，如新闻播报，系统的运行决不能超出内存限制。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> RepositoryMaintenanceThread</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">把索引写入磁盘，它是唯一能把索引数据写入磁盘，并可以从磁盘删除数据的线程，在这个线程中不需要复杂的加锁机制。该线程每分钟激活</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">5</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">次以检查系统当前内存占用量，如果系统使用了过多的内存，它就开始把内存数据写入磁盘。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">如上所述，创建新的磁盘索引并不是总有好处，因为许多小的磁盘索引结构对于大的磁盘索引结构来说需要更多的查询时间。为此，索引库维护线程</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">RepositoryMaintenaceThread</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在写磁盘之前检查查询和文档的载入情况，如果查询相对于文档载入量更大的话，维护线程将进行索引合并而不是往磁盘中写入一个新的索引。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">3.5</span></span> <span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">多版本结构</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Indri Repository</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">可能包含多个索引，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">维护一个称之为</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的结构，这个结构持有指向当前索引库中所有索引的指针。索引数据在两种情况下被写入磁盘：</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">内存索引</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">MemoryIndex</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">已经达到了它的内存限制；</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">存在过多的内存索引</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">MemoryIndex</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，他们需要进行合并。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在上述两种情况下，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">把数据写入磁盘，这些数据可能已经以别的形式存在于系统中，因此</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">需要进行修改以反映数据是否被删除。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">一个解决办法是对</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">结构的读写进行互斥，然而，这种方法也可能导致在重负载情况下系统性能低下。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">考虑</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是运行在一个并行系统中，并且用户正在进行复杂的查询，这些查询每个都需要</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">10</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">分钟的处理时间。假如用户在两个独立的线程中提交查询，系统中就总是有两份同样的查询在运行。如果这些查询以</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">5</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">分钟为时间片轮流执行，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">A</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">线程分别在一个小时的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">0</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">10</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">20</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">30</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">40</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">50</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">分钟时开始运行，而</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">线程则分别在</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">5</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">15</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">25</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">35</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">45</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">55</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">分钟时开始执行。在一个小时开始的时候，我们对</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的写进行加锁，当在</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">线程处理完一个查询前，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">A</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">线程不允许处理它的下一个的查询，于是一个处理器将有</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">5</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">分钟的空闲时间，这是我们不希望看到的。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">为了避免上述情况的出现，</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在同一时间维持多个</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">结构，所有新的任务（如新的查询，文档的加入）使用新的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">结构，而旧的任务继续使用旧的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">结构，当没有用户需要使用</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的时候，它将被删除。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在上面的例子中，这意味着线程</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在使用它旧的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">结构完成它的查询处理的同时，线程</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">A</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">使用新的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">结构开始处理它的下一个查询。当线程</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">处理完毕当前查询，旧的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">index_state</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">将不在有用户使用，从而被删除掉。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">4</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">．删除文档</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">支持删除标记。删除标记是一种弱删除方式，只是简单地隐藏文档对于用户的可见性，而不是真正的删除。文档对应的索引数据并不会真正从倒排链表，有向链表或者压缩集合中删除掉，也就是说文档中词语的计数仍然保留在语料统计数据库中。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">假设我们有一个文档集合</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">A</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，以及它的一个子集</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，首先创建</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">A</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的索引</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">，然后从</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">中删除</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">对应的索引。我们只是通过把文档集合</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">A</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">－</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">添加到</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I&#8217;</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">来创建一个相似的索引</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I&#8217;</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。由于包含了文档集</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">B</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的数据，索引</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">比索引</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I&#8217;</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">需要占用更多的磁盘空间。进一步，因为</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">和</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">I&#8217;</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">对应的语料统计库稍有差别，当在这两个索引上进行查询时，查询结果也会有所不同。基于如上原因，当使用</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">进行搜索的时候要谨慎地使用文档删除特性。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">尽管实际应用中，删除是个很有用的特性，但单纯的删除用处不大。删除往往被用于进行文档更新（删除旧的版本，插入新的版本）。对于桌面搜索或新闻搜索而言，经常需要更新已有文档的错误（或者过时）版本，这就显得尤为重要。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">我们采用一个简单的位图来标记文档的删除，当一个文档需要删除，就为其设置对应的比特位，任何不在位图中的比特位均假设没有被设置。因此，如果没有文档被删除，位图文件将是一个空文件。这个文件会一直扩充直到最后一个比特位被设置为非</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">0</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">查询时，每个文档均要在打分前对照位图进行检查，只有没有被标记为删除的文档才能进行查询计分。</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">5</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">．总结</span></p>
<p><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman"> Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">现在可以在小于</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">1</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">秒的短时间片内完成文档索引并立即用于查询，这使得高速、并发访问新索引的文档所付出的代价足够小，以至</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">不需要采用特殊的批量和增量模式。</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Indri</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">可以每小时索引约</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">15G</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">的</span><span style="FONT-SIZE: 12pt"><span style="FONT-FAMILY: Times New Roman">Web</span></span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">数据，包括压缩和存储每个原始文档。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在这种性能下，我们已经实现了适合新闻过滤以及桌面搜索应用的检索系统，我们相信这是第一个具有如此高性能的开源系统。</span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><em>Copyright@戴维 2005.8 于北京</em></span></p>
<p><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">参考文献：</span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[1] Trevor Strohman, Donald Metzler, Howard Turtle, and W. Bruce Croft,</span> <span style="FONT-FAMILY: CMTI8">Indri: A language model-based serach engine for complex queries</span><span style="FONT-FAMILY: CMR8">, IA 2005: Proceedings of the 2nd International Conference on Intelligence Analysis (to appear), 2005.</span></span></span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[2] Donald Metzler, Victor Lavrenko, and W. Bruce Croft,</span> <span style="FONT-FAMILY: CMTI8">Formal multiple-bernoulli models for language modeling</span><span style="FONT-FAMILY: CMR8">, Proceedings of ACM SIGIR 2004, 2004, pp. 540-541.</span></span></span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[3] Raghu Ramakrishnan and Johannes Gehrke,</span> <span style="FONT-FAMILY: CMTI8">Database management systems</span><span style="FONT-FAMILY: CMR8">, McGraw-Hill Higher Education, 2000.</span></span></span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[4] Jim Gray and Andreas Reuter,</span> <span style="FONT-FAMILY: CMTI8">Transaction processing: Concepts and techniques</span><span style="FONT-FAMILY: CMR8">, Morgan Kaufmann, 1993.</span></span></span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[5] Philip A. Bernstein and Nathan Goodman,</span> <span style="FONT-FAMILY: CMTI8">Multiversion concurrency control˙theory and algorithms</span><span style="FONT-FAMILY: CMR8">, ACM Trans. Database Syst.</span> <span style="FONT-FAMILY: CMBX8">8</span> <span style="FONT-FAMILY: CMR8">(1983), no. 4, 465-483.</span></span></span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[6] Eric W. Brown,</span> <span style="FONT-FAMILY: CMTI8">Fast evaluation of structured queries for information retrieval</span><span style="FONT-FAMILY: CMR8">,SIGIR&#8217;95:Proceedings of the 18th annual international ACM SIGIRconference onResearch and development in information retrieval (NewYork, NY, USA),ACM Press, 1995, pp. 30-38.</span></span></span></p>
<p style="TEXT-ALIGN: left"><span style="FONT-SIZE: 0.9em"><span style="FONT-FAMILY: Times New Roman"><span style="FONT-FAMILY: CMR8">[7] Nicholas Lester, Justin Zobel, and Hugh E. Williams,</span> <span style="FONT-FAMILY: CMTI8">In-place versus re-build versus re-merge: index maintenance strategies for text retrieval systems</span><span style="FONT-FAMILY: CMR8">, Proceedings of the 27th conference on Australasian computer science, Australian Computer Society, Inc., 2004, pp. 15-23.</span></span></span></p>
<p>相关链接：<br /><a href="http://newhaven.lti.cs.cmu.edu/indri/" rel="nofollow"><span style="COLOR: #0e61b2">http://newhaven.lti.cs.cmu.edu/indri/</span></a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/06/5150.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>lucene util PriorityQueue 实现一优先队列&#8211;搜索引擎算法系列</title>
		<link>http://blog.zye.me/2011/05/3961.html</link>
		<comments>http://blog.zye.me/2011/05/3961.html#comments</comments>
		<pubDate>Sat, 28 May 2011 02:28:12 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[MISC]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[priorityQueue]]></category>
		<category><![CDATA[优先队列]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/articles/lucene-util-priorityqueue-%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%bc%98%e5%85%88%e9%98%9f%e5%88%97-%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e7%ae%97%e6%b3%95%e7%b3%bb%e5%88%97.html</guid>
		<description><![CDATA[优先队列是非常常用的数据结构，搜索引擎获取top k个搜索结果时常用改结构，下面是lucene中的一实例。 PriorityQueue 实现一优先队列框架，实例非常简单，只需实现lessThan(Object a, Object b)方法即可，通过该方法可以控制大优先或小优先。 package org.apache.lucene.search; /** * Copyright 2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the &#8220;License&#8221;); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * <a href='http://blog.zye.me/2011/05/3961.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<p align="left">优先队列是非常常用的数据结构，搜索引擎获取top k个搜索结果时常用改结构，下面是lucene中的一实例。</p>
<p align="left"><span style="color: #000000; font-size: small;">PriorityQueue 实现一优先队列框架，实例非常简单，只需实现lessThan(Object a, Object b)方法即可，通过该方法可以控制大优先或小优先。</span></p>
<p align="left"><span style="font-family: Verdana;"><span style="color: #c0c0c0;"><span style="font-size: x-small;">package org.apache.lucene.search;</span></span></span></p>
<p><span style="font-family: Verdana;"><span style="color: #c0c0c0;"><span style="font-size: x-small;">/**<br />
* Copyright 2004 The Apache Software Foundation<br />
*<br />
* Licensed under the Apache License, Version 2.0 (the &#8220;License&#8221;);<br />
* you may not use this file except in compliance with the License.<br />
* You may obtain a copy of the License at<br />
*<br />
* <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a><br />
*<br />
* Unless required by applicable law or agreed to in writing, software<br />
* distributed under the License is distributed on an &#8220;AS IS&#8221; BASIS,<br />
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<br />
* See the License for the specific language governing permissions and<br />
* limitations under the License.<br />
*/</span></span></span></p>
<p><span style="font-family: Verdana;"><span style="color: #c0c0c0;"><span style="font-size: x-small;">import org.apache.lucene.util.PriorityQueue;</span></span></span></p>
<p><span style="font-family: Verdana;"><span style="color: #c0c0c0;"><span style="font-size: x-small;">final class HitQueue extends PriorityQueue {<br />
HitQueue(int size) {<br />
initialize(size);<br />
}</span></span></span></p>
<p><span style="font-family: Verdana;"><span style="color: #c0c0c0;"><span style="font-size: x-small;">/**<br />
*<br />
* Annotator:yezheng<br />
*/<br />
protected final boolean lessThan(Object a, Object b) {<br />
ScoreDoc hitA = (ScoreDoc)a;<br />
ScoreDoc hitB = (ScoreDoc)b;<br />
if (hitA.score == hitB.score)<br />
return hitA.doc &gt; hitB.doc; // 如果分数相同则按索引先后顺序排序<br />
else<br />
return hitA.score &lt; hitB.score;//否则,按score进行排序<br />
}<br />
}</span></span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/05/3961.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Inside Lucene/超人气搜索引擎学习(2.0)-读取索引</title>
		<link>http://blog.zye.me/2011/05/17463.html</link>
		<comments>http://blog.zye.me/2011/05/17463.html#comments</comments>
		<pubDate>Fri, 27 May 2011 14:30:02 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[indexing]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[信息检索]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/2008/03/17463.html</guid>
		<description><![CDATA[以前搜集的两篇文章，感觉对lucene的研究能稍微深入点的，想研究或者修改源代码的话值得一看。 下面是源地址 Inside Lucene/超人气搜索引擎学习(2.0)-读取索引 Index in Practice 索引: 按图索骥 T ermDoc从哪读取数据,自然是硬盘上已经建好的某个index, 具体说, 是从index中的某个文件读取. 要了解TermDoc读了什么东东,怎么读这些东东,必要时得考察Lucene index的细部结构. T ermDoc是个抽象类,这很好,以后可以创建自己的index结构,建立自己的搜索算法.不过这之前先要了解Lucene是怎么干的,而这个抽象类并不包含这个信息,所以,我们首先要找到TermQuery使用哪个TermDoc实现. 回想一下scorer中的TermDoc从哪里来. public class TermQuery extends Query { private class TermWeight implements Weight { public Scorer scorer(IndexReader reader) throws IOException { TermDocs termDocs = reader.termDocs(term); if (termDocs == null) return null; return new TermScorer(this, termDocs, getSimilarity(searcher), reader.norms(term.field())); } ... <a href='http://blog.zye.me/2011/05/17463.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="postTitle"><em>以前搜集的两篇文章，感觉对lucene的研究能稍微深入点的，想研究或者修改源代码的话值得一看。</em> 下面是源地址</div>
<div class="postTitle"><a href="http://blog.csdn.net/bluemiles/archive/2006/07/24/968433.aspx">Inside Lucene/超人气搜索引擎学习(2.0)-读取索引</a></div>
<div class="postText">
<p><span style="FONT-SIZE: 180%"><strong><span style="FONT-SIZE: 0.75em">Index in Practice 索引: 按图索骥</span></strong></span></p>
<p><span style="FONT-SIZE: 180%"><strong><span style="FONT-SIZE: 1.5em">T</span></strong></span> ermDoc从哪读取数据,自然是硬盘上已经建好的某个index, 具体说, 是从index中的某个文件读取. 要了解TermDoc读了什么东东,怎么读这些东东,必要时得考察Lucene index的细部结构.</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>T</strong></span> ermDoc是个抽象类,这很好,以后可以创建自己的index结构,建立自己的搜索算法.不过这之前先要了解Lucene是怎么干的,而这个抽象类并不包含这个信息,所以,我们首先要找到TermQuery使用哪个TermDoc实现.</p>
<p>回想一下scorer中的TermDoc从哪里来. <br /><code><br />public class TermQuery extends Query { <br />private class TermWeight implements Weight { <br />public Scorer scorer(IndexReader reader) throws IOException { <br />TermDocs termDocs = reader.termDocs(term); </p>
<p>if (termDocs == null) <br />return null; </p>
<p>return new TermScorer(this, termDocs, getSimilarity(searcher), <br />reader.norms(term.field())); <br />} <br />... <br />} <br />... <br />} <br /></code> <br /><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>从</strong></span> 这段代码能找到真正创建TermDocs的那个类: IndexReader <br />用哪个TermDocs实现并不是TermQuery说了算,而是IndexReader的权利. TermQuery得到怎样一个TermDocs, 全由我们传递给TermQuery.weight.scorer()的那个IndexReader决定. 将这个TermDocs定位到指定的Term也完全由IndexReader负责。很遗憾,IndexReader也是抽象类. 想知道内幕?先找找IndexReader实现类。</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>如</strong></span> 果按照用户手册的方法进行搜索, IndexReader的一个静态方法将被调用,它返回我们需要的一个IndexReader实现:SegmentReader, 这是整个查询中用到的reader。</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>顺</strong></span> 藤摸瓜,很容易找到SegmentTermDocs这个类,也就是默认查询中SegmentReader使用的TermDocs,大部分查询结果通过这个类的实例来遍历.现在是时候翻它老底了,看看它怎么遍历数据,这些数据又从哪来. <br />&lt;code&gt; <br />class IndexReader{ <br />public TermDocs termDocs(Term term) throws IOException { <br />TermDocs termDocs = termDocs(); <br />termDocs.seek(term); <br />return termDocs; <br />} <br />&#8230; <br />}</p>
<p>class SegmentReader extends IndexReader{ <br />public final TermDocs termDocs() throws IOException { <br />return new SegmentTermDocs(this); <br />} <br />&#8230; <br />} <br />&lt;/code&gt;</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>从</strong></span> 已经列出的代码中, 能清晰地看到SegmentTermDocs从创建到传递给scorer前进行的一系列动作:</p>
<p>1. SegmentTermDocs构造: 根据parent设定自己的属性 <br />2. IndexReader调用TermDocs.seek(term); 实现类中这一步具体化为SegmentReader调用SegmentTermDocs.seek(term)</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>第</strong></span> 二步中, SegmentTermDocs进行了实际对index文件的读取. 而为了进行这些IO操作, 像前边说的, 必须依靠IndexReader才能完成, 这就是SegmentTermDocs构造是需要参数SegmentReader的原因.</p>
<p>seek (term)方法中SegmentTermDocs利用构造函数的唯一参数IndexReader(也就是创建它的那个reader, 称作parent&#8221;), 在硬盘索引文件中定位指定的term, 读入相关信息:df(包含term的文档数), 以及满足该term的文档集合在index文件中的位置. 这个位置后面, 是创建索引时就已排好的包含这个term的文档信息.</p>
<p>seek 完成后, TermDoc已经准备好读取数据了, 只要一声令下, TermDoc.read方法立刻能把每一篇文档的id和该term在这篇文档中的次数tf. 前面的记载是, scorer对象调用read方法, 尔后遍历其返回的全部文档, 把他们一个个塞到Collector中</p>
<p>精妙繁复的步骤: seek如何完成?</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>这</strong></span> 要涉及索引结构, 现在可以掀开索引文件的一个角, 偷窥下.</p>
<p>tis文件: Term InformationS <br />frq文件: FReQuency</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>必</strong></span> 须注意到IO动作一定是在IndexReader的几个成员中作的, 所有其他类中的IO要么用这些成员的Clone来完成, 要么直接代理给IndexReader. SegmentTermDocs.seek(term)动作是通过IndexReader进行的, SegmentTermDocs把创建他的IndexReader尊为parent, 在seek这种关键时刻利用IndexReader来读取索引数据. 没办法, 索引文件的读取(输入流的建立和定位)全由IndexReader负责. <br /><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>s</strong></span> eek 方法中为了实现定位而利用了IndexReader一个负责Term定位的成员tis, 从他的类名TermInfoReader看就知道有什么用途. 这个tis从.tis文件中找到我们指定的term, 读出一切我们需要的信息: 这个term在多少个文档中出现过(df)/这些文档记录在frq文件的什么位置(起始位置) 等等.</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>得</strong></span> 到这些信息后, TermDoc再自己seek, 这一步很简单, 除了几个加法和赋值, 唯一有特色的是对.frq文件的输入流(FileInputStream)进行seek(), seek()的数量正好是tis返回的&#8221;文档记录在frq文件中的位置&#8221;. 这个流是IndexReader初始化时创建的, 专门从frq文件读数据. IndexReader创建TermDoc时, TermDoc把这个输入流Clone()了一下, 赋给自己的成员. 这一seek()把.frq文件的输入指针定位好, 以后真正需要这个流的地方只有从frq文件读文档数据那一阵. 读数据的过程就发生在屡次提到过的termDoc.read()里, 现在我知道这个方法的实现是SegmentTermDocs.read().</p>
<p>read ()的实现是简单的顺序读取文件流, 具体过程涉及Lucene索引文件的二进制结构, 我不想这时候过多地纠缠. 大致了解termDoc如何定位数据, 心中的疑惑就能解开一半. 关于索引文件结构、各文件的关系、程序如何厘清这些关系, 还值得更多的讨论.</p>
<p><span style="FONT-SIZE: 1.5em; FONT-FAMILY: Courier New"><strong>到</strong></span> 这一步, 结合已熟知的scorer调用TermDoc的方式, 查询过程的基本途径已经隐约呈现出来了.</p>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/05/17463.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lucene 2.3: 索引性能500%提升，一个有关机器学习的项目Mahout 将启动</title>
		<link>http://blog.zye.me/2011/05/3993.html</link>
		<comments>http://blog.zye.me/2011/05/3993.html#comments</comments>
		<pubDate>Fri, 27 May 2011 02:28:20 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Hadoop]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[ML]]></category>
		<category><![CDATA[信息检索]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/articles/lucene-23-%e7%b4%a2%e5%bc%95%e6%80%a7%e8%83%bd500%e6%8f%90%e5%8d%87%ef%bc%8c%e4%b8%80%e4%b8%aa%e6%9c%89%e5%85%b3%e6%9c%ba%e5%99%a8%e5%ad%a6%e4%b9%a0%e7%9a%84%e9%a1%b9%e7%9b%aemahout-%e5%b0%86.html</guid>
		<description><![CDATA[Good news，值得期待，这个有关机器学习的项目是建立在Hadoop平台上，大规模的处理成为可能。 下面这篇文章来源于InfoQ, 后面的翻译也来自于InfoQ http://www.infoq.com/news/2008/01/lucene-23-mahout Posted by Ryan Slobojan on Jan 24, 2008 10:00 PM Lucene 2.3: Large indexing performance improvements, new machine-learning project Community Java Topics Search, Open Source The Apache Lucene project, a high-performance full-featured text search engine library written entirely in Java, released version 2.3 today. InfoQ spoke with committer and Project <a href='http://blog.zye.me/2011/05/3993.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<p style="TEXT-ALIGN: justify">Good news，值得期待，这个有关机器学习的项目是建立在Hadoop平台上，大规模的处理成为可能。</p>
<p style="TEXT-ALIGN: justify">下面这篇文章来源于InfoQ, 后面的翻译也来自于InfoQ</p>
<p><a href="http://www.infoq.com/news/2008/01/lucene-23-mahout" target="_blank" title="http://www.infoq.com/news/2008/01/lucene-23-mahout">http://www.infoq.com/news/2008/01/lucene-23-mahout</a></p>
<p class="info">Posted by <strong>Ryan Slobojan</strong> on Jan 24, 2008 10:00 PM</p>
<dl class="tags2">
<dt class="community">Lucene 2.3: Large indexing performance improvements, new machine-learning project</dt>
<dt class="community">Community</dt>
<dd><a title="java" href="http://www.infoq.com/java;jsessionid=0BC6E7567CAAB374CD0285558145890B" id="1" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" name="java">Java</a></dd>
<dt class="topics">Topics</dt>
<dd><a title="search" href="http://www.infoq.com/search;jsessionid=0BC6E7567CAAB374CD0285558145890B" id="228" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" name="search">Search</a>,</dd>
<dd><a title="opensource" href="http://www.infoq.com/opensource;jsessionid=0BC6E7567CAAB374CD0285558145890B" id="221" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" name="opensource">Open Source</a></dd>
</dl>
<p>The <a href="http://lucene.apache.org/">Apache Lucene</a> project, a high-performance full-featured text search engine library written entirely in Java, released <a href="http://lucene.apache.org/java/docs/index.html#24+January+2008+-+Lucene+Java+2.3.0+available">version 2.3</a> today. InfoQ spoke with committer and <a href="http://lucene.apache.org/who.html">Project Management Committee (PMC) member</a> <a href="http://www.grantingersoll.com/">Grant Ingersoll</a> to learn more about this release and the future plans for Lucene.</p>
<p>Ingersoll indicated that the largest change in this release is a new indexing algorithm, which uses new in-memory models to achieve large speed improvements. According to Ingersoll, simply switching the existing Lucene 2.2 JAR for a Lucene 2.3 JAR resulted in speed-ups of 500% in indexing performance in several tests which were performed. Other changes include:</p>
<ul>
<li><strong>Improved index management</strong> &#8211; long pauses which were occasionally seen during indexing due to merging of internal index files have been eliminated, and other approaches to managing the indexing process are now easy to implement</li>
<li><strong>Object pooling</strong> &#8211; <code>Document</code>, <code>Field</code> and <code>Token</code> instances can now be reused during indexing analysis, which both speeds up analysis and reduces the number of allocations during indexing</li>
<li><strong>IndexReader reopening</strong> &#8211; Reopening an IndexReader to capture the latest changes in an index is now much faster with the new reopen() method, which loads in only those index segments which have changed rather than reloading the entire index</li>
<li><strong>Easier IndexWriter tuning</strong> &#8211; The <code>setMaxBufferedDocs</code> method has been supplanted by the more intuitive <code>setRAMBufferSizeMB</code> method</li>
</ul>
<p>In addition, 2.3 is intended to be a drop-in replacement for 2.2, with no recompilation required. A <a href="http://svn.apache.org/repos/asf/lucene/java/tags/lucene_2_3_0/CHANGES.txt">comprehensive changelog</a> is also available.</p>
<p>Ingersoll also discussed the future plans for Lucene, saying that the next release would be 2.9. The 2.9 release will be a relatively minor, with items being marked as deprecated and other clean-up being performed in preparation for Lucene 3.0. The 3.0 version will be a major release which will involve moving the codebase to JDK 5 as the minimum supported codebase &#8211; the other major features of 3.0 are yet to be determined.</p>
<p>The Lucene community as a whole was also discussed, with Ingersoll indicating that Lucene and <a href="http://lucene.apache.org/solr/">Solr</a> have a strong integration, and that <a href="http://lucene.apache.org/nutch/">Nutch</a>, <a href="http://incubator.apache.org/tika/">Tika</a> and <a href="http://lucene.apache.org/hadoop/">Hadoop</a> also enjoyed a fair amount of intercommunication. Ingersoll also described a new project named <a href="http://ml-site.grantingersoll.com/index.php?title=Main_Page">Mahout</a> which he is in the process of launching:</p>
<blockquote><p>That will be a separate project, but may be beneficial to Lucene users. There are currently some patches in JIRA for Lucene that implement ML algorithms. The goal of this project is to provide commercial quality, large scale <span style="PADDING-RIGHT: 0pt; DISPLAY: inline; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; COLOR: black; PADDING-TOP: 0pt; BACKGROUND-COLOR: yellow" id="__firefox-findbar-search-id">machine</span> learning (ML) algorithms built on Hadoop under an Apache license. I have seen a fair amount of interest already, and hope to have this project underway in the coming month.</p>
</blockquote>
<p><a href="http://lucene.grantingersoll.com/2007/12/26/the-two-flavors-of-google-nice-article-on-hadoop/">Ingersoll said</a> that, by creating Mahout, he hoped to &#8220;further unlock the mysteries of Google and companies like it to provide these capabilities to the masses and spur on new innovation in the space&#8221; &#8212; for those with an interest in this new project, there are both a <a href="http://ml-site.grantingersoll.com/index.php?title=Project_Plan">project plan</a> and an <a href="http://ml-site.grantingersoll.com/index.php?title=Incubator_proposal">incubator proposal</a> available.</p>
<p><a href="http://lucene.apache.org/">Apache Lucene</a>项目是一个完全用Java编写的高性能、全功能的文本搜索引擎库，今天它发布了<a href="http://lucene.apache.org/java/docs/index.html#24+January+2008+-+Lucene+Java+2.3.0+available">2.3版</a>。InfoQ采访了<a href="http://lucene.apache.org/who.html">项目管理委员会（PMC）成员</a>以及提交者，<a href="http://www.grantingersoll.com/">Grant Ingersoll</a>，以深入了解这次发布的版本以及Lucene未来的计划。</p>
<p>Ingersoll认为这次的版本中最大的变化是新的索引算法，它使用了新的in-memory模型来达到大幅的速度提升。据Ingersoll 说，单单是把Lucene 2.2 JAR换成Lucene 2.3 JAR就能在某些测试中把索引性能提速500%。其他改变还包括：</p>
<ul>
<li><strong>改进的索引管理</strong>&#8211;以前在索引过程中，当合并内部索引文件时偶尔会出现长时间的停顿，现在已经消灭了这种现象。另外现在也更容易实现其他途径去管理索引过程。</li>
<li><strong>对象池</strong>&#8211;<code>Document</code>、<code>Field</code>和<code>Token</code>的实例现在可在索引分析中重用，因此不但提升了分析的速度，还减少了索引过程中的内存分配次数。</li>
<li><strong>重新打开IndexReader</strong> &#8211;重新打开一个IndexReader去捕捉索引中最新的变化，这个操作的速度现在也更快了，新的reopen()方法只会加载那些变更过的索引片断，而不是重新加载完整的索引。</li>
<li><strong>更简易的IndexWriter微调</strong>&#8211;<code>setMaxBufferedDocs</code>已被更直观的<code>setRAMBufferSizeMB</code>所取代。</li>
</ul>
<p>另外，2.3的目标是只需通过文件替换就能换下2.2，完全不需要重新编译。这里是<a href="http://svn.apache.org/repos/asf/lucene/java/tags/lucene_2_3_0/CHANGES.txt">完整的更新说明</a>。</p>
<p>Ingersoll还谈论了Lucene未来的计划，他说下一版将会是2.9。2.9版是相对改动较小的版本，有些部分会被标为废弃，还会为了给 Lucene 3.0做准备而进行一些清理。3.0版是一个重大的版本，包括把代码库迁移到JDK5，以之作为最低要求。3.0的其他主要特性还有待决定。</p>
<p>采访中还讨论了Lucene社区的总体情况。Ingersoll表示Lucene和<a href="http://lucene.apache.org/solr/">Solr</a>结合得很紧密，</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/05/3993.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lucene 高亮 &#8211;不就行二次分词（zhuan）</title>
		<link>http://blog.zye.me/2011/05/12152.html</link>
		<comments>http://blog.zye.me/2011/05/12152.html#comments</comments>
		<pubDate>Fri, 20 May 2011 14:28:57 +0000</pubDate>
		<dc:creator>yezheng</dc:creator>
				<category><![CDATA[information Retrieval]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[信息检索]]></category>
		<category><![CDATA[分词]]></category>

		<guid isPermaLink="false">http://www.5yiso.cn/2008/02/12152.html</guid>
		<description><![CDATA[1、问题的来源 增加分词以后结果的准确度提高了，但是用户反映返回结果的速度很慢。原因是，Lucene做每一篇文档的相关关键词的高亮显示时，在运行时执行了很多遍的分词操作。这样降低了性能。 BANG48D1BFD627C49E3110A55E03XIANGUO 2、解决方法 在Lucene1.4.3版本中的一个新功能可以解决这个问题。Term Vector现在支持保存Token.getPositionIncrement() 和Token.startOffset() 以及Token.endOffset() 信息。利用Lucene中新增加的Token信息的保存结果以后，就不需要为了高亮显示而在运行时解析每篇文档。通过Field方法控制是否保存该信息。修改HighlighterTest.java的代码如下： //增加文档时保存Term位置信息。 private void addDoc(IndexWriter writer, String text) throws IOException { Document d = new Document(); //Field f = new Field(FIELD_NAME, text, true, true, true); Field f = new Field(FIELD_NAME, text , Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS); d.add(f); writer.addDocument(d); } //利用Term位置信息节省Highlight时间。 void doStandardHighlights() throws Exception { Highlighter highlighter =new <a href='http://blog.zye.me/2011/05/12152.html'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div>
<p><span style="FONT-FAMILY: Verdana"><span lang="EN-US" xml:lang="EN-US">1</span><span>、问题的来源</span></span></p>
<p style="TEXT-INDENT: 21pt"><span>增加分词以后结果的准确度提高了，但是用户反映返回结果的速度很慢<wbr />。原因是，</span><span lang="EN-US" xml:lang="EN-US">Lucene</span><span>做每一篇文档的相关关键词的高亮显示时，在运行时执行了很多遍的分<wbr />词操作。这样降低了性能。</span></p>
<p style="TEXT-INDENT: 21pt"><span lang="EN-US" xml:lang="EN-US">BANG48D1BFD627C49E3110A55E03XIANGUO</span></p>
<p><span lang="EN-US" xml:lang="EN-US">2</span><span>、解决方法</span></p>
<p style="TEXT-INDENT: 21pt"><span>在</span><span lang="EN-US" xml:lang="EN-US">Lucene1.4.3</span><span>版本中的一个新功能可以解决这个问题。</span><span lang="EN-US" xml:lang="EN-US">Term Vector</span><span>现在支持保存</span><span lang="EN-US" xml:lang="EN-US">Token.getPositionIncrement()</span> <span>和</span><span lang="EN-US" xml:lang="EN-US">Token.startOffset()</span> <span>以及</span><span lang="EN-US" xml:lang="EN-US">Token.endOffset()</span> <span>信息。利用</span><span lang="EN-US" xml:lang="EN-US">Lucene</span><span>中新增加的</span><span lang="EN-US" xml:lang="EN-US">Token</span><span>信息的保存结果以后，就不需要为了高亮显示而在运行时解析每篇文档<wbr />。通过</span><span lang="EN-US" xml:lang="EN-US">Field</span><span>方法控制是否保存该信息。修改</span><span lang="EN-US" xml:lang="EN-US">HighlighterTest.java</span><span>的代码如下：</span></p>
<p style="TEXT-INDENT: 21pt">
<p style="TEXT-INDENT: 21pt"><span lang="EN-US" xml:lang="EN-US">//</span><span>增加文档时保存</span><span lang="EN-US" xml:lang="EN-US">Term</span><span>位置信息。</span></p>
<p><span lang="EN-US" xml:lang="EN-US">private void addDoc(IndexWriter writer, String text) throws IOException</span></p>
<p><span lang="EN-US" xml:lang="EN-US">{</span></p>
<p><span lang="EN-US" xml:lang="EN-US">Document d = new Document();</span></p>
<p><span lang="EN-US" xml:lang="EN-US">//Field f = new Field(FIELD_NAME, text, true, true, true);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">Field f = new Field(FIELD_NAME, text ,</span></p>
<p><span lang="EN-US" xml:lang="EN-US"><span><wbr /></span> Field.Store.YES, Field.Index.TOKENIZED,</span></p>
<p><span lang="EN-US" xml:lang="EN-US"><span><wbr /></span> Field.TermVector.WITH_POSITIONS<wbr />_OFFSETS);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">d.add(f);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">writer.addDocument(d);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">}</span></p>
<p style="TEXT-INDENT: 21pt">
<p style="TEXT-INDENT: 21pt"><span lang="EN-US" xml:lang="EN-US">//</span><span>利用</span><span lang="EN-US" xml:lang="EN-US">Term</span><span>位置信息节省</span><span lang="EN-US" xml:lang="EN-US">Highlight</span><span>时间。</span></p>
<p><span lang="EN-US" xml:lang="EN-US">void doStandardHighlights() throws Exception</span></p>
<p><span lang="EN-US" xml:lang="EN-US">{</span></p>
<p><span lang="EN-US" xml:lang="EN-US">Highlighter highlighter =new Highlighter(this,new QueryScorer(query));</span></p>
<p><span lang="EN-US" xml:lang="EN-US">highlighter.setTextFragmenter(new SimpleFragmenter(20));</span></p>
<p><span lang="EN-US" xml:lang="EN-US">for (int i = 0; i &lt; hits.length(); i++)</span></p>
<p><span lang="EN-US" xml:lang="EN-US">{</span></p>
<p><span lang="EN-US" xml:lang="EN-US">String text = hits.doc(i).get(FIELD_NAME);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">int maxNumFragmentsRequired = 2;</span></p>
<p><span lang="EN-US" xml:lang="EN-US">String fragmentSeparator = &#8220;&#8230;&#8221;;</span></p>
<p><span lang="EN-US" xml:lang="EN-US">TermPositionVector tpv = (TermPositionVector)reader<wbr />.getTermFreqVector(hits.id(i),FIELD_NAME);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">//</span><span>如果没有</span><span lang="EN-US" xml:lang="EN-US">stop words</span><span>去除还可以改成</span> <span lang="EN-US" xml:lang="EN-US">TokenSources.getTokenStream(tpv,true);</span> <span>进一步提速。</span></p>
<p><span lang="EN-US" xml:lang="EN-US">TokenStream tokenStream=TokenSources.getTokenStream(tpv);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">//analyzer.tokenStream(FIELD<wbr />_NAME,new StringReader(text));</span></p>
<p><span lang="EN-US" xml:lang="EN-US">String result =</span></p>
<p><span lang="EN-US" xml:lang="EN-US">highlighter.getBestFragments(</span></p>
<p><span lang="EN-US" xml:lang="EN-US"><span><wbr /></span> tokenStream,</span></p>
<p><span lang="EN-US" xml:lang="EN-US"><span><wbr /></span> text,</span></p>
<p><span lang="EN-US" xml:lang="EN-US"><span><wbr /></span> maxNumFragmentsRequired,</span></p>
<p><span lang="EN-US" xml:lang="EN-US"><span><wbr /></span> fragmentSeparator);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">System.out.println(&#8220;t&#8221; + result);</span></p>
<p><span lang="EN-US" xml:lang="EN-US">}</span></p>
<p><span lang="EN-US" xml:lang="EN-US">}</span></p>
<p><span>最后把</span><span lang="EN-US" xml:lang="EN-US">highlight</span><span>包中的一个额外的判断去掉。对于中文来说没有明显的单词界限<wbr />，所以下面这个判断是错误的：</span></p>
<p><span lang="EN-US" xml:lang="EN-US">tokenGroup.isDistinct(token)</span></p>
<p><span>这样中文分词就不会影响到查询速度了</span></p>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://blog.zye.me/2011/05/12152.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

