本案例使用的是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类: