Lucene创建和查询索引库的HelloWorld(含详细注释)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xzm_rainbow/article/details/18948597

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



猜你喜欢

转载自blog.csdn.net/xzm_rainbow/article/details/18948597