lucene中文分词组件(词典全切分算法)下载

下载地址:
http://groups-beta.google.com/group/SegWord/web/IKAnalyzer.jar

IKAnalyzer基于lucene2.0版本API开发,实现了以词典分词为基础的正反向全切分算法,是Lucene Analyzer接口的实现,代码使用例子如下:

import org.mira.lucene.analysis.IK_CAnalyzer <------- 引用类
import .....

public class IKAnalyzerTest extends TestCase {

RAMDirectory directory;
private IndexSearcher searcher;

public void setUp() throws Exception {

directory = new RAMDirectory();

 

IndexWriter writer = new IndexWriter(directory,
new IK_CAnalyzer(), <------- 实例化类
true);

Document doc = new Document();
doc.add(Field.Keyword("partnum", "Q36"));
doc.add(Field.Text("description", "Illidium Space Modulator"));
writer.addDocument(doc);
writer.close();
searcher = new IndexSearcher(directory);

}

public void testTermQuery() throws Exception {
Query query = new TermQuery(new Term("partnum", "Q36"));
Hits hits = searcher.search(query);
assertEquals(1, hits.length());
}
}

分词效果测试,命令行如下:
java -classpath IKAnalyzer.jar;lucene-core-2.0.0.jar org.mira.lucene.analysis.IK_CAnalyzer 中华人民共和国香港特别行政区


该算法适合与互联网用户的搜索习惯和企业知识库检索,用户可以用句子中涵盖的中文词汇搜索,如用“人民”搜索含“人民币”的文章,这是大部分用户的搜索思维;
不适合用于知识挖掘和网络爬虫技术,全切分法容易造成知识歧义,因为在语义学上“人民”和“人民币”是完全搭不上关系的。

分词效果:

1.实现中文单词细粒度全切分

如:中华人民共和国
0 - 2 = 中华
0 - 4 = 中华人民
0 - 7 = 中华人民共和国
1 - 3 = 华人
2 - 4 = 人民
2 - 7 = 人民共和国
4 - 6 = 共和
4 - 7 = 共和国

2.实现对专有名词的识别和切分(人名,公司名)

如:陈文平是开睿动力通讯科技有限公司董事长
0 - 3 = 陈文平 <------ 人名,非汉语词汇
4 - 6 = 开睿 <------ 公司名,非汉语词汇
6 - 8 = 动力
8 - 10 = 通讯
10 - 12 = 科技
12 - 14 = 有限
12 - 16 = 有限公司
14 - 16 = 公司
16 - 18 = 董事
16 - 19 = 董事长
18 - 19 = 长

3.对数词和量词的合理切分

如:据路透社报道,印度尼西亚社会事务部一官员星期二(29日)表示,日惹市附近当地时间27日晨5时53分发生的里氏6.2级地震已经造成至少5427人死亡,20000余人受伤,近20万人无家可归。
0 - 1 = 据
1 - 4 = 路透社
4 - 6 = 报道
。。。。。。
18 - 20 = 官员
20 - 22 = 星期
20 - 23 = 星期二
22 - 23 = 二
24 - 26 = 29
24 - 27 = 29日
26 - 27 = 日
28 - 30 = 表示
31 - 33 = 日惹
33 - 34 = 市
。。。。。。
40 - 42 = 27
40 - 43 = 27日
43 - 44 = 晨
44 - 45 = 5
44 - 46 = 5时
45 - 46 = 时
46 - 48 = 53
46 - 49 = 53分
48 - 50 = 分发
。。。。。。
52 - 54 = 里氏
54 - 57 = 6.2
54 - 58 = 6.2级
57 - 58 = 级
58 - 60 = 地震
。。。。。。
66 - 70 = 5427
66 - 71 = 5427人
71 - 73 = 死亡
72 - 73 = 亡
74 - 79 = 20000
79 - 81 = 余人
81 - 83 = 受伤
84 - 85 = 近
85 - 87 = 20
85 - 89 = 20万人
87 - 89 = 万人
89 - 93 = 无家可归

转载于 http://www.jdon.com/jivejdon/thread/30255.html

posted @ 2008-06-06 14:10 鲁胜迪 阅读(1082) | 评论 (0)编辑 收藏

关于无刷新显示实时数据库程序问题

现在实现的效果是,可以用1.jsp页面调用2.jsp页面,但是问题在于1.jsp页面只能调用编译后的2.jsp
就是说,当我改变数据库中相应内容的值以后,它就不能实现做出相应的改变了。
但是当我将1.jsp和2.jsp运行时,这个问题就解决了,因为在2.jsp实现了自动刷新功能,它可以定时的去获取数据库的值。

抛出问题:那是否可以在同一页面中导入两张jsp页面,然后实现一个通过刷新实现对另一个不刷新页面的更新呢?



客户端页面代码(1.jsp)如下

<html>
 <head>
 <script language="javascript">
 
         var xmlHttp;

         function createXMLHttpRequest(){
            if (window.ActiveXObject){
                 xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
            }else if(window.XMLHttpRequest){
                 xmlHttp = new XMLHttpRequest();
            }
        }

   function re(){
            createXMLHttpRequest();      
            xmlHttp.open("GET","2.jsp",true);
            xmlHttp.onreadystatechange = callBack;
            xmlHttp.send(null);
          }
  
   function callBack(){
            if (xmlHttp.readyState == 4) {
             if (xmlHttp.status == 200) {
                var str = xmlHttp.responseText;
      t.innerHTML=str;
               setTimeout('re()',1000);
                    }
              }
         }

</script>
</head>
<body onload="javascript:re();">
<span id=t>123</span>
</body>

_____________________________________

服务器端代码(2.jsp)如下

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<jsp:directive.page import="java.sql.ResultSet"/>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <meta http-equiv="Refresh" content="10">
  <jsp:useBean id="db"  scope="session" class="news.sql_data"/>
<body>

<%
 String sql="select * from hzt_task t where title='lsd' ";
  
 ResultSet rs=db.executeQuery(sql);
  
 //int i=0;
  
 while(rs.next()){

 // runpercent=rs.getString("runpercent");
     out.println(rs.getString("runpercent"));
  
 // i++;
 }
 %>
</body>
</html>

posted @ 2008-06-02 15:56 鲁胜迪 阅读(1020) | 评论 (1)编辑 收藏

javascript传值给jsp 简单实例

<body>
<div id='aa'>
sdfdsfdsf
<input name="" type="button" onclick="aa();"/>
</div>
<script language="javascript">
 function aa(){
  document.getElementById('aa').innerHTML='123123';
 }
</script>
</body>

posted @ 2008-05-19 15:16 鲁胜迪 阅读(4438) | 评论 (4)编辑 收藏

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 鲁胜迪 阅读(5293) | 评论 (0)编辑 收藏

动态显示进度条


可以将下边的代码嵌入到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 鲁胜迪 阅读(414) | 评论 (0)编辑 收藏

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 鲁胜迪 阅读(21067) | 评论 (12)编辑 收藏

远程操作、远程协助

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

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

 

图1 连接远程桌面

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

 

图2 开启远程桌面

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

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

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 鲁胜迪 阅读(3222) | 评论 (0)编辑 收藏

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 鲁胜迪 阅读(177) | 评论 (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 鲁胜迪 阅读(1787) | 评论 (0)编辑 收藏

仅列出标题
共12页: First 上一页 4 5 6 7 8 9 10 11 12 下一页 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(4)

随笔分类

随笔档案

文章分类

新闻分类

搜索

最新评论

阅读排行榜

评论排行榜