lushengdi

 

2008年5月11日

oracle数据库的备份与还原(本地及远程操作)

     摘要: 备份,在开始运行中CMD
exp ay_user/ay@zhf file=e:\名称.dmp log=e:\log.txt full=y

//ay_user 用户名 zhf 密码 要备份的数据库名和地址,log=e:\log.txt 备份生成的日志文件的地址和名称。full=y完全备份

还原,在开始运行中CMD
imp zhf/huifeng@zhf file=f:\名称.dmp log=f:\log.txt full=y   阅读全文

posted @ 2008-05-11 10:27 鲁胜迪 阅读(30) | 评论 (0)编辑 收藏

2008年5月10日

动态显示进度条


可以将下边的代码嵌入到html,jsp,asp,php或aspx文件中!


截图


代码如下:

<body>
<input type="button" value="start" onclick="processBar.changeMode();if(processBar.isMoving){this.value='Stop';}else{this.value='Start';}">
</body>

<script>
if(window.ActiveXObject) document.execCommand("BackgroundImageCache",false,true);

function ProcessBar(){
    this.width = 256;
    this.height = 18;
    this.top = 0;
    this.left = 0;
    this.backImg = "process_back.gif";
    this.foreImg = "process.gif";
    this.backDiv = document.createElement("div");
    this.foreDiv = document.createElement("div");
    this.fontDiv = document.createElement("div");

    this.isMoving = false;
    this.nowLength = 0;
    this.moveInterval = 100;
    this.moveRange = 1;
    this.timer;

    ProcessBar.nowObj = this;

    this.init = function(){
         this.foreDiv.style.backgroundImage = "url(" + this.foreImg + ")";
         this.foreDiv.style.backgroundRepeat = "no-repeat";
         this.foreDiv.style.position = "absolute";
         this.foreDiv.style.width = this.nowLength;
         this.foreDiv.style.height = this.height;
         this.foreDiv.style.top = 0;
         this.foreDiv.style.left = 0;
       
         this.fontDiv.style.background = "transparent";
         this.fontDiv.style.position = "absolute";
         this.fontDiv.style.width = this.width;
         this.fontDiv.style.height = this.height;
         this.fontDiv.style.top = 2;
         this.fontDiv.style.left = 0;
         this.fontDiv.style.textAlign = "center";
         this.fontDiv.style.fontSize = "13px";
         this.fontDiv.appendChild(document.createTextNode(" "));

         this.backDiv.style.backgroundImage = "url(" + this.backImg + ")";
         this.backDiv.style.backgroundRepeat = "no-repeat";
         this.backDiv.style.position = "absolute";
         this.backDiv.style.width = this.width;
         this.backDiv.style.height = this.height;
         this.backDiv.style.top = this.top;
         this.backDiv.style.left = this.left;

         this.backDiv.appendChild(this.foreDiv);
         this.backDiv.appendChild(this.fontDiv);

         document.body.appendChild(this.backDiv);
    }

    this.changeMode = function(){
         this.isMoving = !this.isMoving;
       
         if(this.isMoving){
             this.timer = window.setInterval(ProcessBar.nowObj.moving, this.moveInterval);
         }else{
             window.clearInterval(this.timer);
         }
    }

    this.moving = function(){
         ProcessBar.nowObj.nowLength += ProcessBar.nowObj.moveRange;
         ProcessBar.nowObj.foreDiv.style.width = ProcessBar.nowObj.nowLength;

         ProcessBar.nowObj.fontDiv.firstChild.data = Math.ceil((ProcessBar.nowObj.nowLength/ProcessBar.nowObj.width )*100) + "%";

         if(ProcessBar.nowObj.nowLength >= ProcessBar.nowObj.width ){
             window.clearInterval(ProcessBar.nowObj.timer);
             ProcessBar.nowObj.fontDiv.firstChild.data = "Complete!";
         }
    }

  
}

var processBar = new ProcessBar();
processBar.backImg = "http://screenprint2007.cpp114.com/UserFiles/20070412155452218.gif";
processBar.foreImg = "http://screenprint2007.cpp114.com/UserFiles/20070412155424937.gif";
processBar.top = 100;
processBar.left = 20;
processBar.init();
</script>

posted @ 2008-05-10 09:34 鲁胜迪 阅读(23) | 评论 (0)编辑 收藏

2008年5月9日

oracle创建表空间,创建用户以及授权

//创建临时表空间

create temporary tablespace test_temp
tempfile 'E:\oracle\product\10.2.0\oradata\testserver\test_temp01.dbf'
size 32m
autoextend on
next 32m maxsize 2048m
extent management local;

//创建数据表空间
create tablespace test_data
logging
datafile 'E:\oracle\product\10.2.0\oradata\testserver\test_data01.dbf'
size 32m
autoextend on
next 32m maxsize 2048m
extent management local;

//创建用户并指定表空间
create user testserver_user identified by testserver_user
default tablespace test_data
temporary tablespace test_temp;

//给用户授予权限

grant connect,resource to testserver_user;

//以后以该用户登录,创建的任何数据库对象都属于test_temp 和test_data表空间,这就不用在每创建一个对象给其指定表空间了。

posted @ 2008-05-09 20:15 鲁胜迪 阅读(36) | 评论 (0)编辑 收藏

2008年5月5日

远程操作、远程协助

在使用电脑的过程中,我们会遇到各种各样的问题。在那些自己不能独立解决的问题面前,我们只能求助于专业的技术人员或者找经验丰富的朋友帮忙。可是技术人员不可能时时都在身边,那还有没有其他办法可以得到技术人员的协助呢?

  微软早就为我们在Windows XP中内置了一个通过网络的远程协助工具:“远程桌面连接”。通过远程桌面连接我们可以控制其他安装了Windows XP、Windows 2000或者Windows Server 2003的计算机。在客户计算机上选择:开始|所有程序|附件|通信|远程桌面连接,在弹出的对话框中输入你要远程连接的计算机的IP地址(如图1所示)。如果对方允许你连接的话,就可以控制对方系统,就好像你正坐在对方的计算机前操作对方计算机的键盘和鼠标一样。

 

图1 连接远程桌面

  相对的,我们也可以设置Windows XP使得其他人可以远程控制我们的计算机。在“我的电脑”上点击右键,选择“属性”然后在弹出的对话框中选择“远程”,然后选中“远程桌面”(如图所示),之后选择“选择远程用户”在其中输入可以访问的用户名。这样其他用户就可以远程控制你的计算机,以便可以远程帮你解决问题。

 

