用Lucene建立索引及查询示例

 


 
 
 
 

 

 

用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 等文件需要经过转化才能对其进行索引。

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 数字

 

  1. 如果数字只是简单的数据, 比如中国有56个民族. 那么可以简单的把它当字符处理
  2. 如果数字还包含数值的意义,比如价格, 我们会有范围搜索的需要(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提供了一些参数可供设置,列表如下

 属性默认值说明
mergeFactororg.apache.lucene.mergeFactor10控制index的大小和频率,两个作用
maxMergeDocsorg.apache.lucene.maxMergeDocsInteger.MAX_VALUE限制一个段中的document数目
minMergeDocsorg.apache.lucene.minMergeDocs10缓存在内存中的document数目,超过他以后会写入到磁盘
maxFieldLength 1000一个Field中最大Term数目,超过部分忽略,不会index到field中,所以自然也就搜索不到

这些参数的的详细说明比较复杂:mergeFactor有双重作用
  1. 设置每mergeFactor个document写入一个段,比如每10个document写入一个段
  2. 设置每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设置给它

用java实现全文检索[zhanglihai.com]

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值