1 简介
1.1 什么是Lucene
Lucene是一个高性能、可伸缩的信息搜索(IR)库。可以为应用程序添加索引和搜索能力。Lucene是用java实现的成熟的、免费的开源项目,是Apache Jakarta大家庭的一员,基于在Apache软件许可 [ASF, License]。同样,Lucene是当前与近几年内非常流行的免费的Java信息搜索(IR)库。
是一个支持全文检索的开源工具包。
1.2 Lucene的发展
Lucene最初是由Doug Cutting 开发的。在 SourceForge 的网站上提供下载。在 2001 年 9 月做为高质量的开源Java产品加入到Apache软件基金会的Jakarta家族中。
1.3 Lucene下载安装
当前版本:lucene-2.4.1
下载地址:http://apache.justdn.org/lucene/java/
下载:lucene-2.4.1.zip jar包
直接使用jar包:
将lucene-2.4.1.zip解压缩,将lucene-core-2.4.1.jar 导入工程。
使用原代码:
将lucene-2.4.1-src.zip解压缩,将%LUCENE%/src/java/org/apache/lucene导入工程。
1.4 一个简单的例子
public class text {
public static List<TestItem> list = new ArrayList<TestItem>();
public text() {
TestItem testItem[] = new TestItem[5];
testItem[0] = new TestItem("01","java编程",11,200.00,new Date(2008,01,06));
testItem[1] = new TestItem("02","java思想",11,200.00,new Date(2008,02,06));
testItem[2] = new TestItem("03","设计模式",11,200.00,new Date(2008,03,06));
testItem[3] = new TestItem("04","编程与模式编程与模式编程与模式编程与模式",11,200.00,new Date(2008,04,06));
testItem[4] = new TestItem("05","设计思想",11,200.00,new Date(2008,05,06));
for (int i = 0; i < testItem.length; i++) {
list.add(testItem[i]);
}
}
public static void createIndex(Directory dir, Analyzer analyzer) {
try {
//参数依次:索引目录、分词工具、是否清空目录、字段值的最大长度(UNLImited即Interger.MaxValue)
IndexWriter writer = new IndexWriter(dir, analyzer, true,
IndexWriter.MaxFieldLength.UNLIMITED);
double start = new Date().getTime();
for (int i = 0; i < list.size(); i++) {
TestItem testItem = (TestItem)list.get(i);
Document doc = new Document();
doc.add(new Field("id",testItem.id , Field.Store.YES,
Field.Index.ANALYZED));
doc.add(new Field("name", testItem.name, Field.Store.YES,
Field.Index.ANALYZED));
doc.add(new Field("number", Integer.toString(testItem.number), Field.Store.YES,
Field.Index.ANALYZED));
doc.add(new Field("worth", Double.toString(testItem.worth), Field.Store.YES,
Field.Index.ANALYZED));
doc.add(new Field("date", testItem.date.toString(), Field.Store.YES,
Field.Index.ANALYZED));
writer.addDocument(doc);
}
writer.close();
double end = new Date().getTime();
System.out.println((end-start)+" milliseconds");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void search(Directory dir, Analyzer analyzer) {
try {
double start = new Date().getTime();
Searcher searcher = new IndexSearcher(dir);
Query query = new QueryParser("name", analyzer).parse("模式");
//此处在2.0基础上有改动,此处必须传入一个返回条数,这里用searcher.maxDoc()表示返回所有条数。
ScoreDoc[] docs = searcher.search(query, searcher.maxDoc()).scoreDocs;
double end = new Date().getTime();
System.out.println("IndexIng "+docs.length +" files took "+(end-start)+" milliseconds");
Document doc;
for (int i = 0; i < docs.length; i++) {
doc = searcher.doc(docs[i].doc);
System.out.println(doc.get("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
text t = new text();
Analyzer analyzer = new StandardAnalyzer();
FSDirectory dir = FSDirectory.getDirectory(new File("D://Lucene_index"));
createIndex(dir, analyzer);
search(dir, analyzer);
dir.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.5 索引和搜索
1.5.1 索引
为了快速搜索大量的文本,必须首先索引那个文本然后把它转化为一个可以让你快速搜索的格式,除去缓慢的顺序地扫描过程。这个转化过程称为索引,它的输出称为一条索引。可以把索引理解为一个可以让你快速随机访问存于其内部的词的数据结构。它隐含的概念类似于 一本 。
1.5.2 搜索
搜索是在一个索引中查找单词来找出它们所出现的文档的过程。一个搜索的质量用精确度和召回率来描述。召回率衡量搜索系统搜索到相关文档的能力,精确度衡量系统过滤不相关文档的能力。支持单个和多个词汇的查询,短语查询,通配符,结果分级和排序。
1.6 索引的关键字
ü IndexWriter
ü Directory
ü Analyzer
ü Document
ü Field
1.6.1 IndexWriter
IndexWriter是在索引过程中的中心组件。这个类创建一个新的索引并且添加文档到一个已有的索引中。你可以把IndexWriter想象成让你可以对索引进行写操作的对象,但是不能让你读取或搜索。
1.6.2 Directory
Directory类代表一个 Lucene 索引的位置。
FSDirectory:lucene索引存储在磁盘上。
RAMDirectory:lucene索引存储在内存中。
1.6.3 Analyzer
在文本索引之前,先通过 Analyzer。Analyzer 在 IndexWriter 的构造函数中指定,对文本内容提取关键词并除去其它的内容。如果要索引的内容不是普通的文本,首先要转化成文本。Analyzer是个抽象类,但是Lucene中有几个它的实现
一个Document代表字段的集合。你可以把它想象为以后可获取的虚拟文档—一块数据,如一个网页一个邮件消息或一个文本文件。一个文档的字段代表这个文档或与这个文档相关的元数据。文档数据的最初来源与Lucene无关。元数据如作者、标题、主题、修改日期等等,分别做为文档的字段索引和存储。
1.6.5 Field
在索引中的每个 Document 含有一个或多个字段,具体化为 Field 类。每个字段相应于数据的一个 片段,将在搜索时查询或从索引中重新获取。
2.1 创建索引步骤
1、 转化为文本
为用Lucene索引数据,必须首先将它转化为纯文本单词流,Lucene能消化的格式。将HTML、PDF、DOC等文本形式转换为txt文件,索引和搜索.txt文件,分析它的内容并使它来生成Field的实例。
2、 分析
一旦准备好了要索引的数据并创建了由Field组成的Lucene Document,就可以调用IndexWriter的addDocument(Document)方法,把数据送入Lucene索引。Lucene首先分析这些数据以使它更适合索引。它将文本数据分割成块或单词,并执行一些可选择的操作。例如,单词可以在索引之前转化为小写,以保证搜索是大小写无关的。典型的,它也有可能排除转入中所有经常出现但无意义的词,例如英语终止词(a, an, the, in, on等等)。类似的,通常分析输入单词以获取它的本质。
3、 写索引
在输入被分析完后,就可以添加到索引中了。Lucene将输入存储在一个反向索引的数据结构中。这个数据结构在允许快速关键字查询的同时有效地利用了磁盘空间。这个结构反向是因为它使用从输入中提取的单词做为查询键值而不是用处理的文档做为中枢入口。换句话说,代替尝试回答这个问题“这个文档中含有哪些单词?”,这个结构为提供快速回答“哪篇文档含有单词X?”做了优化。
2.2 新增document和field
IndexWriter writer = new IndexWriter(dir, analyzer, true,
IndexWriter.MaxFieldLength.UNLIMITED); //dir索引存放位置
TestItem testItem = (TestItem)list.get(i);
Document doc = new Document();
doc.add(new Field("id",testItem.id , Field.Store.YES, Field.Index.ANALYZED));
writer.addDocument(doc);
writer.close();
2.3 索引其他操作
1、 在索引中清除Document
IndexReader reader = IndexRead.open(dir);
Reader.delete(1); // 根据document的序号删除,序号是变化的。
Reader.delete(new Term(“city”,value)); //根据term删除
Reader.close();
2、 恢复清除Document
删除时,在没有关闭indexReader前,要删除的索引做删除标识,并没有对索引进行修改。在关闭IndexReader前执行undeleteAll();撤销对索引的删除标识。
3、 修改Document
先删除后新增的过程。
1. 打开IndexReader。
2. 删除所有你要删除的Document。
3. 关闭IndexReader。
4. 打开IndexWriter。
5. 添加你要添加的所有Document。
6. 关闭IndexWriter。
3 搜索
对创建的索引进行搜索,初始化搜索、设置搜索条件、搜索、结果处理。
Searcher searcher = new IndexSearcher(dir);//dir表示索引文件的存放地址。
Searcher searcher = new IndexSearcher(dir); //dir表示索引文件的存放地址。
Query query = new QueryParser("name", analyzer).parse("模式");
ScoreDoc[] docs = searcher.search(rQuery, searcher.maxDoc()).scoreDocs;
Document doc;
for (int i = 0; i < docs.length; i++) {
doc = searcher.doc(docs[i].doc);
System.out.println(doc.get("name")+ doc.get("date"));
}
3.1 按词条搜索
TermQuery是最简单也是最常用的Query。TermQuery可以理解为“词条搜索”,在搜索引擎中最基本的搜索就是在索引中搜索某一词条,TermQuery就是用来完成这项工作的。
Term term = new Term("name","模式");
Query q = new TermQuery(term);
3.2 或、与搜索
BooleanQuery也是实际开发过程中经常使用的一种Query。它其实是一个组合的Query,在使用时可以把各种Query对象添加进去并标明它们之间的逻辑关系。在本节中所讨论的所有查询类型都可以使用BoolenQuery综合起来。BooleanQuery本身来讲是一般布尔子句的容器,它提供了专门的API方法往其中添加子句,并注明它们之间的关系。
Query query = new QueryParser("name", analyzer).parse("模式");
Query query1 = new QueryParser("name", analyzer).parse("编程");
BooleanQuery query2 = new BooleanQuery();
query2.add(query1,Occur.MUST);
query2.add(query,Occur.SHOULD);
Occur.MUST :必须包含
Occur.NOTMUST :必须不包含
Occur.SHOULD :可以包含
3.3 在一定范围内搜索
有时用户需要一种在一个范围内查找某个文档,比如查找某一时间段内的所有文档,此时,Lucene提供了RangeQuery的类来满足这种需求。
RangeQuery表示在某范围内的搜索条件,实现从一个开始词条到一个结束词条的搜索功能,在查询时“开始词条”和“结束词条”可以被包含在内也可以不被包含在内。
Term term1 = new Term("id","01");
Term term2 = new Term("id","04");
RangeQuery rQuery = new RangeQuery(term1,term2,false);//不包含边界
RangeQuery rQuery = new RangeQuery(term1,term2,true); //包含边界
3.4 使用前缀搜索
PrefixQuery就是使用前缀来进行查询的,通常情况下,首先定义一个词条Term。该词条包含要查找的字段名以及关键字的前缀,然后通过该词条构造一个PrefixQuery对象,就可以进行前缀查找了。
Term term = new Term("name","模式");
PrefixQuery pQuery = new PrefixQuery(term);
3.5 多关键字搜索
除了普通的TermQuery外,Lucene还提供了一种Phrase查询的功能,用户在搜索引擎中进行搜索时,常常查找的并非是一个简单的词,很可能是几个不同的关键字、这些关键字之间要么是紧密相联成为一个精确的短语,要么是可能自爱这几个关键字之间还插有其他无关的关键字。此时,用户希望将它们找出来。从评分的角度看,这些关键字之间拥有与查找内容无关短语所在的文档的分值一般会比较底一些。
PhraseQuery正是Lucene所提供的满足上述需求的一种Query对象、它的addfangf可以让用户往其内添加关键字,在添加完毕后,用户还可以通过setSlop()方法来设定一个称之为“坡度”的变量来确定关键字之间十分允许、允许多少个无关词汇的存在。
Term term1 = new Term("id","01");
Term term2 = new Term("id","04");
PhraseQuery phQuery = new PhraseQuery();
phQuery.add(term1);
phQuery.add(term2);
3.6 使用通配符搜索
WildcardQuery
Term term1 = new Term("name","设计*");
WildcardQuery wildcardQuery = new WildcardQuery(term1);
3.7 相近词语搜索
Term term1 = new Term("name","设计");
FuzzyQuery fuzzyQuery = new FuzzyQuery(term1);