图2 开启远程桌面

  Windows XP中的远程连接允许我们可以从技术人员和朋友那里得到远程协助,这些用户可以在你允许的时间内远程控制你的计算机,并且他们的一切动作都是在你的监视之下的。当然为了得到远程协助你还需要在网络防火墙中开放3389端口。

posted @ 2008-05-05 15:12 鲁胜迪 阅读(30) | 评论 (0)编辑 收藏

2008年4月14日

oracle 自增变量设置

将表t_uaer的字段ID设置为自增:(用序列sequence的方法来实现)

----创建表
Create  table  t_user(
Id number(6),userid varchar2(20),loginpassword varchar2(20),isdisable number(6)
);

----创建序列
create sequence user_seq
increment by 1 
start with 1
nomaxvalue
nominvalue
nocache

----创建触发器
create or  replace trigger tr_user
before insert on t_user
for each row
begin
select user_seq.nextval into :new.id from dual;
end;

----测试
insert into t_user(userid,loginpassword, isdisable)
values('ffll','liudddyujj', 0);
insert into t_user(userid,loginpassword, isdisable)
values('dddd','zhang', 0)
select * from t_user;
就可以看出结果。

***********************************************************************
对sequence说明:
increment by :用于指定序列增量(默认值:1),如果指定的是正整数,则序列号自动递增,如果指定的是负数,则自动递减。
start with :用于指定序列生成器生成的第一个序列号,当序列号顺序递增时默认值为序列号的最小值 当序列号顺序递减时默认值为序列号的最大值。
Maxvalue:用于指定序列生成器可以生成的组大序列号(必须大于或等于start with,并且必须大于minvalue),默认为nomaxvalue。
Minvalue:用于指定序列生成器可以生成的最小序列号(必须小于或等于starr with,并且必须小于maxvalue),默认值为nominvalue。
Cycle:用于指定在达到序列的最大值或最小值之后是否继续生成序列号,默认为nocycle。
Cache:用于指定在内存中可以预分配的序列号个数(默认值:20)。
在sequence中应注意:
1、 第一次NEXTVAL返回的是初始值;随后的NEXTVAL会自动增加你定义的INCREMENT BY值,然后返回增加后的值。CURRVAL 总是返回当前SEQUENCE的值,但是在第一次NEXTVAL初始化之后才能使用CURRVAL,否则会出错。一次NEXTVAL会增加一次SEQUENCE的值,所以如果你在同一个语句里面使用多个NEXTVAL,其值就是不一样的。
2、 如果指定CACHE值,ORACLE就可以预先在内存里面放置一些sequence,这样存取的快些。cache里面的取完后,oracle自动再取一组到cache。 使用cache或许会跳号, 比如数据库突然不正常down掉(shutdown abort),cache中的sequence就会丢失. 所以可以在create sequence的时候用nocache防止这种情况。


关键字:自增    sequence序列    increment    start with

posted @ 2008-04-14 13:13 鲁胜迪 阅读(76) | 评论 (0)编辑 收藏

2008年4月10日

Lucene:基于Java的全文检索引擎简介【转】

http://www.chedong.com/tech/lucene.html



Lucene是一个基于Java的全文索引工具包。

  1. 基于Java的全文索引引擎Lucene简介:关于作者和Lucene的历史
  2. 全文检索的实现:Luene全文索引和数据库索引的比较
  3. 中文切分词机制简介:基于词库和自动切分词算法的比较
  4. 具体的安装和使用简介:系统结构介绍和演示
  5. Hacking Lucene:简化的查询分析器,删除的实现,定制的排序,应用接口的扩展
  6. 从Lucene我们还可以学到什么

基于Java的全文索引/检索引擎——Lucene

Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。

Lucene的作者:Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。

Lucene的发展历程:早先发布在作者自己的www.lucene.com,后来发布在SourceForge,2001年年底成为APACHE基金会jakarta的一个子项目:http://jakarta.apache.org/lucene/

已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎,比较著名的有:

  • Jive:WEB论坛系统;
  • Eyebrows:邮件列表HTML归档/浏览/查询系统,本文的主要参考文档“TheLucene search engine: Powerful, flexible, and free”作者就是EyeBrows系统的主要开发者之一,而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。
  • Cocoon:基于XML的web发布框架,全文检索部分使用了Lucene
  • Eclipse:基于Java的开放开发平台,帮助部分的全文索引使用了Lucene

对于中文用户来说,最关心的问题是其是否支持中文的全文检索。但通过后面对于Lucene的结构的介绍,你会了解到由于Lucene良好架构设计,对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。

全文检索的实现机制

Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表==>记录==>字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看:可以先把Lucene当成一个支持全文索引的数据库系统

比较一下Lucene和数据库:

Lucene 数据库
索引数据源:doc(field1,field2...) doc(field1,field2...)
\ indexer /
_____________
| Lucene Index|
--------------
/ searcher \
结果输出:Hits(doc(field1,field2) doc(field1...))
 索引数据源:record(field1,field2...) record(field1..)
\ SQL: insert/
_____________
| DB Index |
-------------
/ SQL: select \
结果输出:results(record(field1,field2..) record(field1...))
Document:一个需要进行索引的“单元”
一个Document由多个字段组成
Record:记录,包含多个字段
Field:字段 Field:字段
Hits:查询结果集,由匹配的Document组成 RecordSet:查询结果集,由多个Record组成

全文检索 ≠ like "%keyword%"

通常比较厚的书籍后面常常附关键词索引表(比如:北京:12, 34页,上海:3,77页……),它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样,想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高,另外一个原因是它是排好序的。对于检索系统来说核心是一个排序问题

由于数据库索引不是为全文索引设计的,因此,使用like "%keyword%"时,数据库索引是不起作用的,在使用like查询时,搜索过程又变成类似于一页页翻书的遍历过程了,所以对于含有模糊查询的数据库服务来说,LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配:like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。

所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制,将数据源(比如多篇文章)排序顺序存储的同时,有另外一个排好序的关键词列表,用于存储关键词==>文章映射关系,利用这样的映射关系索引:[关键词==>出现关键词的文章编号,出现次数(甚至包括位置:起始偏移量,结束偏移量),出现频率],检索过程就是把模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程。从而大大提高了多关键词查询的效率,所以,全文检索问题归结到最后是一个排序问题。

