Lucene入门

Lucene2.0快速入门
一、Lucene介绍
Apache Lucene是一个基于Java全文搜索引擎,利用它可以轻易地为Java软件加入全文搜寻功能。Lucene的最主要工作是替文件的每一个字作索引,索引让搜寻的效率比传统的逐字比较大大提高,Lucen提供一组解读,过滤,分析文件,编排和使用索引的API,它的强大之处除了高效和简单外,是最重要的是使使用者可以随时应自已需要自订其功能。
Lucene的作者:Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。
1.1 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
1.2 Lucene优点:
大部分的搜索(数据库)引擎都是用B树结构来维护索引,索引的更新会导致大量的IO操作,Lucene在实现中,对此稍微有所改进:不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中(针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效率。
1.3 Lucene 原理
lucene的检索算法属于索引检索,即用空间来换取时间,对需要检索的文件、字符流进行全文索引,在检索的时候对索引进行快速的检索,得到检索位置,这个位置记录检索词出现的文件路径或者某个关键词。
在使用数据库的项目中,不使用数据库进行检索的原因主要是:数据库在非精确查询的时候使用查询语言“like %keyword%”,对数据库进行查询是对所有记录遍历,并对字段进行“%keyword%”匹配,在数据库的数据庞大以及某个字段存储的数据量庞大的时候,这种遍历是致命的,它需要对所有的记录进行匹配查询。因此,lucene主要适用于文档集的全文检索,以及海量数据库的模糊检索,特别是对数据库的xml或者大数据的字符类型。
二、索引的建立:
2.1 一个简单索引的建立:
package cn.netjava.lucene;
import java.io.BufferedReader;
import cn.netjava.dbcc.DBAcc;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
//JE分词组件
import jeasy.analysis.MMAnalyzer;

import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
/**
* 创建索引
* @author saai
*
*/
public class WriteIndex {
//索引目录:
String INDEX_STORE_PATH="d:\\luceneIndex";
/**
* 创建索引
*/
public void createIndex(String inputDir){
try{
//初始化IndexWriter
IndexWriter writer=new IndexWriter(INDEX_STORE_PATH,new MMAnalyzer(),true);
//非压缩存储(True则为复合索引格式)
writer.setUseCompoundFile(false);
//索引合并因子
writer.setMergeFactor(100);
writer.setMaxMergeDocs(1000);
//提取索引数据
java.util.List<cn.netjava.dbcc.WorkTask> worktask= DBAcc.getDBAcc().AllWorkTask();
java.util.Iterator<cn.netjava.dbcc.WorkTask> it=worktask.iterator();
while(it.hasNext()){
//增加文档
addDocument(it.next(),writer,inputDir);

}
//关闭索引,如不关闭索引操作将不会写入磁盘
writer.close();
System.out.println("索引建立完毕。。。。");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 添加索引字段
* @param wtask 要索引的记录
* @param writer IndexWriter
* @param inputDir 目录
*/
private void addDocument(cn.netjava.dbcc.WorkTask wtask,IndexWriter writer,String inputDir){
String path=inputDir+wtask.getContent();
System.out.println(path);
File files=new File(path);
System.out.println(files.getAbsolutePath());
String fileName=files.getName();
Document doc=new Document();
Field f1=new Field("content",loadFileToString(files),Field.Store.NO,Field.Index.TOKENIZED);
doc.add(f1);
System.out.println("已添加一个文档");
Field f2= new Field("name",wtask.getTitle(),Field.Store.YES,Field.Index.TOKENIZED);
doc.add(f2);
String id=java.lang.Integer.toString(wtask.getId());
Field f3= new Field("id",id,Field.Store.YES,Field.Index.TOKENIZED);
doc.add(f3);
try{
writer.addDocument(doc);
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 读出文件
* @param file
* @return
*/
private String loadFileToString(File file){
try{
java.io.BufferedReader br=new java.io.BufferedReader(new java.io.FileReader(file));
StringBuffer sb=new StringBuffer();
String line=br.readLine();
while(line!=null){
sb.append(line);
line=br.readLine();
}
br.close();
System.out.println("文件内容为:"+sb.toString());
return sb.toString();
}catch(Exception e){
e.printStackTrace();
return null;
}
}
}


索引结果:

2.2  Lucene索引及文件的相关含义:
Term:项。为分词后最小单位,具体和使用的分析器有关,Lucene内置为单字。
Field:域。相当于数据库表的字段。
Document:文档。域信息的集合。
Segment:段。一个完整定1索引。
文件名称:segments_N,N为36进制数。
说明:在一个索引目录中,至少存在一个Segments文件,但可索引活动的只会有一个:N值最大的。其他的Segments文件可能为临时文件,一般情况是索引修改进程没有完成。
文件名称 后缀名称 说明
segments_N 段,表示一个完整独立的索引;N为36进制数
segments .gen segments辅助文件,主要用来生成计数
SegmentName .fnm 域集合信息文件
SegmentName .fdt 域值信息文件
SegmentName .tis 项信息文件
SegmentName .tii 项信息索引文件
SegmentName .frq 项频数文件
SegmentName .prx 项位置信息文件
SegmentName nrm 标准化因子文件
SegmentName .tvx 文档信息索引文件
SegmentName .tvd 文档信息文件
SegmentName ..tvf 每个域项向量信息文件
备注 SegmentName为_N,N为36进制数

2.3 Lucene原始存储数据类型(Primitive Types)
类型名称 说明
Byte 8 byte
UInt32 32 byte四字节无符号整型,高位优先
UInt64 64byte八字节无符号整型,高位优先
VInt 可变长度整型。0-127单字节,128-16383两字节,类推
Chars UNICODE字符串
String 写入格式为:VInt,Chars,字符串长度加字符串
其实从上面的例子就可以看出建立索引就用到Document,IndexWriter,Field。
最简单的步骤就是:
首先分别new 一个Document,IndexWriter,Field
然后用Doument.add()方法加入Field,
其次用IndexWrtier.addDocument()方法加入Document。
最后调用一下IndexWriter.close()方法关闭输入索引,这一步非常的重要只有调用这个方法索引才会被写入索引的目录里,而这是被很多初学的人所忽略的。
Document没有什么好介绍的,把它的作用看成数据库中的一行记录就行。
Field是一个比较重要的也是比较复杂的:
看一下它的构造函数有5个:

Field(String name, byte[] value, Field.Store store)
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
Field(String name, String value, Field.Store store, Field.Index index)
Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector)

在Field中有三个内部类:Field.Index,Field.Store,Field.termVector,而构造函数也用到了它们。
参数说明:
Field.Store:
Field.Store.NO:表示该Field不需要存储。
Field.Store.Yes:表示该Field需要存储。
Field.Store.COMPRESS:表示使用压缩方式来存储。
Field.Index:
Field.Index.NO:表示该Field不需要索引。
Field.Index.TOKENIZED:表示该Field先被分词再索引。
Field.Index.UN_TOKENIZED:表示不对该Field进行分词,但要对其索引。
Field.Index.NO_NORMS:表示该Field进行索引,但是要对它用Analyzer,同时禁止它参加评分,主要是为了减少内在的消耗。


2.4 IndexWriter相关说明:
IndexWriter(File path, Analyzer a, boolean create)
IndexWriter(String path, Analyzer a, boolean create)
可见构造它需要一个索引文件目录,一个分析器(一般用标准的这个),最后一个参数是标识是否清空索引目录
a.索引格式:其实索引目录有两种格式,一种是除配置文件外,每一个Document独立成为一个文件(这种搜索起来会影响速度)。另一种是全部的Document成一个文件,这样属于复合模式就快了。
b.索引文件可放的位置:索引可以存放在两个地方1.硬盘,2.内存;
放在硬盘上可以用FSDirectory(),放在内存的用RAMDirectory()不过一关机就没了
FSDirectory.getDirectory(File file, boolean create)
FSDirectory.getDirectory(String path, boolean create)两个工厂方法返回目录
New RAMDirectory()就直接可以再和IndexWriter(Directory d, Analyzer a, boolean create)一配合就行了如:
IndexWrtier indexWriter = new IndexWriter(FSDirectory.getDirectory(“c:\\index”,true),new StandardAnlyazer(),true);
IndexWrtier indexWriter = new IndexWriter(new RAMDirectory(),new StandardAnlyazer(),true);
c.索引的合并:这个可用IndexWriter.addIndexes(Directory[] dirs)将目录加进去,来看个例子:
		
public void UniteIndex() throws IOException
{
IndexWriter writerDisk = new IndexWriter(FSDirectory.getDirectory("c:\\indexDisk", true),new StandardAnalyzer(),true);
Document docDisk = new Document();
docDisk.add(new Field("name","工作日志",Field.Store.YES,Field.Index.TOKENIZED));
writerDisk.addDocument(docDisk);
RAMDirectory ramDir = new RAMDirectory();
IndexWriter writerRam = new IndexWriter(ramDir,new StandardAnalyzer(),true);
Document docRam = new Document();
docRam.add(new Field("name","文艺生活",Field.Store.YES,Field.Index.TOKENIZED));
writerRam.addDocument(docRam);
writerRam.close();//这个方法非常重要,是必须调用的
writerDisk.addIndexes(new Directory[]{ramDir});
writerDisk.close();
}
public void UniteSearch() throws ParseException, IOException
{
QueryParser queryParser = new QueryParser("name",new StandardAnalyzer());
Query query = queryParser.parse("工作");
IndexSearcher indexSearcher =new IndexSearcher("c:\\indexDisk");
Hits hits = indexSearcher.search(query);
System.out.println("找到了"+hits.length()+"结果");
for(int i=0;i<hits.length;i++)
{
Document doc = hits.doc(i);
System.out.println(doc.get("name"));
}
}

这个例子是将内存中的索引合并到硬盘上来.
注意:合并的时候一定要将被合并的那一方的IndexWriter的close()方法调用。
d.对索引的其它操作:IndexReader类是用来操作索引的,它有对Document,Field的删除等操作。
三、检索:
检索前,需要对检索字符串进行分析,这是由QueryParser来完成的。为了保证查询的正确性,最好用创建索引文件时同样的分析器。QueryParser解析字符串时,可以指定查询域,实际可以在字符串中指定一个或多个域。例如:“Info:电视台 AND ID:3329”,“Info:电视台”,“电视台”,假如不指定默认域,就会在默认域查询。
QueryParser调用静态方法Parse后会返回Query的实例,原子查询。例如:“Info:电视台 AND ID:3329”会返回BooleanQuery,“Info:电视台”或“电视台”会返回PhraseQuery,“台”会返回TermQuery。
3.1 Lucene内建Query对象
TermQuery:词条查询。通过对某个词条的指定,实现检索索引中存在该词条的所有文档。
BooleanQuery:布尔查询。Lucene中包含逻辑关系:“与”,“或”,“非”的复杂查询,最终都会表示成BooleanQuery。布尔查询就是一个由多个子句和子句之间组成的布尔逻辑所组成的查询。
RangeQuery:范围查询。这种范围可以是日期,时间,数字,大小等等。
PrefixQuery:前缀查询。
PhraseQuery:短语查询。默认为完全匹配,但可以指定坡度(Slop,默认为0)改变范围。比如Slop=1,检索短语为“电台”,那么在“电台”中间有一个字的也可以被查找出来,比如“电视台”。
MultiPhraseQuery:多短语查询。
FuzzyQuery:模糊查询。模糊查询使用的匹配算法是levensh-itein算法。此算法在比较两个字符串时,将动作分为3种:加一个字母(Insert),删一个字母(Delete),改变一个字母(Substitute)。
WildcardQuery:通配符查询。“*”号表示0到多个字符,“?”表示单个字符。
SpanQuery:跨度查询。此类为抽象类。
SpanTermQuery:检索效果完全同TermQuery,但内部会记录一些位置信息,供SpanQuery的其它API使用,是其它属于SpanQuery的Query的基础。
SpanFirstQuery:查找方式为从Field的内容起始位置开始,在一个固定的宽度内查找所指定的词条。
SpanNearQuery:功能类似PharaseQuery。SpanNearQuery查找所匹配的不一定是短语,还有可能是另一个SpanQuery的查询结果作为整体考虑,进行嵌套查询。
SpanOrQuery:把所有SpanQuery查询结果综合起来,作为检索结果。
SpanNotQuery:从第一个SpanQuery查询结果中,去掉第二个SpanQuery查询结果,作为检索结果。
BooleanClause用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。有以下6种组合:
A.MUST和MUST:取得连个查询子句的交集。
B.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
C.MUST_NOT和MUST_NOT:无意义,检索无结果。
D.SHOULD与MUST、SHOULD与MUST_NOT:SHOULD与MUST连用时,无意义,结果为MUST子句的检索结果。与MUST_NOT连用时,功能同MUST。
E.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。
3.2 多域搜索:

/**
* 检索索引
* @param query 索引查找字符串
* @return 返回结果:id的数组
*/
public int[] indexSearch(String query){
try{
org.apache.lucene.search.IndexSearcher seacrcher=new org.apache.lucene.search.IndexSearcher(INDEX_STORE_PATH);
org.apache.lucene.analysis.Analyzer analyzer=new jeasy.analysis.MMAnalyzer();
BooleanClause.Occur [] clauses={BooleanClause.Occur.SHOULD,BooleanClause.Occur.SHOULD};
Query q = org.apache.lucene.queryParser.MultiFieldQueryParser.parse(query,new String[] {"name","content"},clauses,analyzer);
// org.apache.lucene.queryParser.QueryParser mq=new org.apache.lucene.queryParser.QueryParser("name",analyzer);
// mq.setDefaultOperator(org.apache.lucene.queryParser.QueryParser.AND_OPERATOR);
// Query q=mq.parse(query);
System.out.println("查寻表达式:"+q.toString());
Hits hits=seacrcher.search(q);
int [] id=new int [hits.length()];
for(int i=0;i<hits.length();i++){
System.out.println(hits.doc(i));
System.out.println(hits.score(i));
System.out.println(hits.id(i));
java.lang.Integer ID=new java.lang.Integer(hits.doc(i).get("id"));
id[i]=ID.intValue();
System.out.println("******************");
}
return id;
}catch(Exception e){
e.printStackTrace();
return null;
}
}

3.3 多索引搜索:
根据不同的索引目录创建多个Searcher:


IndexSearcher Searcher1=new IndexSearcher(INDEX_STORE_PATH1);
IndexSearcher Searcher2=new IndexSearcher(INDEX_STORE_PATH2);
IndexSearcher [] searchers={ Searcher1, Searcher2};
MultiSearcher searcher=new MultiSearcher(searchers);
Hits hits=searcher.search(q)

3.4 多线程搜索:
ParallelMultiSearcher searcher =new ParallelMultiSearcher(searchers);
Hits hits=searcher.search(q);

3.5 对搜索结果排序:
缺省的,Lucene返回的结果是按照每个文档的相关度(Lucene分数)以降序排列。如果两个文档的相关度相同,则按照文档的索引顺序(也就是每个文档文档号升序)排列。索引顺序就是在对文档建立索引的顺序。
搜索结果可以按照一个或者多个域进行排序。指定的域必须被索引并且不能被分词,可以存储也可以不存储。域的值类型必须是可排序的整型、浮点性或者字符型。系统会根据域的第一个值自动判断域类型。整型和浮点型排序效率高,系统消耗少。
排序可以是升序,也可以是降序。默认是降序。
IndexSearcher.sort(Query,Sort)---按照指定排序类型排序。
Sort类具体处理排序,SortFiled代表要排序的域。

四、排序、过滤和分布;

[size=medium][/size][align=center][/align]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值