[Lucene]信息检索的函数库Lucene的使用总结

mikel阅读(713)

简单介绍:
Lucene是一个信息检索的函数库(Library),利用它你可以为你的应用加上索引和搜索的功能.

Lucene的使用者不需要深入了解有关全文检索的知识,仅仅学会使用库中的一个类,你就为你的应用实现全文检索的功能.

不过千万别以为Lucene是一个google那样的搜索引擎,Lucene甚至不是一个应用程序,它仅仅是一个工具,一个Library.你也可以把它理解为一个将索引,搜索功能封装的很好的一套简单易用的API.利用这套API你可以做很多有关搜索的事情,而且很方便。
发展历史:参见《.NET 的 Lucene 》
详细使用帮助:http://www.lucene.com.cn/net.htm (包含了建立索引/对索引进行搜索/删除索引中的文档/更新索引中的文档/查询语句/多个条件查询)
90%的语法在上述使用帮助都可以找到答案。
上面的使用帮助对排序说明的比较少,补充如下:
可以通过 SortField 的构造参数,我们可以设置排序字段,排序条件,以及倒排。
Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false));
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query, sort);
排序对搜索速度影响还是很大的,尽可能不要使用多个排序条件。
Lucene.Net的语言处理包中Lucene.Net.Analysis.Cn的Bug :http://www.cnblogs.com/dudu/archive…6/22/17783.aspx
如何联合两个索引查询 / 分布搜索:
我们可以使用 MultiReader 或 MultiSearcher 搜索多个索引库。
MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") });
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);

IndexSearcher searcher1 = new IndexSearcher(reader1);
IndexSearcher searcher2 = new IndexSearcher(reader2);
MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 });
Hits hits = searcher.Search(query);
还可以使用 ParallelMultiSearcher 进行多线程并行搜索。
如何合并索引库?
将 directory1 合并到 directory2 中。
Directory directory1 = FSDirectory.GetDirectory("index1", false);
Directory directory2 = FSDirectory.GetDirectory("index2", false);
IndexWriter writer = new IndexWriter(directory2, analyzer, false);
writer.AddIndexes(new Directory[] { directory });
Console.WriteLine(writer.DocCount());
writer.Close();
关于IndexWriter和IndexReader
Add Optimize Merge操作都是由IndexWriter来做的
Delete则是通过IndexReader完成
文档有一句话“即同一时间只允许IndexWriterIndexReader打开同一份索引.不能允许两个同时打开一份索引

记住IndexReader并不是查询索引的时候要用的,从字面含有好像是读取,所以可以同时查询索引和写入索引。

我的使用Lucene.net成功案例:http://video.5913.com/

[Flex]利用HTTPService实现Flex与服务器端低数据量通信

mikel阅读(606)

        Flex作为企业级的开发平台,与服务器端的通信是至关重要的。传统的Flash与服务器端主要通过HTTP、WebService和Flash Remoting方式通信,Flex除了保留了Flash平台与服务端通信方式外增加了一系列的高级服务功能。Flex与服务端通信主要分为两大类:一类 是通过传统的HTTP方式发送请求来和服务器端进行交互,另外一类则是通过LCDS等与服务端进行通信。

      低数据量的通信我们一般会选择Http方式来进行通信。通过HTTP方式Flex可以与任意服务器端进行通信。通过HTTP与服务器端通信又可以通过 HTTP请求方式和WebService实现。前者使用异步的HTTP请求方式,使用方式与Ajax一致,而后者则是通过标准的WebService协议 与服务端进行通信。下面我们看下HTTPService方式的通信,查阅相关的帮助文档我们可以知道HTTPService一些常用的方法和属性:

属性

method:发送HTTP请求的方法

 url : 请求服务的位置

request:发送请求的参数

requestTimeout:请求超时的时间

useProxy:是否使用代理的别名服务

方法:

send:参数为object类型默认为null,返回值类型为AsyncToken,执行一个HTTPService的请求

下面我们通过一个获取新浪体育新闻汇总的示例来说明HTTPService的使用

 


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="sinaSport.send()">
<mx:Style>
    global
    {
        fontSize:14pt;
    }
</mx:Style>
    
<mx:HTTPService id="sinaSport" url="http://rss.sina.com.cn/roll/sports/hot_roll.xml" useProxy="false">
    
</mx:HTTPService>
    
<mx:Panel x="53" y="90" width="975" height="461" layout="absolute" title="新浪体育要闻汇总 ">
    
<mx:DataGrid id="sportContent" x="23.5" y="10" width="911.5" dataProvider="{sinaSport.lastResult.rss.channel.item}" height="175">
        
<mx:columns>
            
<mx:DataGridColumn  width="300" headerText="标题" dataField="title"/>
            
<mx:DataGridColumn  width="150" headerText="作者" dataField="author" />
            
<mx:DataGridColumn  width="150" headerText="日期" dataField="pubDate" />
        
</mx:columns>
    
</mx:DataGrid>
    