由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题,这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制,并提供了扩展接口,以方便针对不同应用的定制。

可以通过一下表格对比一下数据库的模糊查询:

  Lucene全文索引引擎 数据库
索引 将数据源中的数据都通过全文索引一一建立反向索引 对于LIKE查询来说,数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配,比有索引的搜索速度要有多个数量级的下降。
匹配效果 通过词元(term)进行匹配,通过语言分析接口的实现,可以实现对中文等非英语的支持。 使用:like "%net%" 会把netherlands也匹配出来,
多个关键词的模糊匹配:使用like "%com%net%":就不能匹配词序颠倒的xxx.net..xxx.com
匹配度 有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。 没有匹配程度的控制:比如有记录中net出现5词和出现1次的,结果是一样的。
结果输出 通过特别的算法,将最匹配度最高的头100条结果输出,结果集是缓冲式的小批量读取的。 返回所有的结果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放这些临时结果集。
可定制性 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) 没有接口或接口复杂,无法定制
结论 高负载的模糊查询应用,需要负责的模糊查询的规则,索引的资料量比较大 使用率低,模糊匹配规则简单或者需要模糊查询的资料量少

全文检索和数据库应用最大的不同在于:让最相关的头100条结果满足98%以上用户的需求

Lucene的创新之处:

大部分的搜索(数据库)引擎都是用B树结构来维护索引,索引的更新会导致大量的IO操作,Lucene在实现中,对此稍微有所改进:不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中(针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效率。

Lucene和其他一些全文检索系统/应用的比较:

  Lucene 其他开源全文检索系统
增量索引和批量索引 可以进行增量的索引(Append),可以对于大量数据进行批量索引,并且接口设计用于优化批量索引和小批量的增量索引。 很多系统只支持批量的索引,有时数据源有一点增加也需要重建索引。
数据源 Lucene没有定义具体的数据源,而是一个文档的结构,因此可以非常灵活的适应各种应用(只要前端有合适的转换器把数据源转换成相应结构), 很多系统只针对网页,缺乏其他格式文档的灵活性。
索引内容抓取 Lucene的文档是由多个字段组成的,甚至可以控制那些字段需要进行索引,那些字段不需要索引,近一步索引的字段也分为需要分词和不需要分词的类型:
   需要进行分词的索引,比如:标题,文章内容字段
   不需要进行分词的索引,比如:作者/日期字段
缺乏通用性,往往将文档整个索引了
语言分析 通过语言分析器的不同扩展实现:
可以过滤掉不需要的词:an the of 等,
西文语法分析:将jumps jumped jumper都归结成jump进行索引/检索
非英文支持:对亚洲语言,阿拉伯语言的索引支持
缺乏通用接口实现
查询分析 通过查询分析接口的实现,可以定制自己的查询语法规则:
比如: 多个关键词之间的 + - and or关系等
 
并发访问 能够支持多用户的使用  

 

关于亚洲语言的的切分词问题(Word Segment)

对于中文来说,全文索引首先还要解决一个语言分析的问题,对于英文来说,语句中单词之间是天然通过空格分开的,但亚洲语言的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行索引的话,这个词如何切分出来就是一个很大的问题。

首先,肯定不能用单个字符作(si-gram)为索引单元,否则查“上海”时,不能让含有“海上”也匹配。

但一句话:“北京天安门”,计算机如何按照中文的语言习惯进行切分呢?
“北京 天安门” 还是“北 京 天安门”?让计算机能够按照语言习惯进行切分,往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。

另外一个解决的办法是采用自动切分算法:将单词按照2元语法(bigram)方式切分出来,比如:
"北京天安门" ==> "北京 京天 天安 安门"。

这样,在查询的时候,无论是查询"北京" 还是查询"天安门",将查询词组按同样的规则进行切分:"北京","天安安门",多个关键词之间按与"and"的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。

基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低,但对于中小型应用来说,基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多,而对于英文,索引文件一般只有原文件的30%-40%不同,


自动切分 词表切分
实现 实现非常简单 实现复杂
查询 增加了查询分析的复杂程度, 适于实现比较复杂的查询语法规则
存储效率 索引冗余大,索引几乎和原文一样大 索引效率高,为原文大小的30%左右
维护成本 无词表维护成本 词表维护成本非常高:中日韩等语言需要分别维护。
还需要包括词频统计等内容
适用领域 嵌入式系统:运行环境资源有限
分布式系统:无词表同步问题
多语言环境:无词表维护成本
对查询和存储效率要求高的专业搜索引擎

目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法,大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。

安装和使用

下载:http://jakarta.apache.org/lucene/

注意:Lucene中的一些比较复杂的词法分析是用JavaCC生成的(JavaCC:JavaCompilerCompiler,纯Java的词法分析生成器),所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器,还需要从https://javacc.dev.java.net/下载javacc。

lucene的组成结构:对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口

org.apache.Lucene.search/ 搜索入口
org.apache.Lucene.index/ 索引入口
org.apache.Lucene.analysis/ 语言分析器
org.apache.Lucene.queryParser/ 查询分析器
org.apache.Lucene.document/ 存储结构
org.apache.Lucene.store/  底层IO/存储结构
org.apache.Lucene.util/ 一些公用的数据结构

简单的例子演示一下Lucene的使用方法:

索引过程:从命令行读取文件名(多个),将文件分路径(path字段)和内容(body字段)2个字段进行存储,并对内容进行全文索引:索引的单位是Document对象,每个Document对象包含多个字段Field对象,针对不同的字段属性和数据输出的需求,对字段还可以选择不同的索引/存储字段规则,列表如下:
方法 切词 索引 存储 用途
Field.Text(String name, String value) Yes Yes Yes 切分词索引并存储,比如:标题,内容字段
Field.Text(String name, Reader value) Yes Yes No 切分词索引不存储,比如:META信息,
不用于返回显示,但需要进行检索内容
Field.Keyword(String name, String value) No Yes Yes 不切分索引并存储,比如:日期字段
Field.UnIndexed(String name, String value) No No Yes 不索引,只存储,比如:文件路径
Field.UnStored(String name, String value) Yes Yes No 只全文索引,不存储
public class IndexFiles { 
//使用方法:: IndexFiles [索引输出目录] [索引的文件列表] ...
public static void main(String[] args) throws Exception {
String indexPath = args[0];
IndexWriter writer;
//用指定的语言分析器构造一个新的写索引器(第3个参数表示是否为追加索引)
writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);

for (int i=1; i<args.length; i++) {
System.out.println("Indexing file " + args[i]);
InputStream is = new FileInputStream(args[i]);

//构造包含2个字段Field的Document对象
//一个是路径path字段,不索引,只存储
//一个是内容body字段,进行全文索引,并存储
Document doc = new Document();
doc.add(Field.UnIndexed("path", args[i]));
doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));
//将文档写入索引
writer.addDocument(doc);
is.close();
};
//关闭写索引器
writer.close();
}
}
 

索引过程中可以看到:

  • 语言分析器提供了抽象的接口,因此语言分析(Analyser)是可以定制的,虽然lucene缺省提供了2个比较通用的分析器SimpleAnalyser和StandardAnalyser,这2个分析器缺省都不支持中文,所以要加入对中文语言的切分规则,需要修改这2个分析器。
  • Lucene并没有规定数据源的格式,而只提供了一个通用的结构(Document对象)来接受索引的输入,因此输入的数据源可以是:数据库,WORD文档,PDF文档,HTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。
  • 对于大批量的数据索引,还可以通过调整IndexerWrite的文件合并频率属性(mergeFactor)来提高批量索引的效率。

检索过程和结果显示:

搜索结果返回的是Hits对象,可以通过它再访问Document==>Field中的内容。

假设根据body字段进行全文检索,可以将查询结果的path字段和相应查询的匹配度(score)打印出来,

public class Search { 
public static void main(String[] args) throws Exception {
String indexPath = args[0], queryString = args[1];
//指向索引目录的搜索器
Searcher searcher = new IndexSearcher(indexPath);
//查询解析器:使用和索引同样的语言分析器
Query query = QueryParser.parse(queryString, "body",
new SimpleAnalyzer());
//搜索结果使用Hits存储
Hits hits = searcher.search(query);
//通过hits可以访问到相应字段的数据和查询的匹配度
for (int i=0; i<hits.length(); i++) {
System.out.println(hits.doc(i).get("path") + "; Score: " +
hits.score(i));
};
}
}
在整个检索过程中,语言分析器,查询分析器,甚至搜索器(Searcher)都是提供了抽象的接口,可以根据需要进行定制。

Hacking Lucene

简化的查询分析器

个人感觉lucene成为JAKARTA项目后,画在了太多的时间用于调试日趋复杂QueryParser,而其中大部分是大多数用户并不很熟悉的,目前LUCENE支持的语法:

Query ::= ( Clause )*
Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")

中间的逻辑包括:and or + - &&||等符号,而且还有"短语查询"和针对西文的前缀/模糊查询等,个人感觉对于一般应用来说,这些功能有一些华而不实,其实能够实现目前类似于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以,Lucene早期版本的QueryParser仍是比较好的选择。

添加修改删除指定记录(Document)

Lucene提供了索引的扩展机制,因此索引的动态扩展应该是没有问题的,而指定记录的修改也似乎只能通过记录的删除,然后重新加入实现。如何删除指定的记录呢?删除的方法也很简单,只是需要在索引时根据数据源中的记录ID专门另建索引,然后利用IndexReader.delete(Termterm)方法通过这个记录ID删除相应的Document。

根据某个字段值的排序功能

lucene缺省是按照自己的相关度算法(score)进行结果排序的,但能够根据其他字段进行结果排序是一个在LUCENE的开发邮件列表中经常提到的问题,很多原先基于数据库应用都需要除了基于匹配度(score)以外的排序功能。而从全文检索的原理我们可以了解到,任何不基于索引的搜索过程效率都会导致效率非常的低,如果基于其他字段的排序需要在搜索过程中访问存储字段,速度回大大降低,因此非常是不可取的。

但这里也有一个折中的解决方法:在搜索过程中能够影响排序结果的只有索引中已经存储的docID和score这2个参数,所以,基于score以外的排序,其实可以通过将数据源预先排好序,然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访问不在索引中的某个字段值。

这里需要修改的是IndexSearcher中的HitCollector过程:

...
 scorer.score(new HitCollector() {
private float minScore = 0.0f;
public final void collect(int doc, float score) {
if (score > 0.0f && // ignore zeroed buckets
(bits==null || bits.get(doc))) { // skip docs not in bits
totalHits[0]++;
if (score >= minScore) {
/* 原先:Lucene将docID和相应的匹配度score例入结果命中列表中:
* hq.put(new ScoreDoc(doc, score)); // update hit queue
* 如果用doc 或 1/doc 代替 score,就实现了根据docID顺排或逆排
* 假设数据源索引时已经按照某个字段排好了序,而结果根据docID排序也就实现了
* 针对某个字段的排序,甚至可以实现更复杂的score和docID的拟合。
*/
hq.put(new ScoreDoc(doc, (float) 1/doc ));
if (hq.size() > nDocs) { // if hit queue overfull
hq.pop(); // remove lowest in hit queue
minScore = ((ScoreDoc)hq.top()).score; // reset minScore
}
}
}
}
}, reader.maxDoc());

更通用的输入输出接口

虽然lucene没有定义一个确定的输入文档格式,但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口,然后其他数据,比如PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主,类似实现已经不下4,5个:

数据源: WORD       PDF     HTML    DB       other
\ | | | /
XML中间格式
|
Lucene INDEX

目前还没有针对MSWord文档的解析器,因为Word文档和基于ASCII的RTF文档不同,需要使用COM对象机制解析。这个是我在Google上查的相关资料:http://www.intrinsyc.com/products/enterprise_applications.asp
另外一个办法就是把Word文档转换成text:http://www.winfield.demon.nl/index.html


索引过程优化

索引一般分2种情况,一种是小批量的索引扩展,一种是大批量的索引重建。在索引过程中,并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作(文件I/O是一件非常消耗资源的事情)。

Lucene先在内存中进行索引操作,并根据一定的批量进行文件的写入。这个批次的间隔越大,文件的写入次数越少,但占用内存会很多。反之占用内存少,但文件IO操作频繁,索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件的操作。根据我的使用经验:缺省Indexer是每20条记录索引后写入一次,每将MERGE_FACTOR增加50倍,索引速度可以提高1倍左右。

搜索过程优化

