本案例使用的是Lucene-3.6.2版本,Lucene官方网站:http://lucene.apache.org/。
案例说明:
本例模拟了贴吧中检索帖子的功能,通过创建Article类来模拟帖子对象。用户输入检索信息,Lucene就可以根据检索信息来获取与之相关的Article对象,并返回给用户。
一、建立工程
首先在我们的MyEclipse中创建一个Java工程即可,在里面创建一个lib文件夹用于存放我们开发时用的jar包。
二、导入jar包
本案例需要Lucene的4个基本jar包。如下:
lucene-core-3.6.2.jar
contrib\analyzers\common\lucene-analyzers-3.6.2.jar(分词器)
contrib\highlighter\lucene-highlighter-3.6.2.jar(高亮)
contrib\memory\lucene-memory-3.6.2.jar(高亮)
然后将lib中的四个jar包Build Path。三、创建HelloWorld类
在src下自创建一个包,并在包中创建HelloWorld.java文件。
这个文件中不需要main函数,我们将通过jUnit来测试程序。
所以在我们的方法中需要添加@Test注解。
public class HelloWorld {
//创建索引库
@Test
public void createIndex() {
}
//搜索索引库
@Test
public void seacherIndex() {
}
}
四、创建检索类PO
创建我们需要检索的类Article(模拟帖子对象),里面有三个字段:id,title,content,
分别表示:编号、标题、内容。
public class Article {
private Integer id; //id
private String title; //标题
private String content; //内容
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Article [id=" + id + ", title=" + title + ", content="
+ content + "]";
}
}
五、编写HelloWorld,实现创建和查询索引库
这里需要记住创建和查询的两个核心API:
向索引库中增删改的时候,使用IndexWriter对象。
其主要方法为:addDocument()、updateDocument()、deleteDocument()。
从索引库中搜索的时候,使用IndexSearcher对象。
其主要方法为:search()。里面的一些API的体系结构图会在最后的附件中列出,可对照查看。
程序中的步骤是按照编号走的,由于需要准备各种参数,所以显得有些凌乱,只要按照步骤参考即可。
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import org.junit.Test;
import lucene.a_domain.Article;
public class HelloWorld {
//创建索引库
/**
* 执行这个方法会将这个方法中创建的Article对象中的属性:id,title,content转换成Document字段,
* 索引库中只能存放Document类型对象,不能存放我们创建的对象,将Article转成Document后,
* 通过IndexWriter的addDocument方法将转换后的Document对象添加到索引库中,
* 执行这个方法后,将会在指定的索引库目录中创建索引文件,这是一堆二进制文件。
* 因为用到了IO,所以最后需要将IndexWriter关闭。
* @throws Exception
*/
@Test
public void createIndex() throws Exception {
/*
* 2.创建索引库目录,这是IndexWriter构造器的第一个参数。
* 这个Directory是个抽象类,按住ctrl+t可以查看这个类的继承体系,
* 会发现他有一个子抽象类,叫做FSDirectory,
* 我们需要使用这个类的实例当作我们的Directory,
* 但他是抽象的,无法new,
* FSDirectory里面有一个open方法, open方法接收一个File,
* 这个open方法就可以获取FSDirectory实例,
* 而File是我们指定的存放目录的路径,在当前工程创建一个indexDir文件夹作为路径即可,
* 至此,索引库目录配置完成,将这个引用添加到IndexWriter的第一个参数的位置。
*/
Directory directory = FSDirectory.open(new File("./indexDir/"));
/*
* 4.创建IndexWriterConfig构造器的第二个参数,analyzer分词器。
* 这个Analyzer也是一个抽象类,ctrl+t查看其继承体系可以发现它有很多子类。
* 这里先使用它的标准分词器,StandardAnalyzer。
* new这个类需要一个版本号参数,同样通过Version的静态常量给出。
* 分词器创建完成。添加到IndexWriterConfig的第二个参数上。
*/
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);
/*
* 3.创建IndexWriter构造器的第二个参数,配置。
* 直接new,发现报错,它的构造器需要两个参数,
* 一个是Version,一个是Analyzer,
* Version通过Version.LUCENE_36即可创建,这时Version类中的一个静态常量。
* Analyzer是分词器,这是Lucene自带的分词器,这个分词器不支持中文
*/
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36,analyzer);
//1.这里需要两个参数,第一个是索引库目录,第二个是配置,在上面提供这两个参数即可
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
/*
* 6.创建要存入索引库的对象Article,
* 创建完成后,我们需要将Article中的属性转换成Document的字段
*/
Article article = new Article();
article.setId(1);
article.setTitle("Lucene是什么");
article.setContent("Lucene,速度快捷方式离开减肥啦实际得分卡设计大方");
/*
* 7.将Article转换成Document对象,
* 需要将Article中的属性转换成Document的字段
* 直接new一个Document,再通过Document的add方法添加字段,
* add方法需要一个Fieldable类型的参数,Fieldable是一个接口,
*/
Document doc = new Document();
/*
* 8.创建Fieldable的子类,ctrl+t查看继承体系,发现有个Field子类
* 创建Field需要四个参数,String name,String value,Store store,Index index
* 第一个参数表示索引库的字段名称;
* 第二个参数表示存放到该字段上的值;
* 第三个参数表示是否存储
* 第四个参数表示分词
* 在add方法里直接new,Article有三个属性,需要添加三个字段到Document中,所以add三次
*/
//Fieldable field = new Field("id",article.getId().toString(),Store.YES,Index.ANALYZED);
doc.add(new Field("id",article.getId().toString(),Store.YES,Index.ANALYZED));
doc.add(new Field("title", article.getTitle(), Store.YES, Index.ANALYZED));
doc.add(new Field("content", article.getContent(), Store.YES, Index.ANALYZED));
/*
* 5.创建索引库,使用IndexWriter的addDocument方法。
* 这个方法需要一个Document参数。这个Document就是放入索引库的数据,
* 但是我们需要放入的是Article对象,这就需要将我们的Article类型转换成Document类型。
*/
indexWriter.addDocument(doc);
/*
* 9.关闭流
*/
indexWriter.close();
}
//搜索索引库
/**
* 搜索索引库时,开始先创建了一个List集合来存储最终查询的结果。
* 这个程序中,将查询条件写死了,真正开发的时候是不可能写死的,所以开发时这个查询条件是需要获取的。
* 根据查询条件就可以从索引库的到最终的结果集,但是索引库中存放的是Document对象,所以获取的结果集也是Document对象集合。
* 还需要将Document对象转换成我们需要的Article对象,根据document对象的get方法获取即可,get(name)参数为字段名称。
* 最后我们调用了showResults(List list)方法将获取的结果集遍历取出,显示在控制台上。
* @throws Exception
*/
@Test
public void seacherIndex() throws Exception {
//创建一个集合,存放查询出来的数据。
List<Article> list = new ArrayList<Article>();
/*
* 2.创建索引库目录,指定索引库目录路径,提供给IndexSearcher
*/
Directory directory = FSDirectory.open(new File("./indexDir/"));
/*
* 1.创建IndexSearcher对象,这个对象的构造器需要接收一个IndexReader对象
* 这个IndexReader是一个抽象类,它里面有一个抽象方法:open。
* open方法的参数是Directory,即索引库目录,需要指定它从那个索引库目录中读取数据。
* 在上面创建这个目录,跳到第2步。
*/
IndexSearcher indexSearcher = new IndexSearcher(IndexReader.open(directory));
/*
* 4.创建查询条件
* QueryParser只能指定在一个字段上进行检索,例如如果指定了id字段就只能查询id字段。是单字段检索。
* QueryParser有三个参数,
* 参数1:版本
* 参数2:表示对哪个字段进行检索,这里传入一个字段名称
* 参数3:分词器
* 这里查询条件是"Lucene"表示在某个字段中查询"Lucene"。
*/
String queryString = "Lucene";
//6.准备分词器:
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);
//5.创建解析器
QueryParser queryParser = new QueryParser(Version.LUCENE_36,"title",analyzer);
//7.将查询条件放入解析器中。这里返回的是query对象,这个对象作为IndexSearcher的search方法的第一个参数
Query query = queryParser.parse(queryString);
/*
* 3.通过IndexSearcher的search方法创建查询
* 参数1:表示查询条件
* 参数2:表示返回的前多少条记录
* search返回值为TopDocs类型,这是查询完的结果集。
*/
TopDocs topDocs = indexSearcher.search(query, 100);
System.out.println("总记录数:" + topDocs.totalHits);//这个字段获取查询到的总记录数。
//8.获取结果集数组
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//遍历scoreDocs,
if(scoreDocs!=null && scoreDocs.length>0) {
for (int i = 0; i < scoreDocs.length; i++) {
ScoreDoc scoreDoc = scoreDocs[i];
System.out.println("获取这个记录的得分:" + scoreDoc.score);
//获取检索出的记录在索引库中的唯一编号,根据这个编号就可以获取需要的数据
int doc = scoreDoc.doc;
//IndexSearcher的doc方法可以通过刚获取的唯一编号从索引库中获取我们需要的数据
Document document = indexSearcher.doc(doc);
//获取了Document对象,还需要将Document对象转成Article对象。
Article article = new Article();
/*
* 通过document对象的get方法,根据字段名称获取值,
* 这里的名称是通过上面的
* doc.add(new Field("id",article.getId().toString(),Store.YES,Index.ANALYZED));
* 这个方法设置的字段名称
*/
article.setId(Integer.parseInt(document.get("id")));
article.setTitle(document.get("title"));
article.setContent(document.get("content"));
//添加到存放结果的集合中。
list.add(article);
}
}
//9.关闭流
indexSearcher.close();
//10.遍历输出最终获取的结果集合
if(list != null && list.size() > 0) {
showResults(list);
}
}
private void showResults(List<Article> list) {
for(Article article : list) {
System.out.println("文章编号:" + article.getId());
System.out.println("文章标题:" + article.getTitle());
System.out.println("文章内容:" + article.getContent());
System.out.println("------------------------------------------------");
}
}
}
先执行createIndex方法,这样我们先建立的索引库就有数据了,然后再执行seacherIndex方法,就可以将索引库获取的数据显示在控制台上。
六、查看索引库中生成的文件
我们将索引库定义在了工程的根目录下:
进入这个目录,可以看到生成的文件,这都是一些二进制文件。
七、附件
这里提供了一些类的继承体系信息。
Directory类:
Analyzer类: