February 13, 2007
用Lucene-1.3-final为网站数据库建立索引
以下是看了lnboy写的《用lucene建立大富翁论坛的全文检索》后写的测试代码。
为数据库cwb.mdb建立全文索引的indexdb.jsp
<%@ page import ="org.apache.lucene.analysis.standard.*" %>
<%@ page import="org.apache.lucene.index.*" %>
<%@ page import="org.apache.lucene.document.*" %>
<%@ page import="lucene.*" %>
<%@ page contentType="text/html; charset=GBK" %>
<%
long start = System.currentTimeMillis();
String aa=getServletContext().getRealPath("/")+"index";
IndexWriter writer = new IndexWriter(aa, new StandardAnalyzer(),
true);
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance();
继续阅读 "用Lucene-1.3-final为网站数据库建立索引" »
Posted by kevinwu 03:02 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
July 09, 2006
Applications and web applications using Lucene
PoweredBy - Jakarta-lucene Wiki
Posted by kevinwu 12:19 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
July 08, 2006
用Lucene建立索引及查询示例
首先去 apache 网站下载 lucene 的开发包,并配置好环境变量
http://jakarta.apache.org/lucene/docs/index.html
建立索引程序:
/*
* Created on 2004-4-26
*/
import org.apache.lucene.index.*;
import org.apache.lucene.analysis.standard.*;
import org.apache.lucene.document.*;
import java.io.*;
/**
* @author bell.wang
*/
public class IndexFiles {
public static void main(String[] args) {
try{
IndexWriter writer = new IndexWriter("myindex", new
StandardAnalyzer(), true);
File files = new File("mydoc");
String[] Fnamelist = files.list();
for (int i = 0; i < Fnamelist.length; i++){
File file = new File(files,Fnamelist[i]);
Document doc = new Document();
Field fld = Field.Text("path", file.getPath());
doc.add(fld);
fld = Field.Keyword("modified",
DateField.timeToString(file.lastModified()));
doc.add(fld);
FileInputStream in = new FileInputStream(file);
Reader reader = new BufferedReader(new InputStreamReader(in));
fld = Field.Text("contents", reader);
doc.add(fld);
writer.addDocument(doc);
System.out.println("Added : " + doc.get("path"));
}
writer.optimize();
writer.close();
System.out.println("Has Added Total: " + Fnamelist.length);
}catch(Exception e){
System.out.println(e);
}
}
}
程序对当前路径下mydoc目录下所有文件建立索引,其中索引有三个字段: 文件路径,
最后修改时间,文件内容. 建立的索引文件在当前路径下的myindex目录
检索程序:
/*
* Created on 2004-4-26
*
*/
import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.standard.*;
import org.apache.lucene.search.*;
import org.apache.lucene.queryParser.*;
import org.apache.lucene.document.*;
//import com.augmentum.hrms.*;
import java.util.Date;
/**
* @author bell.wang
*
*/
public class SearchFile {
public static void main(String[] args) {
//XMap a = new XMap("");
Analyzer anlzr = new StandardAnalyzer();
try{
Query q = QueryParser.parse("数据库", "contents", anlzr);
System.out.println("Searching for : " + q.toString("contents"));
Searcher serch = new IndexSearcher("myindex");
Hits hts = serch.search(q);
for(int i=0; i<hts.length(); i++){
Document doc = hts.doc(i);
String path = doc.get("path");
System.out.println("Find: " +i+": "+ path);
System.out.println("Find: " + doc.get("modified"));
System.out.println("Find: " + doc.get("path"));
}
System.out.println("Find Total: " + hts.length());
}catch(Exception e){
System.out.println(e);
}
}
}
程序对索引的contents字段用“数据库“关键字进行查询,返回
的是所有包含有关键字的文档集合,分别打印出各个字段.
上面的程序我用纯文本文件测试通过,.txt,.jsp,.html 都可以,
word,pdf 等文件需要经过转化才能对其进行索引。
Posted by kevinwu 09:27 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
lucene 学习笔记 2
Boosting特性
luncene对Document和Field提供了一个可以设置的Boosting参数, 这个参数的用处是告诉lucene, 某些记录更重要,在
搜索的时候优先考虑他们 比如在搜索的时候你可能觉得几个门户的网页要比垃圾小站更优先考虑
lucene默认的boosting参数是1.0, 如果你觉得这个field重要,你可以把boosting设置为1.5, 1.2....等,
对Document设置boosting相当设定了它的每个Field的基准boosting,到时候实际Field的boosting就是
(Document-boosting*Field-boosting)设置了一遍相同的boosting.
似乎在lucene的记分公式里面有boosting参数,不过我估计一般人是不会去研究他的公式的(复杂),而且公式也无法给出最佳值,所以我们所能
做的只能是一点一点的改变boosting, 然后在实际检测中观察它对搜索结果起到多大的作用来调整
一般的情况下是没有必要使用boosting的, 因为搞不好你就把搜索给搞乱了, 另外如果是单独对Field来做Bossting, 也可以通过将
这个Field提前来起到近似的效果
Indexing Date
日期是lucene需要特殊考虑的地方之一, 因为我们可能需要对日期进行范围搜索, Field.keyword(string,Date)提供了这
样的方法,lucene会把这个日期转换为string, 值得注意的是这里的日期是精确到毫秒的,可能会有不必要的性能损失, 所以我们也可以把日期
自行转化为YYYYMMDD这样的形势,就不用精确到具体时间了,通过File.keyword(Stirng,String) 来index, 使用
PrefixQuery 的YYYY一样能起到简化版的日期范围搜索(小技巧), lucene提到他不能处理1970年以前的时间,似乎是上一代电脑
系统遗留下来的毛病
Indexing 数字
如果数字只是简单的数据, 比如中国有56个民族. 那么可以简单的把它当字符处理
如果数字还包含数值的意义,比如价格, 我们会有范围搜索的需要(20元到30元之间的商品),那么我们必须做点小技巧, 比如把3,34,100 这
三个数字转化为003,034,100 ,因为这样处理以后, 按照字符排序和按照数值排序是一样的,而lucene内部按照字符排序,003-
>034->100 NOT(100->3->34)
排序
Lucene默认按照相关度(score)排序,为了能支持其他的排序方式,比如日期,我们在add Field的时候,必须保证field被
Index且不能被tokenized(分词),并且排序的只能是数字,日期,字符三种类型之一
Lucene的IndexWriter调整
IndexWriter提供了一些参数可供设置,列表如下
属性 默认值 说明
mergeFactor org.apache.lucene.mergeFactor 10 控制index的大小和频率,两个作用
maxMergeDocs org.apache.lucene.maxMergeDocs Integer.MAX_VALUE 限制一个段中的
document数目
minMergeDocs org.apache.lucene.minMergeDocs 10 缓存在内存中的document数目,超过他以后会
写入到磁盘
maxFieldLength 1000 一个Field中最大Term数目,超过部分忽略,不会index到field中,所以自然也就搜索不
到
这些参数的的详细说明比较复杂:mergeFactor有双重作用
设置每mergeFactor个document写入一个段,比如每10个document写入一个段
设置每mergeFacotr个小段合并到一个大段,比如10个document的时候合并为1小段,以后有10个小段以后合并到一个大段,有10个大
段以后再合并,实际的document数目会是mergeFactor的指数
简单的来说mergeFactor 越大,系统会用更多的内存,更少磁盘处理,如果要打批量的作index,那么把mergeFactor设置大没
错, mergeFactor 小了以后, index数目也会增多,searhing的效率会降低,但是mergeFactor增大一点一点,内存消
耗会增大很多(指数关系),所以要留意不要"out of memory"
把maxMergeDocs设置小,可以强制让达到一定数量的document写为一个段,这样可以抵消部分mergeFactor的作用.
minMergeDocs相当于设置一个小的cache,第一个这个数目的document会留在内存里面,不写入磁盘。这些参数同样是没有最佳值
的, 必须根据实际情况一点点调整。
maxFieldLength可以在任何时刻设置, 设置后,接下来的index的Field会按照新的length截取,之前已经index的部分不
会改变。可以设置为Integer.MAX_VALUE
RAMDirectory 和 FSDirectory 转化
RAMDirectory(RAMD)在效率上比FSDirectyr(FSD)高不少, 所以我们可以手动的把RAMD当作FSD的buffer,这
样就不用去很费劲的调优FSD那么多参数了,完全可以先用RAM跑好了index,周期性(或者是别的什么算法)来回写道FSD中。 RAMD完全可以
做FSD的buffer。
为查询优化索引(index)
Indexwriter.optimize()方法可以为查询优化索引(index),之前提到的参数调优是为indexing过程本身优化,而这里是
为查询优化,优化主要是减少index文件数,这样让查询的时候少打开文件,优化过程中,lucene会拷贝旧的index再合并,合并完成以后删除旧
的index,所以在此期间,磁盘占用增加, IO符合也会增加,在优化完成瞬间,磁盘占用会是优化前的2倍,在optimize过程中可以同时作
search。
并发操作Lucene和locking机制
所有只读操作都可以并发
在index被修改期间,所有只读操作都可以并发
对index修改操作不能并发,一个index只能被一个线程占用
index的优化,合并,添加都是修改操作
IndexWriter和IndexReader的实例可以被多线程共享,他们内部是实现了同步,所以外面使用不需要同步
Locing
lucence内部使用文件来locking,默认的locking文件放在java.io.tmpdir,可以通过-
Dorg.apache.lucene.lockDir=xxx指定新的dir,有write.lock commit.lock两个文件,lock文
件用来防止并行操作index,如果并行操作, lucene会抛出异常,可以通过设置-DdisableLuceneLocks=true来禁止
locking,这样做一般来说很危险,除非你有操作系统或者物理级别的只读保证,比如把index文件刻盘到CDROM上。
调试IndexWriter
IndexWriter 有一个infoStream的变量,调试信息从这里输出。可以把System.out设置给它
Posted by kevinwu 09:16 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
用java实现全文检索[zhanglihai.com]
用java实现全文检索
张利海 于 2004年11月24日 12:45 发表
关键词 : 全文检索 lucene
在本文我又提到lucene了,在java业界,提到全文检索,几乎没有什么人不知道它。
用google搜索一下,满世界都是有关资料。具有代表性的就是车东的“基于Java的全文索引引擎Lucene简介”,
我要写的也就只有最简单的三板斧,再加上支持中文的ChineseAnalyzer以及按照时间排序的搜索结果排序方法。
这些都可以在其他地方找到相关资料,我只是把他们提出来,作为lucence应用中经常遇到的麻烦解决办法。
去年MSN上面有个朋友跟我提到希望用lucene构建个网站的全文检索,我当时就觉得很简单,直说没问题没问题,
不过他提到一个要求就是搜索结果要安装时间排序,我查阅了些资料,发现lucene并不提供用户自定义排序方式,
而只能按照自己相关性算法排序。后来我在车东的weblucene项目找到了IndexOrderSearcher。
解决了结果排序常规需求。
IndexOrderSearcher跟一般IndexSearch使用差不多,仅仅在构建对象的时候多加一个参数
IndexOrderSearcher.ORDER_BY_DOCID_DESC
IndexOrderSearcher indexsearcher = new IndexOrderSearcher("/home/
lucenetest/index",IndexOrderSearcher.ORDER_BY_DOCID_DESC);
新版本的lucene还提供了一个MultiFieldQueryParser,可以同时检索多个字段,以前QueryParser比较麻烦。
private static ChineseAnalyzer chineseAnalyzer = new
ChineseAnalyzer();
public Hits search(String queryText){
if (queryText == null){
return null;
}
Query query;
try{
query = MultiFieldQueryParser.parse(queryText, new String[]
{"title"},chineseAnalyzer);
return indexsearcher.search(query);
}catch(Exception e){
return null;
}
}
下面是构建索引,定时从数据库取出数据索引,做完记录完成时间,我是把时间写入一个txt文件。
package com.test.search;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.cn.*;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import java.io.*;
import java.sql.*;
import java.util.Date;
import com.test.db.*;
import com.test.utility.*;
/**
* Title: SearchIndexer
* Description: 全文索引
* Copyright: Copyright (c) 2001
* Company: test
* @author Sean
* @version 1.0
*/
public class SearchIndexer {
private String indexPath = null;
protected Analyzer analyzer = new ChineseAnalyzer();
public SearchIndexer(String s) {
this.indexPath = s;
}
/**
* 索引某日期以前的所有文档
* @param fromdate
* @return
*/
public final void updateIndex(String fromdate) {
Connection conn = DbUtil.getCon();
IndexWriter indexWriter = null;
try {
indexWriter = getWriter(false);
//索引发布系统内部文件
PreparedStatement pstm = conn.prepareStatement(
"select title,body,creationtime from document where
creationtime > '" + fromdate +
"' order by creationtime");
ResultSet rs = pstm.executeQuery();
while (rs.next()) {
String creationtime = rs.getString("creationtime");
String title = rs.getString("title");
String body = rs.getString("body");
if (title == null || body == null) {
continue;
}
try {
addDocsToIndex(title,body, creationtime,indexWriter);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
indexWriter.optimize();
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
try {
indexWriter.close();
conn.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 检查索引文件是否存在
* @param s
* @return 索引是否存在
*/
private boolean indexExists(String s) {
File file = new File(s + File.separator + "segments");
return file.exists();
}
/**
* 增加一组索引
* @param title
* @param body
* @param creationtime
* @param indexwriter
* @return
*/
private final void addNewsToIndex(String docid, String url,String
title, String body,
String ptime, IndexWriter
indexwriter) throws
IOException {
if (indexwriter == null) {
return;
}
else {
try {
Document document = new Document();
document.add(Field.Text("title", title));
document.add(Field.Text("body", body));
document.add(new Field("creationtime", creationtime, true,
true, false));
indexwriter.addDocument(document);
}
catch (Exception ex) {
ex.printStackTrace();
}
return;
}
}
/**
* 取得IndexWriter
* @param flag 是否新建索引
* @return IndexWriter
*/
private IndexWriter getWriter(boolean flag) throws IOException {
String s = indexPath;
if (s == null) {
throw new IOException("索引文件路径设置错误.");
}
indexPath = s + File.separator + "search";
IndexWriter indexwriter = null;
if (flag) {
try {
indexwriter = new IndexWriter(indexPath, analyzer, true);
}
catch (Exception exception) {
System.err.println("ERROR: Failed to create a new index
writer.");
exception.printStackTrace();
}
}
else {
if (indexExists(indexPath)) {
try {
indexwriter = new IndexWriter(indexPath, analyzer, false);
}
catch (Exception exception1) {
System.err.println("ERROR: Failed to open an index
writer.");
exception1.printStackTrace();
}
}
else {
try {
indexwriter = new IndexWriter(indexPath, analyzer, true);
}
catch (Exception exception2) {
System.err.println("ERROR: Failed to create a new index
writer.");
exception2.printStackTrace();
}
}
}
return indexwriter;
}
public static void main(String[] args) {
String lastUpdate = "/home/lucenetest/lastUpdate.txt";
SearchIndexer searchIndexer = new SearchIndexer("/home/lucenetest/
index");
//取出上次更新时间
String str = Util.readTxtFile(lastUpdate);
if(str==null || str.length()==0){
str = new java.util.Date().toString();
}
searchIndexer.updateIndex(str);
//写入当前时间
Util.writeTxtFile(lastUpdate,new java.util.Date(),false);
}
}
写个cmd或者sh在相应操作系统下面定时执行SearchIndexer就可以了。
Posted by kevinwu 09:14 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
用PHP调用Lucene包来实现全文检索[转]
作者:张杰
URL:http://spaces.msn.com/members/newbdez33/
http://www.phpboom.com/
由于工作需要,需要使用PHP实现对网站内大量数量进行全文检索,
而且目前最流行的全文检索的搜索引擎库就是Lucene了,
它是Apache Jakarta的一个子项目,并且提供了简单实用的API,
用这些API,就可以对任何基本文本的数据(包括数据库)进行全文检索。
因为PHP本身就支持调用外部Java类,所以先用Java写了一个类,
这个类通过调用Lucene的API,实现了两个方法:
public String createIndex(String indexDir_path,String dataDir_path)
public String searchword(String ss,String index_path)
其中createIndex是创建索引方法,
传入了两个参数分别是indexDir_path(索引文件的目录),dataDir_path(被索引的文件目录),返回被索引的文件列表字符串,
另一个是searchword,通过传入的关键字参数(ss)对索引进行检索,index_path就是索引文件的目录。返回所有检索到的文件。
这里是源代码,很简单,大家可以参考一下:TxtFileIndexer.java
而PHP程序就调用这两个方法,实现对Lucene的调用,从而达到全文检索的目的。
PHP的调用方法如下:
先创建一个我们写的TxtFileIndexer类的实例,
$tf = new Java('TestLucene.TxtFileIndexer');
然后就按正常PHP类的调用方法的方式进行调用,首先创建索引:
$data_path = "F:/test/php_lucene/htdocs/data/manual"; //定义被索引内容的目
录
$index_path = "F:/test/php_lucene/htdocs/data/search"; //定义生成的索引文件
存放目录
$s = $tf->createIndex($index_path,$data_path); //调用Java类的方法
print $s; //打印返回的结果
这次再试试检索:
$index_path = "F:/test/php_lucene/htdocs/data/search"; //定义生成的索引文件
存放目录
$s = $tf->searchword("here is keyword for search",$index_path);
print $s;
另外要注意Java类的路径,可以在PHP里设置
java_require("F:/test/php_lucene/htdocs/lib/"); //这是个例子,我的类和Lucene
都放到这个目录下
这样就可以了,是不是很简单。
PHP源代码:test.php
接下来我把环境配置说一下,
首先需要有Java SDK,是必须的,我使用的是1.4.2版的,其它版本应该也没问题。
PHP5,试过PHP4,应该可以。
由于PHP5带的Java扩展没调通,并且以前用过调用Java效率很低,很慢,所以使用了 Php/Java Bridge 这个项目。
1.下载JavaBridge
URL:http://sourceforge.net/projects/php-java-bridge/
目前版本是
php-java-bridge_3.0.8_j2ee.zip
解包后把
JavaBridge\WEB-INF\cgi\java-x86-windows.dll
JavaBridge\WEB-INF\lib\JavaBridge.jar
复制到 c:\php\ext 目录下,并把
java-x86-windows.dll 改名为 php_java.dll
2.修改php.ini(例)
extension=php_java.dll
[Java]
java.class.path = "C:\php\ext\JavaBridge.jar;F:\test\php_lucene
\htdocs"
java.java_home = "C:\j2sdk1.4.2_10"
java.library.path = "c:\php\ext;F:\test\php_lucene\htdocs"
3.重启Apache即可。
4.可以找一些文件进行索引
在test.php里可以修改索引文件和数据文件的路径。
TxtFileIndexer.java的37行限制了只索引html后缀的文件,有需要也可以修改。
根据目前的情况(JavaBridge支持Linux和Freebsd),完全可以在
linux或freebsd/apache2/php4/lucene/JavaBridge
环境下运行。
Posted by kevinwu 09:11 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
PHP / Java Integration
PHP / Java Integration
简介
There are two possible ways to bridge PHP and Java: you can either
integrate PHP into a Java Servlet environment, which is the more
stable and efficient solution, or integrate Java support into PHP. The
former is provided by a SAPI module that interfaces with the Servlet
server, the latter by this Java extension.
The Java extension provides a simple and effective means for creating
and invoking methods on Java objects from PHP. The JVM is created
using JNI, and everything runs in-process.
....
原文链接:http://nio.infor96.com/phpdoc-zh/ref.java.html
Posted by kevinwu 08:38 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
Lucene学习笔记
本文主要介绍了Lucene的起源、发展、现状,以及Luence的初步应用,可以作为了解和学习Lucene的入门资料。
1.起源与发展
Lucene是一个高性能、纯Java的全文检索引擎,而且免费、开源。Lucene几乎适合于任何需要全文检索的应用,尤其是跨平台的应用。
Lucene的作者Doug Cutting是一个资深的全文检索专家,刚开始,Doug Cutting将Lucene发表在自己的主页上,2000
年3月将其转移到sourceforge,于2001年10捐献给Apache,作为Jakarta的一个子工程。
2.使用现状
经过多年的发展,Lucene在全文检索领域已经有了很多的成功案例,并积累了良好的声誉。
基于Lucene的全文检索产品(Lucene本身只是一个组件,而非一个完整的应用)和应用Lucene的项目在世界各地已经非常之多,比较知名的
有:
l Eclipse:主流Java开发工具,其帮助文档采用Lucene作为检索引擎
l Jive:知名论坛系统,其检索功能基于Lucene
l Ifinder:出自德国的网站检索系统,基于Lucene(http://ifinder.intrafind.org/)
l MIT DSpace Federation:一个文档管理系统(http://www.dspace.org/)
国内外采用Lucene作为网站全文检索引擎的也很多,比较知名的有:
l http://www.blogchina.com/weblucene/
(更多案例,请参见http://wiki.apache.org/jakarta-lucene/PoweredBy)
在所有这些案例中,开源应用占了很大一部分,但更多的还是商化业产品和网站。毫不夸张的说,Lucene的出现,极大的推动了全文检索技术在各个行业或
领域中的深层次应用。
3.初步应用
前面提到,Lucene本身只是一个组件,而非一个完整的应用,所以若想让Lucene跑起来,还得在Lucene基础上进行必要的二次开发。
下载与安装
首先,你需要到Lucene的官方网站http://jakarta.apache.org/lucene/ 去下载一份拷贝,最新版是1.4。下载后
将得到一个名为lucene-1.4-final.zip的压缩文件,将其解压,里面有一个名为lucene- 1.4-final.jar的文件,这
就是Lucene组件包了,若需要在项目使用Lucene,只需要把lucene-1.4-final.jar置于类路径下即可,至于解压后的其他文件
都是参考用的。
接下来,我用Eclipse建立一个工程,实现基于Lucene的建库、记录加载和记录查询等功能。
如上图所示,这是开发完成后的工程,其中有三个源文件
CreateDataBase.java,InsertRecords.java,QueryRecords.java,分别实现建库、入库、检索的功
能。
以下是对这三个源文件的分析。
建库源码及说明
CreateDataBase.java
packagecom.holen.part1;
importjava.io.File;
importorg.apache.lucene.analysis.standard.StandardAnalyzer;
importorg.apache.lucene.index.IndexWriter;
/**
* @authorHolenChen
*初始化检索库
*/
public classCreateDataBase{
publicCreateDataBase(){
}
public intcreateDataBase(Filefile){
intreturnValue=0;
if(!file.isDirectory()){
file.mkdirs();
}
try{
IndexWriterindexWriter=
newIndexWriter(file,newStandardAnalyzer(),true);
indexWriter.close();
returnValue=1;
}catch(Exceptionex){
ex.printStackTrace();
}
returnreturnValue;
}
/**
*传入检索库路径,初始化库
* @paramfile
* @return
*/
public intcreateDataBase(Stringfile){
return this.createDataBase(newFile(file));
}
public static voidmain(String[]args){
CreateDataBasetemp= newCreateDataBase();
if(temp.createDataBase("e:\\lucene\\holendb")==1){
System.out.println("db init succ");
}
}
}
说明:这里最关键的语句是IndexWriterindexWriter=
newIndexWriter(file,newStandardAnalyzer(),true)。
第一个参数是库的路径,也就是说你准备把全文检索库保存在哪个位置,比如main方法中设定的“e:\\lucene\
\holendb”,Lucene支持多库,且每个库的位置允许不同。
第二个参数是分析器,这里采用的是Lucene自带的标准分析器,分析器用于对整篇文章进行分词解析,这里的标准分析器实现对英文(或拉丁文,凡是由字
母组成,由空格分开的文字均可)的分词,分析器将把整篇英文按空格切成一个个的单词(在全文检索里这叫切词,切词是全文检索的核心技术之一,
Lucene默认只能切英文或其他拉丁文,默认不支持中日韩等双字节文字,关于中文切词技术将在后续章节重点探讨)。
第三个参数是是否初始化库,这里我设的是true,true意味着新建库或覆盖已经存在的库,false意味着追加到已经存在的库。这里新建库,所以肯
定需要初始化,初始化后,库目录下只存在一个名为segments的文件,大小为1k。但是当库中存在记录时执行初始化,库中内容将全部丢失,库回复到
初始状态,即相当于新建了该库,所以真正做项目时,该方法一定要慎用。
加载记录源码及说明
InsertRecords.java
packagecom.holen.part1;
importjava.io.File;
importjava.io.FileReader;
importjava.io.Reader;
importorg.apache.lucene.analysis.standard.StandardAnalyzer;
importorg.apache.lucene.document.Document;
importorg.apache.lucene.document.Field;
importorg.apache.lucene.index.IndexWriter;
/**
* @authorHolenChen
*记录加载
*/
public classInsertRecords{
publicInsertRecords(){
}
public intinsertRecords(Stringdbpath,Filefile){
intreturnValue=0;
try{
IndexWriterindexWriter
= newIndexWriter(dbpath,newStandardAnalyzer(),false);
this.addFiles(indexWriter,file);
returnValue=1;
}catch(Exceptionex){
ex.printStackTrace();
}
returnreturnValue;
}
/**
*传入需加载的文件名
* @paramfile
* @return
*/
public intinsertRecords(Stringdbpath,Stringfile){
return this.insertRecords(dbpath,newFile(file));
}
public voidaddFiles(IndexWriterindexWriter,Filefile){
Documentdoc= newDocument();
try{
doc.add(Field.Keyword("filename",file.getName()));
//以下两句只能取一句,前者是索引不存储,后者是索引且存储
//doc.add(Field.Text("content",new FileReader(file)));
doc.add(Field.Text("content",this.chgFileToString(file)));
indexWriter.addDocument(doc);
indexWriter.close();
}catch(Exceptionex){
ex.printStackTrace();
}
}
/**
*从文本文件中读取内容
* @paramfile
* @return
*/
publicStringchgFileToString(Filefile){
StringreturnValue= null;
StringBuffersb= newStringBuffer();
char[]c= new char[4096];
try{
Readerreader= newFileReader(file);
intn=0;
while(true){
n=reader.read(c);
if(n>0){
sb.append(c,0,n);
}else{
break;
}
}
reader.close();
}catch(Exceptionex){
ex.printStackTrace();
}
returnValue=sb.toString();
returnreturnValue;
}
public static voidmain(String[]args){
InsertRecordstemp= newInsertRecords();
Stringdbpath="e:\\lucene\\holendb";
//holen1.txt中包含关键字"holen"和"java"
if(temp.insertRecords(dbpath,"e:\\lucene\\holen1.txt")==1){
System.out.println("add file1 succ");
}
//holen2.txt中包含关键字"holen"和"chen"
if(temp.insertRecords(dbpath,"e:\\lucene\\holen2.txt")==1){
System.out.println("add file2 succ");
}
}
}
说明:这个类里面主要有3个方法
insertRecords(Stringdbpath,Filefile),addFiles(IndexWriterindexWriter,Filefile),chgFileToString(Filefile)。
ChgFileToString方法用于读取文本型文件到一个String变量中。
InsertRecords方法用于加载一条记录,这里是将单个文件入全文检索库,第一个参数是库路径,第二个参数是需要入库的文件。
InsertRecords需要调用addFiles,addFiles是文件入库的真正执行者。AddFiles里有如下几行重点代码:
doc.add(Field.Keyword("filename",file.getName()));
注意,在Lucene里没有严格意义上表,Lucene的表是通过Field类的方法动态构建的,比如Field.Keyword
("filename",file.getName())就相当于在一条记录加了一个字段,字段名为filename,该字段的内容为
file.getName()。
常用的Field方法如下:
方法
切词
索引
存储
用途
Field.Text(String name, String value)
Y
Y
Y
标题,文章内容
Field.Text(String name, Reader value)
Y
Y
N
META信息
Field.Keyword(String name, String value)
N
Y
Y
作者
Field.UnIndexed(String name, String value)
N
N
Y
文件路径
Field.UnStored(String name, String value)
Y
Y
N
与第二种类似
为了更深入的了解全文检索库,我们可以将全文检索库与通常的关系型数据库(如Oracle,Mysql)作一下对比。
全文检索库对关系型数据库对比
对比项
全文检索库(Lucene)
关系型数据库(Oracle)
核心功能
以文本检索为主,插入(insert)、删除(delete)、修改(update)比较麻烦,适合于大文本块的查询。
插入(insert)、删除(delete)、修改(update)十分方便,有专门的SQL命令,但对于大文本块(如CLOB)类型的检索效率低
下。
库
与Oracle类似,都可以建多个库,且各个库的存储位置可以不同。
可以建多个库,每个库一般都有控制文件和数据文件等,比较复杂。
表
没有严格的表的概念,比如Lucene的表只是由入库时的定义字段松散组成。
有严格的表结构,有主键,有字段类型等。
记录
由于没有严格表的概念,所以记录体现为一个对象,在Lucene里记录对应的类是Document。
Record,与表结构对应。
字段
字段类型只有文本和日期两种,字段一般不支持运算,更无函数功能。
在Lucene里字段的类是Field,如document(field1,field2…)
字段类型丰富,功能强大。
record(field1,field2…)
查询结果集
在Lucene里表示查询结果集的类是Hits,如hits(doc1,doc2,doc3…)
在JDBC为例, Resultset(record1,record2,record3...)
两种库对比图如下:
检索源码及说明
QueryRecords.java
packagecom.holen.part1;
importjava.util.ArrayList;
importorg.apache.lucene.analysis.standard.StandardAnalyzer;
importorg.apache.lucene.document.Document;
importorg.apache.lucene.queryParser.QueryParser;
importorg.apache.lucene.search.Hits;
importorg.apache.lucene.search.IndexSearcher;
importorg.apache.lucene.search.Query;
importorg.apache.lucene.search.Searcher;
/**
* @authorHolenChen
*检索查询
*/
public classQueryRecords{
publicQueryRecords(){
}
/**
*检索查询,将结果集返回
* @paramsearchkey
* @paramdbpath
* @paramsearchfield
* @return
*/
publicArrayListqueryRecords(Stringsearchkey,Stringdbpath,Stringsearchfield)
{
ArrayListlist= null;
try{
Searchersearcher= newIndexSearcher(dbpath);
Queryquery
=QueryParser.parse(searchkey,searchfield,newStandardAnalyzer());
Hitshits=searcher.search(query);
if(hits!= null){
list= newArrayList();
inttemp_hitslength=hits.length();
Documentdoc= null;
for(inti=0;i<temp_hitslength;i++){
doc=hits.doc(i);
list.add(doc.get("filename"));
}
}
}catch(Exceptionex){
ex.printStackTrace();
}
returnlist;
}
public static voidmain(String[]args){
QueryRecordstemp= newQueryRecords();
ArrayListlist= null;
list=temp.queryRecords("holen","e:\\lucene\
\holendb","content");
for(inti=0;i<list.size();i++){
System.out.println((String)list.get(i));
}
}
}
说明:该类中Searcher负责查询,并把查询结果以Hits对象集方式返回,Hits好比JDBC中的RecordSet,Hits是
Document的集合,每个Document相当于一条记录,Document中包含一个或多个字段,可以通过Document.get(“字段
名”) 方法得到每个字段的内容。
通过这三个类,就完成了一个简单的基于Lucene的全文检索应用。
4.总结
Lucene十分精练纯粹,就一个jar包,引入到你的工程中,调用其接口,就可以为你的应用增添全文检索功能。
通过上一节的初步应用会发现,Lucene使用起来很简单,与JDBC有些类似,应用时重点掌握好
IndexWriter,Document,Field,Searcher等几个类即可。
Lucene的结构很清晰,每个package司职一项,比如org.apache.Lucene.search负责检索,
org.apache.Lucene.index索引,org.apache.Lucene.analysis切词等,且Lucene的主要动作都采用
了抽象类,扩展起来十分方便。
相对于一些商业化全文检索,Lucene的入库速度更快。因为它的存储采取分步合并的方法,先建立小索引,待时机成熟才把小索引合并到大索引树上。因
此,我们在操作应用数据时可以同步进行全文检索库的操作而不会(或许很少)影响系统的效能。
Lucene性能稳定,使用简单,而且开源免费,有Apache基金在后面做支撑,资金和技术力量都十分雄厚,这两年也一直是稳步更新,每次新版本的推
出,业界均争相报导。
参考资料
1. Introduction to Text Indexing with Apache Jakarta Lucene
2. Lucene Introduction in Chinese
3. Lucene Tutorial
Posted by kevinwu 08:35 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)
Lucene 1.9 改进特性列表
1.9 RC1
注:lucene2.0发布版本并不是100%的和1.4.3版兼容。也就是说在你用2.0版本的Lucene开发包替换原来的1.4.3版本时,应该
让你的应用程序首先和1.9的兼容。
使用前提:
1. 编译和使用Lucene需要 Java1.4 或以上版本。
Lucene 1.9 在运行时的变化:
1. 模糊搜索 FuzzyQuery 不再抛出 TooManyClauses 异常。当 FuzzyQuery 扩展多于
BooleanQuery.maxClauseCount 时,只有最相关的term会被重新写入query,因此避免了异常的抛出。
(Christoph)
2. 把系统属性 "org.apache.lucene.lockdir" 改为
"org.apache.lucene.lockDir"。(Bernhard)
3. RangeQueries 和 FuzzyQueries 默认被转换成小写。 (as it has been the case
for PrefixQueries and WildcardQueries before).使用
setLowercaseExpandedTerms(false) 来禁止大小写自动转换的行为;同样也影响 PrefixQueries
和 WildcardQueries。(Daniel Naber)
4. 在使用 MultiSearcher 的时候文档频率也可以正确计算,全局性的计算各个 subsearchers 和 indices 中。
以前计算的时候只是 locally 的,每个 index 的计算是分开的,这样引发的一个问题是:在多个indices中rank 是不相等的。
(Chuck Williams, Wolf Siberski via Otis, bug #31841)
5. 在打开 IndexWriter 使用 create=true 参数,Lucene 现在只是删除index目录中属于Lucene自己的文
件。( 判断文件名后缀的方式 )。原来是删除整个目录中的所有文件。(Daniel Naber and Bernhard Messer,
bug #34695)
6. IndexReader 的版本 ,可以通过 getCurrentVersion() 和 getVersion() 返回。以前如果是新
的indexes 那么返回的是0 。现在则用系统的毫秒数来初始化。
(Bernhard Messer via Daniel Naber)
7. 一些默认的初始化值不再允许通过 system properties 来设置。相反在 IndexWriter 中新增了相关的 set/
get 方法来设置相关属性。主要包括以下属性:
在 IndexWriter 的 getter/setter 方法中:
org.apache.lucene.writeLockTimeout,
org.apache.lucene.commitLockTimeout,
org.apache.lucene.minMergeDocs, org.apache.lucene.maxMergeDocs,
org.apache.lucene.maxFieldLength,
org.apache.lucene.termIndexInterval,
org.apache.lucene.mergeFactor,
还有 BooleanQuery 的 getter/setter 方法:
org.apache.lucene.maxClauseCount
还有 FSDirectory 的 getter/setter 方法:
disableLuceneLocks
(Daniel Naber)
8. 修改了 FieldCacheImpl 方法使用用户提供的 IntParser 和 FloatParser,来替代使用
Integer 和 Float 的相关方法。
(Yonik Seeley via Otis Gospodnetic)
9. 高级搜索返回的 TopDocs 和 TopFieldDocs 不再规范scores。
(Luc Vanlerberghe via Yonik Seeley, LUCENE-469)
1.9 的新特性:
1. 增加了对压缩字段存储的支持。(patch #31149)
(Bernhard Messer via Christoph)
2. 增加了对压缩字段存储的支持。(patch #29370)
(Bernhard Messer via Christoph)
3. 在 term vectors 中增加了 位置和偏移信息。(Grant Ingersoll & Christoph)
4. 增加了一个新的 DateTools 。允许用户格式化日期到一种更可读的格式,以便于更好的适应索引。DateTools 不像
DateFields 类,它允许日期指定到1970年以前,但必须使用指定的日期格式。这样,在RangeQuerys中使用就更加有效率了。
(Daniel Naber)
5. QueryParser 现在可以正确的和Analyzers 一起工作了,即可以在一个位置返回多个 Token 。 比如:查询:
“+fast + car”如果 Analyzer 在同一位置返回 car 和 automobile ,那么上面的查询将被解析
成:”+fast +(car automobile)”。
(Pierrick Brihaye, Daniel Naber)
6. 允许unbuffered的目录实现。(e.g.,using mmap)。
InputStream 被新类 IndexInput 替换, BufferedIndexInput 和
OutputStream 则被 IndexOutput 和 BufferedIndexOutput。 InputStream 和
OutputStream 已经被废弃了。FSDirectory 现在是一个子类了。(cutting)
7. 增加了原生 Directory 和 TermDocs 的实现,可以工作在 GCJ 下。GCJ的版本需要 3.4.0 以上。可以使用
ant gcj 来运行例子程序。(cutting)
8. 增加了 MmapDirectory 类,它使用 nio to mmap 输入文件。现在MmapDirectory 比
FSDirectory 要慢些。但他对每个查询term 使用更少的内存。(cutting & Paul Elschot)
9. 增加 javadocs-internal 到 build.xml – bug #30360
10. 增加了 RangeFileter ,比 DateFilter 更加通用,实用。
(Chris M Hostetter via Erik)
11. 增加了 NumberTools ,一个用来索引数字字段的工具类。
(adapted from code contributed by Matt Quail; committed by Erik)
12. 增加了 public static IndexReader.main(String[] args) 方法。
IndexReader 现在可以直接在命令行方式下使用,用来 列出或者从现存的索引中抽取单独的文件出来。
(adapted from code contributed by Garrett Rooney; committed by
Bernhard)
13. 增加 IndexWriter.setTermIndexInterval() 方法。
(Doug Cutting)
14. 增加 LucenePackage ,这些静态的 get() 方法返回 java.util.Package。调用者可以用它来获得
Lucene jar 中的版本信息。
(Doug Cutting via Otis)
15. 增加 Hits.iterator() 方法和相应的 HitIterator 和 Hit 对象。
他提供了对 Hits对象标准的 java.util.Iterator 叠代操作。
每个iterator's next() 方法返回一个 Hit 对象。
(Jeremy Rayner via Erik)
16. 增加 ParallelReader,这个一种IndexReader 他合并多个单独的索引到一个单独的虚拟索引上。(Doug
Cutting)
17. 增加对 FieldCache 的 IntParser , FloatParser 接口, 这样任何格式的字段可以被以int 和
float的形式缓存。
(Doug Cutting)
18. 新增类: org.apache.lucene.index.IndexModifier ,它合并了 IndexWriter 和
IndexReader,好处是我们可以增加和删除文档的时候不同担心 synchronisation/locking 的问题了。
(Daniel Naber)
19. Lucene 现在可以被用在一个没有签名的applet中了,Lucene’s 读取系统属性不会抛出
SecurityException 异常。
(Jon Schuster via Daniel Naber, bug #34359)
20. 增加了新类 MatchAllDocsQuery 用来匹配所有文档。
(John Wang via Daniel Naber, bug #34946)
21. 当索引太多的字段时,为了消减索引大小和内存消耗,提供了忽略规范化字段的功能。
见: Field.setOmitNorms()
(Yonik Seeley, LUCENE-448)
22. 增加对 contrib/highlighter 的 NullFragmenter , 这对全文本加亮很有用。
(Erik Hatcher)
23. 增加了正则表达式的查询: RegexQuery 和 SpanRegexQuery。
。
。
(Erik Hatcher)
24. 增加 ConstantScoreQuery 类,它包装了一个 filter produces a score
equal to the query boost for every matching document.
(Yonik Seeley, LUCENE-383)
25. 增加了 ConstantScoreRangeQuery 类,为某个区间的每个文档提供一个不变的 score。这个类比普通的
RangeQuery 类的好处是它并不展开到 BooleanQuery ,因此也不存在区间最大term上限。
(Yonik Seeley, LUCENE-383)
26. 为BooleanQuery增加了最小的匹配短语。见:
BooleanQuery.setMinimumNumberShouldMatch().
(Paul Elschot, Chris Hostetter via Yonik Seeley, LUCENE-395)
27. 增加了 DisjunctionMaxQuery 类,提供了针对某个短语的最大score。
这一点对多字段的搜索非常有用。
(Luc Vanlerberghe via Yonik Seeley, LUCENE-323)
28. 新增类:ISOLatin1AccentFilter ,用 ISO Latin 1 字符集中的unaccented类字符替代
accented 类字符。
(Sven Duzont via Erik Hatcher)
29. 新增类:KeywordAnalyzer。"Tokenizes" 整个流作为一个单独的token。
这个类对于 邮政编码,序列号,和产品名称等比较有用。
(Erik Hatcher)
30. 把 LengthFilter 类从 contrib 放到了 core 代码里。从 stream 中去掉太长和太短的单词。
(David Spencer via Otis and Daniel)
31. 增加了 getPositionIncrementGap 方法到 Analyzer 中。这样用户自定义的 analyzer 可以在相同字
段名的实例之间增加间隙 gaps,用来防止 phrase 和 span 查询超出边界。默认的 gap 是 0 。
(Erik Hatcher, with advice from Yonik)
32. StopFilter 增加了对处理stop words 的忽略大小写处理。
(Grant Ingersoll via Yonik, LUCENE-248)
33. 增加了 TopDocCollector 和 TopFieldDocCollector。用来简化实现hit 集合针对 top-
scoring 和 top-sorting hits的处理。
API 的改变:
1. 几个方法和字段已经被废弃。在API 文档中包含了建议替换的内容。在这些建议中,这些不建议使用的方法和字段将会在Lucene2.0中被删
除。(Daniel Naber)
2. Russian 和 German 的 analyzers 被移到了 contrib/analyzers 。
同样 WordlistLoader 类也被放到了 org.apache.lucene.analysis.WordlistLoader
下 (Daniel Naber)
3. API 包含抛出 IOException 异常的声明,但是实际上不会抛出。 These declarations have
been removed. If
your code tries to catch these exceptions you might need to remove
those catch clauses to avoid compile errors.(Daniel Naber)
4. 为BooleanClause 类的enum 标准参数增加序列化的参数类。 (Christoph)
5. 为 SpanQuery 的子类嵌套其他SpanQuery 增加了 rewrite方法。
Lucene 的源代码管理器也从cvs 换到了svn:
http://svn.apache.org/repos/asf/lucene/java/trunk
参考资料:http://blog.csdn.net/accesine960/archive/2006/02/28/612622.aspx
原文地址:
http://svn.apache.org/viewcvs.cgi/*checkout*/lucene/java/branches/lucene_1_9/CHANGES.txt?rev=379190
Posted by kevinwu 08:33 PM | 全文 | 评论 (0) | 引用 (0) | LUCENE(9)