lucene支持内存索引:这样的搜索比基于文件的I/O有数量级的速度提升。
http://www.onjava.com/lpt/a/3273
而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。

Lucene面向全文检索的优化在于首次索引检索后,并不把所有的记录(Document)具体内容读取出来,而起只将所有结果中匹配度最高的头100条结果(TopDocs)的ID放到结果集缓存中并返回,这里可以比较一下数据库检索:如果是一个10,000条的数据库检索结果集,数据库是一定要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多,Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是用不到这么多的结果的,头100条已经可以满足90%以上的检索需求。

如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存,并再重新向后抓取。所以如果构造一个Searcher去查1-120条结果,Searcher其实是进行了2次搜索过程:头100条取完后,缓存结果用完,Searcher重新检索再构造一个200条的结果缓存,依此类推,400条缓存,800条缓存。由于每次Searcher对象消失后,这些缓存也访问那不到了,你有可能想将结果记录缓存下来,缓存数尽量保证在100以下以充分利用首次的结果缓存,不让Lucene浪费多次检索,而且可以分级进行结果缓存。

Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。

我的一些尝试

  • 支持中文的Tokenizer:这里有2个版本,一个是通过JavaCC生成的,对CJK部分按一个字符一个TOKEN索引,另外一个是从SimpleTokenizer改写的,对英文支持数字和字母TOKEN,对中文按迭代索引。
  • 基于XML数据源的索引器:XMLIndexer,因此所有数据源只要能够按照DTD转换成指定的XML,就可以用XMLIndxer进行索引了。
  • 根据某个字段排序:按记录索引顺序排序结果的搜索器:IndexOrderSearcher,因此如果需要让搜索结果根据某个字段排序,可以让数据源先按某个字段排好序(比如:PriceField),这样索引后,然后在利用这个按记录的ID顺序检索的搜索器,结果就是相当于是那个字段排序的结果了。

从Lucene学到更多

Luene的确是一个面对对象设计的典范

  • 所有的问题都通过一个额外抽象层来方便以后的扩展和重用:你可以通过重新实现来达到自己的目的,而对其他模块而不需要;
  • 简单的应用入口Searcher, Indexer,并调用底层一系列组件协同的完成搜索任务;
  • 所有的对象的任务都非常专一:比如搜索过程:QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取,并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高,因此可以通过重新实现而不需要修改其他模块。 
  • 除了灵活的应用接口设计,Lucene还提供了一些适合大多数应用的语言分析器实现(SimpleAnalyser,StandardAnalyser),这也是新用户能够很快上手的重要原因之一。

这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包,Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。

此外,通过对Lucene的学习和使用,我也更深刻地理解了为什么很多数据库优化设计中要求,比如:

  • 尽可能对字段进行索引来提高查询速度,但过多的索引会对数据库表的更新操作变慢,而对结果过多的排序条件,实际上往往也是性能的杀手之一。
  • 很多商业数据库对大批量的数据插入操作会提供一些优化参数,这个作用和索引器的merge_factor的作用是类似的,
  • 20%/80%原则:查的结果多并不等于质量好,尤其对于返回结果集很大,如何优化这头几十条结果的质量往往才是最重要的。
  • 尽可能让应用从数据库中获得比较小的结果集,因为即使对于大型数据库,对结果集的随机访问也是一个非常消耗资源的操作。

参考资料:

Apache: Lucene Project
http://jakarta.apache.org/lucene/
Lucene开发/用户邮件列表归档
Lucene-dev@jakarta.apache.org
Lucene-user@jakarta.apache.org

The Lucene search engine: Powerful, flexible, and free
http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html

Lucene Tutorial
http://www.darksleep.com/puff/lucene/lucene.html

Notes on distributed searching with Lucene
http://home.clara.net/markharwood/lucene/

中文语言的切分词
http://www.google.com/search?sourceid=navclient&hl=zh-CN&q=chinese+word+segment

搜索引擎工具介绍
http://searchtools.com/

Lucene作者Cutting的几篇论文和专利
http://lucene.sourceforge.net/publications.html 

Lucene的.NET实现:dotLucene
http://sourceforge.net/projects/dotlucene/

Lucene作者Cutting的另外一个项目:基于Java的搜索引擎Nutch
http://www.nutch.org/   http://sourceforge.net/projects/nutch/

关于基于词表和N-Gram的切分词比较
http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html

2005-01-08 Cutting在Pisa大学做的关于Lucene的讲座:非常详细的Lucene架构解说

特别感谢:
前网易CTO许良杰(Jack Xu)给我的指导:是您将我带入了搜索引擎这个行业。

posted @ 2008-04-10 13:52 鲁胜迪 阅读(13) | 评论 (0)编辑 收藏

lucene多字段查询

http://hi.baidu.com/%B3%BF%D1%F4%C2%FE%B2%BD/blog/item/9478602deaa1cd37349bf7d5.html


我的例子就是2.0的,现在给你的是两个域,你可以用n个域

BooleanQuery typeNegativeSearch = new BooleanQuery();
QueryParser parser = new QueryParser("contents", new Analyzer());
                parser.setDefaultOperator(QueryParser.AND_OPERATOR);
                query = parser.parse(queryString);
                QueryParser parser2 = new QueryParser("adISELL", new Analyzer());  

           
                query2 = parser2.parse("\"2\"");  
QueryParser parser3 = new QueryParser("adISELL", new Analyzer());              
                query3 = parser3.parse("\"2\"");             
QueryParser parser4 = new QueryParser("adISELL", new Analyzer());              
                query4 = parser4.parse("\"2\"");             
QueryParser parser4 = new QueryParser("adISELL", new Analyzer());              
                query4 = parser4.parse("\"2\"");  
。。。。
     QueryParser parser..n = new QueryParser("adISELL", new Analyzer());           

  
                query..n = parser..n.parse("\"2\"");  
                
                typeNegativeSearch.add(query,Occur.MUST);
                typeNegativeSearch.add(query2,Occur.MUST);
typeNegativeSearch.add(query3,Occur.MUST);
                typeNegativeSearch.add(query4,Occur.MUST);
.....
typeNegativeSearch.add(query..n,Occur.MUST);

hits = searcher.search(typeNegativeSearch);

 

1, 几种span的querySpanTermQuery:检索效果完全同TermQuery,但内部会记录一些位置信息