<mx:TextArea x="23" y="193" width="912" height="183" htmlText="{sportContent.selectedItem.description}"/>
 
<mx:LinkButton x="855" y="384" label="详细信息" click="navigateToURL(new URLRequest(sportContent.selectedItem.link))"/>
    
</mx:Panel>
    
</mx:Application>

示例运行效果如下图:

在上面的代码中通过 如下代码声明了HTTPService 数据的位置


<mx:HTTPService id="sinaSport" url="http://rss.sina.com.cn/roll/sports/hot_roll.xml" useProxy="false">
    
</mx:HTTPService>

 

通过


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="sinaSport.send()">

进行数据的发送

服务器端返回的结果则是通过sinaSport.lastResult来引用最后通过DataGrid来显示数据。

         从这个简单的例子我们可以看出因为HTTPService不是标准协议,基于HTTPService的通信方式访问的数据格式是由服务端来制定的,所以在 使用中要求Flex端与服务端的数据处理方式和交换格式一致。基于HTTP协议访问一般都通过纯文本数据进行传输。Flex能够轻松的将之转换为 ActionScript对象。

[JQuery]JQuery1.3发布了

mikel阅读(638)

2009-1-14,JQuery 1.3发布正式版,尽管之前版本的jQ已经让我们感受到了诸多便利,但是1.3还是给了我们额外的惊喜。
大家可以参考JQuery对于这个版本发布的官方文档:http://docs.jQuery.com/Release:jQuery_1.3
在官方的文档里你可以看到更详细的数据,不过在本篇里你可以看到更细节的分析。
==Sizzle,独立的css选择器引擎==
jQuery在之前的版本里已经逐渐酝酿出css选择器模块的独立。因为Sizzle的独立性,jQuery和Dojo开发组也有意通过Sizzle来开展长期合作(也就是共享部分代码)。
Sizzle也兼容之前版本的jQuery,那些依赖于低版本的jQuery的代码并无重写之忧。
Sizzle在最常见的使用场景中,也有了不同程度的性能提高。(什么是最常见使用的场景呢?可以参考http://ejohn.org/blog/selectors-that-people-actually-use/)
==Live,持久化事件绑定==
通常我们在使用jQuery的过程中,新构造的DOMElement也需要重新完成其事件绑定的逻辑。而jQuery 1.3 里的live函数可以让我们用类似于css behavior的方式来为未来要产生的DOMElement绑定事件。(当然你要拿它当css用,也无妨)
随之对应的,解除这种持久化事件绑定的方式可以用die来完成。
在sizzle.js的源码开头的几行,我们可以看到实现的思路。
document.addEventListener("DOMAttrModified", invalidate, false);
document.addEventListener("DOMNodeInserted", invalidate, false);
document.addEventListener("DOMNodeRemoved", invalidate, false);
==Event对象重封装==
jQuery.Event的主要功能被封装。(jQ有分拆IPO的倾向)
==html注入重写==
性能大幅提高(about 6x faster overall)。
还提供了新用法$("<script/>") 以代替$(document.createElement("script"))
==Offset重写==
性能大幅提高(almost 3x jump)
==jQuery.support,浏览器识别==
jQuery.browser是用useragent来识别浏览器,新增的jQuery.support以js的功能来识别浏览器。
==更新==
jQuery UI and the Validation plugin会同步更新。
如果你有依赖低版本的jQuery的代码的话,请阅读potentially-breaking changes
==Downloading==
jQuery Minified (18kb Gzipped)
jQuery Regular (114kb)
也可以用 http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js for a quickly-loading jQuery
==不兼容的Changes==
[@attr]不再需要写@了。(个人感觉这是一个很坏的change)
使用trigger( event, [data] )触发的事件现在会造成冒泡。(言下之意就是以前不会,以前没在意这个细节)
ready()不会等待所有的.css文件下载完毕才执行。(页面上的css引用要写在js前面了)
isFunction被简化。
(原来 !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test( fn + "" )
现在 toString.call(obj) === "[object Function]" ,有很多你想不到的东西的typeof会是"Function")
逗号选择器的顺序有可能改变(因为Browsers that support querySelectorAll (Safari, Firefox 3.1+, Opera 10+, IE 8+),而它们返回的顺序和jQuery现有逻辑不同)
trigger 和 triggerHandler 原来提供的data形参可以接受一个数组,现在必须是类似于options的对象
(原来按以下对象属性的数组也可以被接受,{type: type,target: elem,preventDefault: function(){},stopPropagation: function(){},timeStamp: now()})
"extra"函数被去除(原来就没有写进文档里),如果trigger 和 triggerHandler 里data参数里提供了多余的成员,如{fa:function(){},fb:function(){},fc:function(){},type: type,target: elem,preventDefault: function(){},stopPropagation: function(){},timeStamp: now()},如果该成员isFunction的话,也会随trigger 和 triggerHandler被执行。
interal jQuery.event.trigger不再返回最后一个hander的返回值,只返回true or false(需要的话可以用jQuery.Event 来作为返回值的句柄)
需要在标准模式下运行jQuery,因为有一些函数在quirks mode运行不正确(Safari下甚至如此重要的一些选择器函数都会出错)
==增强特性==
(FEATURES)
===Core===
New queueing methods which you can use to manage existing queues (such as the animation queue) or construct new ones to manage completely separate workflows.(不是很理解有什么个特法)
jQuery 实例对象新增了两个属性selector and .context 。(以唤回你在构造这些对象时的记忆,jQuery( expression, context ) )
===Selector===
新增支持 复杂的not表达式。如 $(":not(a, b)") and $(":not(div a)")
not表达式其实是反向选择器,老实说我觉得这是一个很重要的特性
===Attributes===
jQuery实例对象新增成员函数toggleClass( String class, Boolean switch )
===Traversing===
jQuery实例对象新增成员函数 .closest( selector ) 以定位位置最接近的符合selector的ancestor element(祖先元素)。(会递归匹配parentNode,我原以为parent( [expr] ) 是做这个用的,后来发现parent( [expr] )只是很直白的选出以expr构造出的jQuery实例对象或者jQuery实例对象this的parentNode)
jQuery实例对象成员函数is()可以支持复杂选择器
===Manipulation===
$("<script/>")相当于原来的$(document.createElement("script")),前面介绍过了
===CSS===
Offset 重写了,性能提高,前面介绍过了
===Events===
Live Events
jQuery Event Object
.trigger()冒泡
(前面都介绍过了)
===Effects===
.hide() and .show() 重写,性能提高。
animate更加平滑,因为原来只控制width, height,opacity,现在连margin and padding控制了
未提供duration参数的animate调用不会有渐变,而是直接被同步化(因为原来animate的渐变都是异步的)的设为最终值
.toggle( Boolean switch ) 所有类似于toggle的函数(如toggleClass)都增加了switch这个参数支持,把toggle类的对象分为正反两种状态的话(如采用和不 采用,显示和隐藏),if(switch)则为正状态,反之亦然。
参考下面的代码:
var flip = 0;
$("button").click(function () {
$("p").toggle( flip++ % 2 == 0 );
});
原来无switch参数的时候相当于总是( flip++ % 2 == 0 ),现在我们可以自己控制了。
这是一个很好的新增特性。
jQuery.fx.off=true用来禁止所有animate
===Ajax===
jQuery.ajax( options )
options新增了{Function(data, type) dataFilter},可以用来在jQuery.ajax返回data前再处理一下data(比如过滤)
options新增了{Function() xhr},以xhr返回值作为XMLHttpRequest对象。这是一个很有用的增强,在跨域ajax时,一种做法是制造一个假的XMLHttpRequest。
load( url, [data], [callback] )的参数data可以是String了
===Utilities===
新增了jQuery.isArray( obj )函数
===Internals===
不再使用ua来作为识别浏览器的判断
YUI Compressor作为默认jQuery压缩工具
jQuery不再使用.constructor and instanceof(Will help to give jQuery better cross-frame support.)
==TESTING==
brabra
==PERFORMANCE==
css选择器,delegate过滤器,DOM修改操作(尤其是插入),.offset(),.hide()/.show() 都获得了不同程度的性能改进

[Lucene]Annotated Lucene:第三节 索引文件结构(1)

mikel阅读(685)

Annotated Lucene(源码剖析中文版)

Annotated Lucene 作者:naven  


  

3.1      索引文件结构

  Lucene使用文件扩展名标识不同的索引文件,文件名标识不同版本或者代(generation)的索引片段(segment)。如.fnm文件存储域Fields名称及其属性,.fdt存储文档各项域数据,.fdx存储文档在fdt中的偏移位置即其索引文件,.frq存储文档中term位置数据,.tii文件存储term字典,.tis文件存储term频率数据,.prx存储term接近度数据,.nrm存储调节因子数据,另外segments_X文件存储当前最新索引片段的信息,其中X为其最新修改版本,segments.gen存储当前版本即X值,这些文件的详细介绍上节已说过了。

    下面的图描述了一个典型的lucene索引文件列表:

     

lucene-indexfiles.jpg

    

如果将它们的关系划成图则如下所示(该图来自网上,出处忘了):

 

image010.jpg

 

这些文件中存储数据的详细结构是怎样的呢,下面几个小节逐一介绍它们,熟悉它们的结构非常有助于优化Lucene的查询和索引效率和存储空间等。

    

         

3.2  每个Index包含的单个文件

下面几节介绍的文件存在于每个索引index中,并且只有一份。 

3.2.1 Segments文件

    

索引中活动(active)的Segments被存储在segment info文件中,segments_N,在索引中可能会包含一个或多个segments_N文件。然而,最大一代的那个文件(the one with largest generation)是活动的片断文件(这时更旧的segments_N文件依然存在(are present)是因为它们暂时(temporarily)还不能被删除,或者,一个writer正在处理提交请求(in the process of committing),或者一个用户定义的(customIndexDeletionPolicy正被使用)。这个文件按照名称列举每一个片断(lists each segment by name),详细描述分离的标准(seperate norm)和要删除的文件(deletion files),并且还包含了每一个片断的大小。

  

2.1版本来说,还有一个文件segments.gen。这个文件包含了该索引中当前生成的代(current generation)(segments_N中的_N)。这个文件仅用于一个后退处理(fallback)以防止(in case)当前代(current generation)不能被准确地(accurately)通过单独地目录文件列举(by directory listing alone)来确定(determened)(由于某些NFS客户端因为基于时间的目录(time-based directory)的缓存终止(cache expiration)而引起)。这个文件简单地包含了一个int32的版本头(version header)(SegmentInfos.FORMAT_LOCKLESS=-2),遵照代的记录(followed by the generation recorded)规则,对int64来说会写两次(write twice)。

    

版本 包含的项 数目 类型 描述
2.1之前版本 Format 1 Int32 Lucene1.4中为-1,而在Lucene 2.1中为-3SegmentsInfos.FORMAT_SINGLE_NORM_FILE
Version 1 Int64 统计在删除和添加文档时,索引被更改了多少次。
NameCounter 1 Int32 用于为新的片断文件生成新的名字。
SegCount 1 Int32 片断的数目
SegName SegCount String 片断的名字,用于所有构成片断索引的文件的文件名前缀。
SegSize SegCount Int32 包含在片断索引中的文档的数目。
2.1及之后版本 Format 1 Int32 Lucene 2.1Lucene 2.2中为-3SegmentsInfos.FORMAT_SINGLE_NORM_FILE
Version 1 Int64 同上
NameCounter 1 Int32 同上
SegCount 1 Int32 同上
SegName SegCount String 同上
SegSize SegCount Int32 同上
DelGen SegCount Int64 为分离的删除文件的代的数目(generation count of the separate deletes file),如果值为-1,表示没有分离的删除文件。如果值为0,表示这是一个2.1版本之前的片断,这时你必须检查文件是否存在_X.del这样的文件。任意大于0的值,表示有分离的删除文件,文件名为_X_N.del
HasSingleNormFile SegCount Int8 该值如果为1,表示Norm域(field)被写为一个单一连接的文件(single joined file)中(扩展名为.nrm),如果值为0,表示每一个fieldnorms被存储为分离的.fN文件中,参考下面的“标准化因素(Normalization Factors)”
NumField SegCount Int32 表示NormGen数组的大小,如果为-1表示没有NormGen被存储。
NormGen SegCount * NumField Int64 记录分离的标准文件(separate norm file)的代(generation),如果值为-1,表示没有normGens被存储,并且当片断文件是2.1之前版本生成的时,它们全部被假设为0assumed to be 0)。而当片断文件是2.1及更高版本生成的时,它们全部被假设为-1。这时这个代(generation)的意义与上面DelGen的意义一样。
IsCompoundFile SegCount Int8 记录是否该片断文件被写为一个复合的文件,如果值为-1表示它不是一个复合文件(compound file),如果为1则为一个复合文件。另外如果值为0,表示我们需要检查文件系统是否存在_X.cfs
2.3 Format 1 Int32 Lucene 2.3中为-4 (SegmentInfos.FORMAT_SHARED_DOC_STORE)
Version 1 Int64 同上
NameCounter 1 Int32 同上
SegCount 1 Int32 同上
SegName SegCount String 同上
SegSize SegCount Int32 同上
DelGen SegCount Int64 同上
DocStoreOffset 1 Int32 如果值为-1则该segment有自己的存储文档的fields数据和term vectors的文件,并且DocStoreSegment, DocStoreIsCompoundFile不会存储。在这种情况下,存储fields数据(*.fdt*.fdx文件)以及term vectors数据(*.tvf*.tvd*.tvx文件)的所有文件将存储在该segment下。另外,DocStoreSegment将存储那些拥有共享的文档存储文件的segmentDocStoreIsCompoundFile值为1如果segment存储为compound文件格式(如.cfx文件),并且DocStoreOffset值为那些共享文档存储文件中起始的文档编号,即该segment的文档开始的位置。在这种情况下,该segment不会存储自己的文档数据文件,而是与别的segment共享一个单一的数据文件集。
[DocStoreSegment] 1 String 如上
[DocStoreIsCompoundFile] 1 Int8 如上
HasSingleNormFile SegCount Int8 同上
NumField SegCount Int32 同上
NormGen SegCount * NumField Int64 同上
IsCompoundFile SegCount Int8 同上
2.4及以上 Format 1 Int32 Lucene 2.4中为-7 (SegmentInfos.FORMAT_HAS_PROX)
Version 1 Int64 同上
NameCounter 1 Int32 同上
SegCount 1 Int32 同上
SegName SegCount String 同上
SegSize SegCount Int32 同上
DelGen SegCount Int64 同上
DocStoreOffset 1 Int32 同上
[DocStoreSegment] 1 String 同上
[DocStoreIsCompoundFile] 1 Int8 同上
HasSingleNormFile SegCount Int8 同上
NumField SegCount Int32 同上
NormGen SegCount * NumField Int64 同上
IsCompoundFile SegCount Int8 同上
DeletionCount SegCount Int32 记录该segment中删除的文档数目
HasProx SegCount Int8 值为1表示该segment中至少一个fieldsomitTf设置为false,否则为0
Checksum 1 Int64 存储segments_N文件中直到checksum的所有字节的CRC32 checksum数据,用来校验打开的索引文件的完整性(integrity)。

   

3.2.2 Lock文件

    

写锁(write lock)文件名为“write.lock”,它缺省存储在索引目录中。如果锁目录(lock directory)与索引目录不一致,写锁将被命名为“XXXX-write.lock”,其中“XXXX”是一个唯一的前缀(unique prefix),来源于(derived from)索引目录的全路径(full path)。当这个写锁出现时,一个writer当前正在修改索引(添加或者清除文档)。这个写锁确保在一个时刻只有一个writer修改索引。

  

需要注意的是在2.1版本之前(prior to),Lucene还使用一个commit lock,这个锁在2.1版本里被删除了。

  

3.2.3 Deletable文件

    

Lucene 2.1版本之前,有一个“deletable”文件,包含了那些需要被删除文档的详细资料。在2.1版本后,一个writer会动态地(dynamically)计算哪些文件需要删除,因此,没有文件被写入文件系统。

           

3.2.4  Compound文件(.cfs

    

Lucene 1.4版本开始,compound文件格式成为缺省信息。这是一个简单的容器(container)来服务所有下一章节(next section)描述的文件(除了.del文件),格式如下:

  

   

版本 包含的项 数目 类型 描述
1.4之后版本 FileCount 1 VInt  
DataOffset FileCount Long  
FileName FileCount String  
FileData FileCount raw Raw文件数据是上面命名的所有单个的文件数据(the individual named above)。

   

结构如下图所示:

    

image012.gif

      

               Annotated Lucene 作者:naven 日期:2008-10-20

[lucene]Lucene.Net FAQ问题集锦

mikel阅读(862)

部分信息翻译自 Apache Lucene FAQ,请注意标题中 "(翻译)" 字样。
IndexWriter.SetUseCompoundFile(true) 有什么用?
在创建索引库时,会合并多个 Segments 文件到一个 .cfs 中。此方式有助于减少索引文件数量,减少同时打开的文件数量。
可以使用 CompoundFileReader 查看 .cfs 文件内容。

CompoundFileReader reader = new CompoundFileReader(FSDirectory.GetDirectory("y:\\index", false), "_1oa.cfs");
foreach (string filename in reader.List())
{
  Console.WriteLine(filename);
}

IOException "Too many open files" (翻译)
原因:
某些操作系统会限制同时打开的文件数量。
解决方法:
1. 使用 IndexWriter's setUseCompoundFile(true) 创建复合文件,减少索引文件数量。
2. 不要将 IndexWriter's mergeFactor 的值设置过大。尽管这能加快索引速度,但会增加同时打开的文件数量。
3. 如果在搜索时发生该错误,那么你最好调用 IndexWriter.Optimize() 优化你的索引库。
4. 确认你仅创建了一个 IndexSearcher 实例,并且在所有的搜索线程中共用。(原文:"Make sure you only open one IndexSearcher, and share it among all of the threads that are doing searches — this is safe, and it will minimize the number of files that are open concurently. " 晕~~~,究竟要怎么做? )
为什么搜索不到结果?(翻译)
可能原因:
1. 搜索字段没有被索引。
2. 索引库中的字段没有分词存储,无法和搜索词语进行局部匹配。
3. 搜索字段不存在。
4. 搜索字段名错误,注意字段名称区分大小写。建议对字段名进行常量定义。
5. 要搜索的词语是忽略词(StopWords)。
6. 索引(IndexWriter)和搜索(IndexSearcher)所使用的 Analyzer 不同。
7. 你所使用的 Analyzer 区分大小写。比如它使用了 LowerCaseFilter,而你输入的查询词和目标大小写不同。
8. 你索引的文档(Document)太大。Lucene 为避免造成内存不足(OutOfMemory),缺省仅索引前10000个词语(Term)。可以使用 IndexWriter.setMaxFieldLength() 调整。
9. 确认在搜索前,目标文档已经被添加到索引库。
10. 如果你使用了 QueryParser,它可能并没有按照你所设想的去分析 BooleanQuerySyntax。
如果还不行,那么:
1. 使用 Query.ToString() 查看究竟搜索了些什么。
2. 使用 Luke 看看你的索引库究竟有什么。
TooManyClauses Exception (翻译)
使 用 RangeQuery, PrefixQuery, WildcardQuery, FuzzyQuery 等类型时可能会引发该异常。比如我们使用 "ca*" 来搜索 "car" 和 "cars",由于搜索结果文档(Document)所包含的 Term 超出 Lucene 默认数量限制 (默认1024),则会引发 TooManyClauses 异常。解决方法:
1. 使用 Filter 替换引发异常的 Query,比如使用 RangeFilter 替换 RangeQuery 搜索 DateField 就不会引发 TooManyClauses 异常。你可以使用 ConstantScoreQuery 像 Query 那样执行 Filter。第一次使用 Filters 时速度要比 Queries 慢一点,但我们可以使用 CachingWrapperFilter 进行缓存。
2. 使用 BooleanQuery.setMaxClauseCount() 加大 Terms 数量,当然这会增加内存占用。使用 BooleanQuery.setMaxClauseCount(int.MaxValue) 会避开任何限制。
3. 还有一个解决方法是通过缩小字段数据精度来达到减少索引中 Terms 数量的目的。例如仅保存 DateField 中的 "yyyymmddHHMM"(可以使用 Lucene 1.9 版本中的 DateTools)。
通配符
Lucene 支持英文 "?" 和 "*" 通配符,但不能放在单词首位。
QueryParser 是线程安全的吗?
不是。
MaxDoc() 和 DocCount()、NumDocs() 有什么不同?
MaxDocs() 表示索引库中最大的 Document ID 号,由于中间的某些 Document 可能被删除,因此不能使用 MaxDocs() 来表示 Document 数量。IndexWriter.DocCount()、IndexReader.NumDocs()、 IndexSearcher.Reader.NumDocs() 都表示索引库中 Document 数量。
为什么同时进行搜索和更新会引发 FileNotFoundException 异常?(翻译)
可能原因:
1. 某个搜索或更新对象禁用了锁。
2. 搜索和更新使用了不同的 lockDir。
3. 索引库被存放在 NFS (or Samba) 文件系统上。
尽管搜索是只读操作,但 IndexSeacher 为了获取索引文件列表,也必须打开时锁定索引库。如果锁没有正确设置,那么它将取回一个错误的文件列表(此时 IndexWriter 可能正在添加或优化索引),从而导致该异常发生。
索引文件有大小限制吗?(翻译)
某些 32 位操作系统限制每个文件不能大于 2GB。
解决方法:
1. 使用 IndexWriter.setMaxMergeDocs() 减小 MaxMergeDocs 数值。
2. 使用多个索引库。
什么是 Segments ?(翻译)
索引库中每个索引文件都是由多个 Segments 组成。当你添加一个 Document 到索引库,那么就可能会创建一个新的 Segment。你可以使用 Optimize() 来压缩索引库以减少 Segments 数量。
write.lock 有什么用?哪些类会用到它?(翻译)
write.lock 用来协调索引库的并发修改处理。
当 IndexWriter 打开索引库,或者 IndexReader 删除文档时都将创建该锁。
commit.lock 文件有什么用?哪些类会用到它?(翻译)
commit.lock 在调整索引库 segments 文件内容时使用。 IndexReader 和 IndexWriter 都会使用到它。
"Lock obtain timed out." 错误。在哪删除锁文件?(翻译)
一 般存放在系统临时目录(System.IO.Path.GetTempPath()),也可以在 app.config/web.config 中手工设置。可以手工进行删除,或者使用 "IndexReader.isLocked"、"IndexReader.unlock" 进行自动判断和删除操作。
FSDirectory.cs

public static readonly System.String LOCK_DIR = SupportClass.AppSettings.Get("Lucene.Net.lockDir", System.IO.Path.GetTempPath());

app.config / web.config

<configuration>
  <appSettings>
    <add key="Lucene.Net.lockdir" value="c:\index" />
  </appSettings>
</configuration>

如何更新已经索引的文档? (翻译)
你只能先删除,然后添加更新后的文档。
使用 IndexWriter.addIndexes(IndexReader[]) 和 IndexWriter.addIndexes(Directory[]) 合并索引库有什么不同? (翻译)
使用 Directory[] 参数所需的文件句柄和内存较小,索引文件仅需打开一次,而使用 IndexReader[] 参数则需要打开所有的索引库。

[SQLServer]从SQL中的一个表中导出HTML文件表格

mikel阅读(694)

EXECUTE sp_makewebtask @outputfile = 'D:"alcatel"20030902"check.htm',

@query = 'Select * FROM Eiems_temporarydata..property', @templatefile = 'D:"alcatel"20030902"check.TPL',——check.tpl文件为模板文件

@dbname = 'Eiems_temporarydata', @rowcnt = 0, @whentype = 9 ,@lastupdated = 1

GO

******************************check.TPL*******************************

<HTML>
 
<HEAD>
 
<TITLE>pelease check it when you finish!</TITLE>
<style type="text/css">

body{font-size:9pt}

TH{FONT-SIZE: 12pt}

TD{ font-size: 12pt }

–>

</style>
 
<BODY  text="#ffffff" BGCOLOR="#FFF5EE">
 
<center>
<H1><font color="000000">check it!</font></H1>
<HR>

<P>
<H2>
<TABLE BORDER="1" cellpadding="0" cellspacing="0"bgcolor="#436EEE">
<TR> <TH><B>Size</B></TH>
     <TH><B>Date</B></TH>
     <TH><B>Name</B></TH>
     <TH><B>Exist</B></TH>
     <TH><B>Remark</B></TH>
     <TH><B>result</B></TH>
</TR>
<%begindetail%>
<TR> <TD> <%insert_data_here%>  </TD>
    <TD ALIGN=RIGHT><%insert_data_here%></TD>
    <TD ALIGN=RIGHT><%insert_data_here%></TD>
    <TD ALIGN=RIGHT><%insert_data_here%></TD>
    <TD ALIGN=RIGHT><%insert_data_here%></TD>
    <TD ALIGN=RIGHT><%insert_data_here%></TD>
</TR>
<%enddetail%>
</TABLE>
</center> 
</H2>
</BODY>
 
</HTML>

[Lucene]使用Lucene.net索引目录文件实例代码

mikel阅读(852)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lucene.Net.Index;
using Lucene.Net.Store;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Search;
using Lucene.Net.Analysis.Cn;
using Lucene.Net.Documents;
using Lucene.Net.QueryParsers;
using System.IO;
namespace Search
{
    class Program
    {
        static void Main(string[] args)
        {
            //
            StandardAnalyzer analyzer=new StandardAnalyzer();
            //建立一个内存目录
            //Lucene.Net.Store.RAMDirectory ramDir = new Lucene.Net.Store.RAMDirectory();
            Lucene.Net.Store.Directory ramDir = FSDirectory.GetDirectory("../index/", true);
           
            //建立一个索引书写器
            IndexWriter ramWriter = new IndexWriter(ramDir,analyzer , true);
            //ramWriter.SetMaxFieldLength(25000);
            ////要索引的词,这就相当于一个个的要索引的文件
            //string[] words = { "中华人民共和国", "人民共和国", "人民", "共和国" };
            ////循环数组,创建文档,给文档添加字段,并把文档添加到索引书写器里
            //Document doc = null;
            //for (int i = 0; i < words.Length; i++)
            //{
            //    doc = new Document();
            //    doc.Add(new Field("contents", words[i], Field.Store.YES, Field.Index.TOKENIZED));
            //    ramWriter.AddDocument(doc);
            //}
            IndexDirectory(ramWriter, new System.IO.FileInfo("../tmp/"));    
            //索引优化
            ramWriter.Optimize();
            //TokenStream st = analyzer.TokenStream("contents", new StringReader());
            //关闭索引读写器,一定要关哦,按理说应该把上面的代码用try括主,在finally里关闭索引书写器
            ramWriter.Close();
            //构建一个索引搜索器
            IndexSearcher searcher = new IndexSearcher(ramDir);
            //用QueryParser.Parse方法实例化一个查询
            //Query query = QueryParser.Parse("中华人民", "contents", new ChineseAnalyzer());
            QueryParser parser=new QueryParser("contents",analyzer);
            Query query = parser.Parse("唐");
            //获取搜索结果
            Hits hits = searcher.Search(query);
          
           
            //判断是否有搜索到的结果,当然你也可以遍历结果集并输出
            if (hits.Length() != 0)
                Console.WriteLine("有");
            else
                Console.WriteLine("没有");
            for (int i = 0; i < hits.Length(); i++)
            {
                Document doc = hits.Doc(i);
                Console.Write(doc.Get("contents"));
                //String[] str = hits.Doc(i).GetValues("contents");
                //for (int j = 0; j < str.Length; j++)
                //{
                //    Console.Write(str[j]);
                //}
                //TextReader reader=doc.GetField("contents").ReaderValue();
                //Console.WriteLine(reader.ReadLine());
                       
               
            }
           
            searcher.Close();
            ramDir.Close();
            Console.Read();
        }
        /// <summary>
        /// 生成指定文件或目录的索引
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="file"></param>
        public static void IndexDirectory(IndexWriter writer, FileInfo file)
        {
            //文件路径是否存在
            if (System.IO.Directory.Exists(file.FullName))
            {
                //获得文件列表
                String[] files = System.IO.Directory.GetFileSystemEntries(file.FullName);
                // an IO error could occur
                //文件不存在
                if (files != null)
                {
                    //遍历目录文件
                    for (int i = 0; i < files.Length; i++)
                    {                      
                        IndexDirectory(writer, new FileInfo(files[i]));  //这里是一个递归
                    }
                }
            }
            else if (file.Extension == ".htm")//文件扩展名符合直接创建索引
            {
                IndexFile(file, writer);
            }
        }
        /// <summary>
        /// 创建文件的索引
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="writer">索引编写器</param>
        private static void IndexFile(FileInfo file, IndexWriter writer)
        {
            Console.Out.WriteLine("adding " + file);
            try
            {
                //创建新文档
                Document doc = new Document();
                //添加Field
                doc.Add(new Field("filename", file.FullName,Field.Store.YES,Field.Index.TOKENIZED));
                //读取文件内容
                string values;
                using(StreamReader reader=new StreamReader(file.FullName,Encoding.UTF8))
                {
                    values = reader.ReadToEnd();
                }
                //创建Field
                doc.Add(new Field("contents",values,Field.Store.YES,Field.Index.TOKENIZED));
                //写入索引
                writer.AddDocument(doc);
            }
            catch (FileNotFoundException fnfe)
            {
            }
        }
    }
}

[Lucene]Lucene 中 Field 的类型

mikel阅读(707)

1.       2.0 以前的版本

  • Keyword: Field 的值将被保存到索引文件,为Field的值建立索引,建立索引时不需要分词。
  • UnIndexed: Field 的值将被保存到索引文件,不为Field的值建立索引,因此不能通过该Field搜索文档。
  • UnStored: Field 的值不被保存到索引文件,将Field的值分词后建立索引
  • Text: Field 的值分词后建立索引。如果参数为String值将被保存,为Reader值不被保存

2.       2.0 版本
 
用几个内部类的组合来区分Field的具体类型。

  • Store

²        COMPRESS: 压缩保存。用于长文本或二进制数据

²        YES :保存

²        NO :不保存

  • Index

²        NO :不 建索引

²        TOKENIZED :分词, 建索引

²        UN_TOKENIZED :不分词, 建索引

²        NO_NORMS :不分词, 建索引。但是Field的值不像通常那样被保存,而是只取一个byte,这样节约存储空间

  • TermVector

²        NO 不保存term vectors

²        YES 保存term vectors

²        WITH_POSITIONS 保存term vectors。(保存值和token位置信息)

²        WITH_OFFSETS 保存term vectors。(保存值和TokenoffsetWITH_POSITIONS_OFFSETS保存term vectors。(保存值和token位置信息和Tokenoffset

[Lucene]Lucene.NET - Index and Search Any Website

mikel阅读(765)

One of my customers wanted to make their site searchable. They have a lot of content in different places (physical files, other websites, database, etc.). Trying to search all of these places real time would be a nightmare…and incredibly slow! So instead, I decided to build a web spider that caches the website content to a local drive on the web server and indexes the content into a Lucene index. The searches are now incredibly fast (under 1/2 sec), have relevancy scores and are merged. Some of the code was example code found at DotLucene (http://www.dotlucene.net/download/)…most of it is original.

This application shows you how to use Lucene to create a custom advanced search engine. There is way too much code to go over every part so I'm only discussing the important parts.
Download code 
Admin page – Allows user to index/re-index websites, delete indexed websites and index physical hard drive folders.
"http://www.brianpautsch.com" is indexed in less than 5 seconds.

"C:\_Websites\" is indexed in less than 5 seconds.

Lines 138-153: AddWebPage method: The spider calls this method for each link found. This method strips off any bookmarks, verifies the file extension is in our list of valid extensions and ensures the site is within our base URL. If all of these tests pass, an instance of WebPageState is created (URL loaded in constructor), a unique ID is assigned and the page is put in the queue of pages that need to be visited and indexed/cached.
Lines 171-214 : Process method: This method makes a WebRequest to the URL, checks the status code, stores the HTML and sets the process success flags.
Lines 261-269 : HandleLinks method: This method uses a regular expression to find all URL links on the page.
Lines 272-285: AddWebPageToIndex method: Once all of the information for the page is gathered this method is called to add the information to the index. Note that some fields are added as "UnIndexed", "Text", etc. Here's a little explanation on each:
    Field.Keyword – The data is stored and indexed but not tokenized – (Last modified date, filename)
    Field.Text – The data is stored, indexed, and tokenized – Searchable small text (Title)
    Field.UnStored – The data is not stored but it is indexed and tokenized. (File content)
    Field.UnIndexed – The data is stored but not indexed or tokenized. (URL)
Index/Cache Storage
As new websites are indexed, they are stored in separate folders under the "…\LuceneWeb\WebUI\cache\" and "…\LuceneWeb\WebUI\index\" folders. The current version only allows for one index of the hard drive and it's stored in the "…\LuceneWeb\WebUI\index\folders\" folder. This logic could be easily changed to have multiple indices of the hard drive.
 
Search page – Allows user to select an index and search it.
"http://www.gotdotnet.com" is searched for "microsoft" – 158 results found in .26 seconds.

"C:\_Websites\" is searched for "search" – 10 results found in .63 seconds.
 
Lines 145-217 : PerformSearch method – This is the main method for this class (code-behind). It starts off by determining the index location and creating an empty datatable of results. A basic query is performed (no special filtering, i.e. datetime, buckets) and returns a "Hits" object. A QueryHighlighter object is created and as each result is extracted the contents are highlighted. Finally, the datarow is saved to the datatable and later bound to the repeater.