,供SpanQuery的其它API使用,是其它属于SpanQuery的Query的基础。
SpanFirstQuery:查找方式为从Field的内容起始位置开始,在一个固定的宽度内查找所指定的

词条。
SpanNearQuery:功能类似PharaseQuery。SpanNearQuery查找所匹配的不一定是短语,还有可

能是另一个SpanQuery的查询结果作为整体考虑,进行嵌套查询。
SpanOrQuery:把所有SpanQuery查询结果综合起来,作为检索结果。
SpanNotQuery:从第一个SpanQuery查询结果中,去掉第二个SpanQuery查询结果,作为检索结

果。

2, 多条件索引关系

BooleanClause用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST,

BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。有以下6种组合:
1.MUST和MUST:取得连个查询子句的交集。
2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
3.MUST_NOT和MUST_NOT:无意义,检索无结果。
4.SHOULD与MUST、SHOULD与MUST_NOT:SHOULD与MUST连用时,无意义,结果为MUST子句的检索

结果。与MUST_NOT连用时,功能同MUST。
5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。

posted @ 2008-04-10 13:45 鲁胜迪 阅读(115) | 评论 (0)编辑 收藏

2008年4月8日

为oracle客户端添加连接到服务器端的方法

1.在oracle客户端中按照如下路径找到tnsnames.ora文件,并以文本格式打开
D:\oracle\ora92\network\admin\tnsnames.ora

2.在打开的文本中加入以下内容
LSD_192.168.1.32 =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.32)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SID = ora9i)
      (SERVER = DEDICATED)
    )
  )

3.打开oracle客户端,进行连接服务器的操作
此时可以发现在"数据库"选项中多出了"LSD_192.168.1.32"这条记录,
如果选择该项,就可以连接到ip为192.168.1.32的服务器上
数据库的SID为 ora9i.

posted @ 2008-04-08 13:09 鲁胜迪 阅读(58) | 评论 (0)编辑 收藏

2008年4月1日

使用lucene对搜索结果排序

lucene默认根据匹配度对搜索结果降序排,如果对某个域进行排序?
通常分两步:

 step1)建索引时
doc.add(new Field("audittime", row.get("audittime").toString(),
                Field.Store.NO, Field.Index.UN_TOKENIZED));

关键点是你需要排序的字段建索引时应该采用Field.Index.UN_TOKENIZED,至于需不需要Field.Store.NO看实际情况,反正不影响排序

step2)搜索时
public Hits search(SearchVO searchVO) throws IOException {
        ................

        Sort sort = this.buildSort(searchVO);

        Hits hits = searcher.search(query, sort);

        return hits;
}

private Sort buildSort(SearchVO searchVO) {

        if (searchVO.getOrderby() == null || searchVO.getOrderby().length() < 1) {
            return null;
        }

        Sort sort = new Sort(searchVO.getOrderby(),
                (searchVO.getAscflag() == 1 ? false : true));

        return sort;

}

 关键点是通过建立一个Sort实例然后传给IndexSearcher 的另一个重载方法search(Query query,Sort sort);

通过Sort你可以指定排序字段名,升序降序.

如果你需要特指字段类型,你需要使用  new Sort(SortField field)
或者你需要通过多字段排序,你需要使用  new Sort(SortField[] fields)
我就不再赘述了

题外话:
   
使用lucene 排序是很简单的,但如何发挥它的效果是一个值得思考的地方.
   
如果你是实时的更新lucene索引我没话说(这需要相当谨慎的设计).

如果你定时更新索引,就会有些问题,比如你要排序的字段更新速度很快,你应该如何显示它? 因为你排序的数据(即建索引时的数据)和当前的数据并不完全同步,这会给显示带来问题.如果你显示建索引时的数据,这样排序本身没问题,但客户会很奇怪我的数据明明是6了怎么还显示4.如果你显示当前的数据就更奇怪了,客户可能会奇怪我的数据是1000怎么排名第4,排名第一的才500?  如果你的数据更新缓慢,这样做不会有什么问题.但更新很快的话就是灾难了.

posted @ 2008-04-01 10:22 鲁胜迪 阅读(93) | 评论 (0)编辑 收藏

2008年3月31日

深入 Lucene 索引机制

Lucene 是一个基于 Java 的全文检索工具包,你可以利用它来为你的应用程序加入索引和检索功能。Lucene 目前是著名的 Apache Jakarta 家族中的一个开源项目,下面我们即将学习 Lucene 的索引机制以及它的索引文件的结构。

在这篇文章中,我们首先演示如何使用 Lucene 来索引文档,接着讨论如何提高索引的性能。最后我们来分析 Lucene 的索引文件结构。需要记住的是,Lucene 不是一个完整的应用程序,而是一个信息检索包,它方便你为你的应用程序添加索引和搜索功能。

架构概览

图一显示了 Lucene 的索引机制的架构。Lucene 使用各种解析器对各种不同类型的文档进行解析。比如对于 HTML 文档,HTML 解析器会做一些预处理的工作,比如过滤文档中的 HTML 标签等等。HTML 解析器的输出的是文本内容,接着 Lucene 的分词器(Analyzer)从文本内容中提取出索引项以及相关信息,比如索引项的出现频率。接着 Lucene 的分词器把这些信息写到索引文件中。


图一:Lucene 索引机制架构
图一:Lucene 索引机制架构

 

用Lucene索引文档

      接下来我将一步一步的来演示如何利用 Lucene 为你的文档创建索引。只要你能将要索引的文件转化成文本格式,Lucene 就能为你的文档建立索引。比如,如果你想为 HTML 文档或者 PDF 文档建立索引,那么首先你就需要从这些文档中提取出文本信息,然后把文本信息交给 Lucene 建立索引。我们接下来的例子用来演示如何利用 Lucene 为后缀名为 txt 的文件建立索引。

1. 准备文本文件

首先把一些以 txt 为后缀名的文本文件放到一个目录中,比如在 Windows 平台上,你可以放到 C:\\files_to_index 下面。

2. 创建索引

清单1是为我们所准备的文档创建索引的代码。


清单1:用 Lucene 索引你的文档
package lucene.index;
            import java.io.File;
            import java.io.FileReader;
            import java.io.Reader;
            import java.util.Date;
            import org.apache.lucene.analysis.Analyzer;
            import org.apache.lucene.analysis.standard.StandardAnalyzer;
            import org.apache.lucene.document.Document;
            import org.apache.lucene.document.Field;
            import org.apache.lucene.index.IndexWriter;
            /**
            * This class demonstrates the process of creating an index with Lucene
            * for text files in a directory.
            */
            public class TextFileIndexer {
            public static void main(String[] args) throws Exception{
            //fileDir is the directory that contains the text files to be indexed
            File   fileDir  = new File("C:\\files_to_index ");
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            File[] textFiles  = fileDir.listFiles();
            long startTime = new Date().getTime();
            //Add documents to the index
            for(int i = 0; i < textFiles.length; i++){
            if(textFiles[i].isFile() && textFiles[i].getName().endsWith(".txt")){
            System.out.println("File " + textFiles[i].getCanonicalPath()
            + " is being indexed");
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Text("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("It took " + (endTime - startTime)
            + " milliseconds to create an index for the files in the directory "
            + fileDir.getPath());
            }
            }
            

正如清单1所示,你可以利用 Lucene 非常方便的为文档创建索引。接下来我们分析一下清单1中的比较关键的代码,我们先从下面的一条语句开始看起。


Analyzer luceneAnalyzer = new StandardAnalyzer();
            

这条语句创建了类 StandardAnalyzer 的一个实例,这个类是用来从文本中提取出索引项的。它只是抽象类 Analyzer 的其中一个实现。Analyzer 也有一些其它的子类,比如 SimpleAnalyzer 等。

我们接着看另外一条语句:


IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            

       这条语句创建了类 IndexWriter 的一个实例,该类也是 Lucene 索引机制里面的一个关键类。这个类能创建一个新的索引或者打开一个已存在的索引并为该索引添加文档。我们注意到该类的构造函数接受三个参数,第一个参数指定了存储索引文件的路径。第二个参数指定了在索引过程中使用什么样的分词器。最后一个参数是个布尔变量,如果值为真,那么就表示要创建一个新的索引,如果值为假,就表示打开一个已经存在的索引。

接下来的代码演示了如何添加一个文档到索引文件中。


Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Text("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            

      首先第一行创建了类 Document 的一个实例,它由一个或者多个的域(Field)组成。你可以把这个类想象成代表了一个实际的文档,比如一个 HTML 页面,一个 PDF 文档,或者一个文本文件。而类 Document 中的域一般就是实际文档的一些属性。比如对于一个 HTML 页面,它的域可能包括标题,内容,URL 等。我们可以用不同类型的 Field 来控制文档的哪些内容应该索引,哪些内容应该存储。如果想获取更多的关于 Lucene 的域的信息,可以参考 Lucene 的帮助文档。代码的第二行和第三行为文档添加了两个域,每个域包含两个属性,分别是域的名字和域的内容。在我们的例子中两个域的名字分别是 "content"和"path"。分别存储了我们需要索引的文本文件的内容和路径。最后一行把准备好的文档添加到了索引当中。

当我们把文档添加到索引中后,不要忘记关闭索引,这样才保证 Lucene 把添加的文档写回到硬盘上。下面的一句代码演示了如何关闭索引。


indexWriter.close();
            

利用清单1中的代码,你就可以成功的将文本文档添加到索引中去。接下来我们看看对索引进行的另外一种重要的操作,从索引中删除文档。

从索引中删除文档

类IndexReader负责从一个已经存在的索引中删除文档,如清单2所示。


清单2:从索引中删除文档
File   indexDir = new File("C:\\luceneIndex");
            IndexReader ir = IndexReader.open(indexDir);
            ir.delete(1);
            ir.delete(new Term("path","C:\\file_to_index\lucene.txt"));
            ir.close();
            

       在清单2中,第二行用静态方法 IndexReader.open(indexDir) 初始化了类 IndexReader 的一个实例,这个方法的参数指定了索引的存储路径。类 IndexReader 提供了两种方法去删除一个文档,如程序中的第三行和第四行所示。第三行利用文档的编号来删除文档。每个文档都有一个系统自动生成的编号。第四行删除了路径为"C:\\file_to_index\lucene.txt"的文档。你可以通过指定文件路径来方便的删除一个文档。值得注意的是虽然利用上述代码删除文档使得该文档不能被检索到,但是并没有物理上删除该文档。Lucene 只是通过一个后缀名为 .delete 的文件来标记哪些文档已经被删除。既然没有物理上删除,我们可以方便的把这些标记为删除的文档恢复过来,如清单 3 所示,首先打开一个索引,然后调用方法 ir.undeleteAll() 来完成恢复工作。


清单3:恢复已删除文档
File   indexDir = new File("C:\\luceneIndex");
            IndexReader ir = IndexReader.open(indexDir);
            ir.undeleteAll();
            ir.close();
            

你现在也许想知道如何物理上删除索引中的文档,方法也非常简单。清单 4 演示了这个过程。


清单4:如何物理上删除文档
File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,false);
            indexWriter.optimize();
            indexWriter.close();
            

在清单 4 中,第三行创建了类 IndexWriter 的一个实例,并且打开了一个已经存在的索引。第 4 行对索引进行清理,清理过程中将把所有标记为删除的文档物理删除。

Lucene 没有直接提供方法对文档进行更新,如果你需要更新一个文档,那么你首先需要把这个文档从索引中删除,然后把新版本的文档加入到索引中去。

提高索引性能

      利用 Lucene,在创建索引的工程中你可以充分利用机器的硬件资源来提高索引的效率。当你需要索引大量的文件时,你会注意到索引过程的瓶颈是在往磁盘上写索引文件的过程中。为了解决这个问题, Lucene 在内存中持有一块缓冲区。但我们如何控制 Lucene 的缓冲区呢?幸运的是,Lucene 的类 IndexWriter 提供了三个参数用来调整缓冲区的大小以及往磁盘上写索引文件的频率。

1.合并因子(mergeFactor)

这个参数决定了在 Lucene 的一个索引块中可以存放多少文档以及把磁盘上的索引块合并成一个大的索引块的频率。比如,如果合并因子的值是 10,那么当内存中的文档数达到 10 的时候所有的文档都必须写到磁盘上的一个新的索引块中。并且,如果磁盘上的索引块的隔数达到 10 的话,这 10 个索引块会被合并成一个新的索引块。这个参数的默认值是 10,如果需要索引的文档数非常多的话这个值将是非常不合适的。对批处理的索引来讲,为这个参数赋一个比较大的值会得到比较好的索引效果。

2.最小合并文档数

这个参数也会影响索引的性能。它决定了内存中的文档数至少达到多少才能将它们写回磁盘。这个参数的默认值是10,如果你有足够的内存,那么将这个值尽量设的比较大一些将会显著的提高索引性能。

3.最大合并文档数

这个参数决定了一个索引块中的最大的文档数。它的默认值是 Integer.MAX_VALUE,将这个参数设置为比较大的值可以提高索引效率和检索速度,由于该参数的默认值是整型的最大值,所以我们一般不需要改动这个参数。

清单 5 列出了这个三个参数用法,清单 5 和清单 1 非常相似,除了清单 5 中会设置刚才提到的三个参数。


清单5:提高索引性能
/**
            * This class demonstrates how to improve the indexing performance
            * by adjusting the parameters provided by IndexWriter.
            */
            public class AdvancedTextFileIndexer  {
            public static void main(String[] args) throws Exception{
            //fileDir is the directory that contains the text files to be indexed
            File   fileDir  = new File("C:\\files_to_index");
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            File[] textFiles  = fileDir.listFiles();
            long startTime = new Date().getTime();
            int mergeFactor = 10;
            int minMergeDocs = 10;
            int maxMergeDocs = Integer.MAX_VALUE;
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            indexWriter.mergeFactor = mergeFactor;
            indexWriter.minMergeDocs = minMergeDocs;
            indexWriter.maxMergeDocs = maxMergeDocs;
            //Add documents to the index
            for(int i = 0; i < textFiles.length; i++){
            if(textFiles[i].isFile() && textFiles[i].getName().endsWith(".txt")){
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Keyword("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("MergeFactor: " + indexWriter.mergeFactor);
            System.out.println("MinMergeDocs: " + indexWriter.minMergeDocs);
            System.out.println("MaxMergeDocs: " + indexWriter.maxMergeDocs);
            System.out.println("Document number: " + textFiles.length);
            System.out.println("Time consumed: " + (endTime - startTime) + " milliseconds");
            }
            }
            

通过这个例子,我们注意到在调整缓冲区的大小以及写磁盘的频率上面 Lucene 给我们提供了非常大的灵活性。现在我们来看一下代码中的关键语句。如下的代码首先创建了类 IndexWriter 的一个实例,然后对它的三个参数进行赋值。


int mergeFactor = 10;
            int minMergeDocs = 10;
            int maxMergeDocs = Integer.MAX_VALUE;
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            indexWriter.mergeFactor = mergeFactor;
            indexWriter.minMergeDocs = minMergeDocs;
            indexWriter.maxMergeDocs = maxMergeDocs;
            

下面我们来看一下这三个参数取不同的值对索引时间的影响,注意参数值的不同和索引之间的关系。我们为这个实验准备了 10000 个测试文档。表 1 显示了测试结果。


表1:测试结果
表1:测试结果

        通过表 1,你可以清楚地看到三个参数对索引时间的影响。在实践中,你会经常的改变合并因子和最小合并文档数的值来提高索引性能。只要你有足够大的内存,你可以为合并因子和最小合并文档数这两个参数赋尽量大的值以提高索引效率,另外我们一般无需更改最大合并文档数这个参数的值,因为系统已经默认将它设置成了最大。

Lucene 索引文件结构分析

        在分析 Lucene 的索引文件结构之前,我们先要理解反向索引(Inverted index)这个概念,反向索引是一种以索引项为中心来组织文档的方式,每个索引项指向一个文档序列,这个序列中的文档都包含该索引项。相反,在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。你可以利用反向索引轻松的找到那些文档包含了特定的索引项。Lucene正是使用了反向索引作为其基本的索引结构。

索引文件的逻辑视图

在Lucene 中有索引块的概念,每个索引块包含了一定数目的文档。我们能够对单独的索引块进行检索。图 2 显示了 Lucene 索引结构的逻辑视图。索引块的个数由索引的文档的总数以及每个索引块所能包含的最大文档数来决定。


图2:索引文件的逻辑视图
图2:索引文件的逻辑视图

Lucene 中的关键索引文件

下面的部分将会分析Lucene中的主要的索引文件,可能分析有些索引文件的时候没有包含文件的所有的字段,但不会影响到对索引文件的理解。

1.索引块文件

这个文件包含了索引中的索引块信息,这个文件包含了每个索引块的名字以及大小等信息。表 2 显示了这个文件的结构信息。


表2:索引块文件结构
表2:索引块文件结构

2.域信息文件

我们知道,索引中的文档由一个或者多个域组成,这个文件包含了每个索引块中的域的信息。表 3 显示了这个文件的结构。


表3:域信息文件结构
表3:域信息文件结构

3.索引项信息文件

这是索引文件里面最核心的一个文件,它存储了所有的索引项的值以及相关信息,并且以索引项来排序。表 4 显示了这个文件的结构。


表4:索引项信息文件结构
表4:索引项信息文件结构

4.频率文件

这个文件包含了包含索引项的文档的列表,以及索引项在每个文档中出现的频率信息。如果Lucene在索引项信息文件中发现有索引项和搜索词相匹配。那么 Lucene 就会在频率文件中找有哪些文件包含了该索引项。表5显示了这个文件的一个大致的结构,并没有包含这个文件的所有字段。


表5:频率文件的结构
表5:频率文件的结构

5.位置文件

这个文件包含了索引项在每个文档中出现的位置信息,你可以利用这些信息来参与对索引结果的排序。表 6 显示了这个文件的结构


表6:位置文件的结构
表6:位置文件的结构

到目前为止我们介绍了 Lucene 中的主要的索引文件结构,希望能对你理解 Lucene 的物理的存储结构有所帮助。

总结

目前已经有非常多的知名的组织正在使用 Lucene,比如,Lucene 为 Eclipse 的帮助系统,麻省理工学院的 OpenCourseWare 提供了搜索功能。通过阅读这篇文章,希望你能对 Lucene 的索引机制有所了解,并且你会发现利用 Lucene 创建索引是非常简单的事情。

转载于http://www.128kj.com/article/article5/lucene4.html

posted @ 2008-03-31 20:39 鲁胜迪 阅读(86) | 评论 (0)编辑 收藏

仅列出标题  下一页

导航

统计

常用链接

留言簿

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜

60天内阅读排行