JAVA_WEB项目之Lucene检索框架入门案例

web开发中一般会碰到查询数据到前台显示,对于较低要求的项目可以用like检索每次检索数据库,但是非常消耗数据库资源,因此我们可以使用Lucene检索框架来解决查询效率的问题。下面直接用代码贴出个人学习的案例:

开始前先下载lucene3.0的5个jar包:

1、IKAnalyzer3.2.8.jar分词器包

2、lucene-analyzers-3.0.0.jar分词器包

3、lucene-core-3.0.0.jar核心包

4、lucene-highlighter-3.0.0.jar高亮包

5、lucene-memory-3.0.0.jar


首先定义一个实体类:Goods

package com.shop.demo;

public class Goods {

	private Integer id;
	private String name;
	private Double price;
	private String pic;
	private String remark;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getPrice() {
		return price;
	}
	public void setPrice(Double price) {
		this.price = price;
	}
	public String getPic() {
		return pic;
	}
	public void setPic(String pic) {
		this.pic = pic;
	}
	public String getRemark() {
		return remark;
	}
	public void setRemark(String remark) {
		this.remark = remark;
	}
	
	
}

接下来定义一个HelloWordLucene:

package com.shop.demo;

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.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
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.util.Version;
/**
 *  此案例实现Lucene向索引库中添加索引和查询索引的功能
 * @author Administrator
 *
 */
public class HelloWordLucene {

	/**
	 * 把good商品对象添加到索引库中
	 * @param goods
	 */
	public void addDocument(Goods goods){
		//创建indexWriter
		IndexWriter indexwriter=null;
		//创建索引库
		Directory dir=null;
		//创建分词器
		Analyzer ana=null;
		try {
			//根据指定的路径创建索引库,如果路径不存在就会创建
			dir=FSDirectory.open(new File("c:/demo"));
			//不同的分词器的版本不同,分词的算法不同,StandardAnalyzer只适用于英文
			ana=new StandardAnalyzer(Version.LUCENE_30);
			//限制Field的数量为10000
			indexwriter=new IndexWriter(dir, ana, MaxFieldLength.LIMITED);
			//把goods对象转为document
			Document doc=new Document();
			/**
			 * Store配置field字段是否存储到索引库
			 * YES:字段存储到索引库中,以后查询的时候可以查询出来
			 * No:不存储到索引库中
			 *  Index: Lucene为提高查询效率,会像字典一样创建索引. 配置此字段是否要建立索引(建立索引的Field就是Term),
			 *  如果建立索引以后就可以通过此字段查询记录
			 *   NOT_ANALYZED: 创建索引,但是Field的不分词(不分开) 整体作为一个索引
			 *   ANALYZED: 不但要建立索引此Field会被分词(可能一个Field分为多个Term的情况)
			 *   NO: 不建立索引,以后不能通过此字段查询数据 
			 *  Store yes Index: ANALYZED: 此Field可以存储,而且Field 关键字支持分词
			 *  Store yes Index: NOT_ANALYZED 此Field可以存储,但是Field不支持分词,作为一个完成Term   例如: 数字 id  price  和URL 专业词汇
			 *  Store yes Index: NO:  可以查询出此字段, 但是此字段不作为查询关键字
			 *  Store no  Index: ANALYZED:  此Field不存储,但是此Field可以做为关键字搜索  
			 *  Store no  Index: NOT_ANALYZED: 此Field不存储,但是此Field可以做为整体(不拆分)关键字搜索
			 *  Store no  Index: NO:  既不建索引也不存储 没有任何意义,如果这样配置则会抛出异常
			 */
			doc.add(new Field("id", goods.getId().toString(), Store.YES, Index.NOT_ANALYZED));
			doc.add(new Field("name", goods.getName(), Store.YES, Index.ANALYZED));
			doc.add(new Field("price", goods.getPrice().toString(), Store.YES, Index.NOT_ANALYZED));
			doc.add(new Field("remark", goods.getRemark(), Store.NO, Index.ANALYZED));
			indexwriter.addDocument(doc);
			// 如果没有提交,在没有异常的情况close()之前会自动提交
			indexwriter.commit();
		} catch (Exception e) {
			try {
				indexwriter.rollback();
				throw new RuntimeException(e);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				throw new RuntimeException(e);
			}
		}finally{
			try {
				indexwriter.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				throw new RuntimeException(e);
			}
		}
	}
	/**
	 * 根据指定的条件查询,
	 * @param name 指定的关键字
	 * @return 封装了goods对象的list集合
	 */
	public List<Goods> queryGoods(String name){
		List<Goods> goodsList=new ArrayList<Goods>();
		//创建查询对象
		IndexSearcher searcher=null;
		//创建索引库
		Directory dir=null;
		//创建分词器
		Analyzer analyzer=null;
		try {
			dir=FSDirectory.open(new File("c:/demo"));
			searcher=new IndexSearcher(dir);
			// 创建分词器,给查询的关键字先做分词操作,然后在到索引库中匹配Term
			analyzer=new StandardAnalyzer(Version.LUCENE_30);
			//创建查询解析对象,"name"指定从索引库中的哪个field属性里面查找,也就是name到那个Term(key value)中去查询
			QueryParser queryParser=new QueryParser(Version.LUCENE_30, "name", analyzer);
			//  指定查询的关键字到索引库查询
			Query query=queryParser.parse(name);
			/**
			 * 根据给定的关键字查询,与索引库Term去匹配,5代表: 期望返回的结果数
			 *  第一次查询: indexSearcher.search 只能获取文档的索引号和匹配的数量
			 *  返回的结果是TopDoc类型
			 *  totalHits: 命中数, 数组的长度,后面用来做分页
			 *  ScoreDoc[]: 存储匹配的文档编号的数组
			 *  Score: 文档的积分,按照命中率自动算出来
			 *  Doc:当前文档的编号
			 */
			TopDocs topDocs= searcher.search(query, 5);
			// 此变量/每页显示的记录数就是总页数
			System.out.println("真正命中的结果数:" + topDocs.totalHits);
			// 返回的是符合条件的文档编号,并不是文档本事
			ScoreDoc scoreDocs[]= topDocs.scoreDocs;
			for(int i=0;i<scoreDocs.length;i++){
				ScoreDoc scoreDoc= scoreDocs[i];
				System.out.println("真正的命中率:"+scoreDoc.score);
				System.out.println("存储的是文档编号:"+scoreDoc.doc);
				Document doc= searcher.doc(scoreDoc.doc);
				System.out.println(doc.get("id"));
				System.out.println(doc.get("name"));
				System.out.println(doc.get("price"));
				System.out.println(doc.get("remark"));
				System.out.println("---------");
				Goods goods=new Goods();
				goods.setId(Integer.parseInt(doc.get("id")));
				goods.setName(doc.get("name"));
				goods.setPrice(Double.parseDouble(doc.get("price")));
				goods.setRemark(doc.get("remark"));
				goodsList.add(goods);
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new RuntimeException(e);
		}finally{
			try {
				searcher.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException(e);
			}
		}
		return goodsList;
	}
}

接下写测试用例:

package com.shop.demo;

import java.util.List;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class HelloWordLuceneTest {

	private static HelloWordLucene hellowod;
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		hellowod=new HelloWordLucene();
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
		hellowod=null;
	}

	@Test
	public void testAddDocument() {
		Goods goods=new Goods();
		goods.setId(13);
		goods.setName("IBM Computer1112 IBM");
		goods.setPrice(2333.9);
		goods.setRemark("IBM Computer is good");
		hellowod.addDocument(goods);
	}


	@Test
	public void testquery() {
		List<Goods> list= hellowod.queryGoods("ibm");
		for(Goods good:list){
			System.out.println("商品编号:"+good.getId()+",商品名称:"+good.getName()+
					",商品价格:"+good.getPrice()+",商品的详细信息:"+good.getRemark()
					);
		}
	}
}

结果显示:

真正命中的结果数:5
真正的命中率:0.578186
存储的是文档编号:4
13
IBM Computer1112 IBM
2333.9
null
---------
真正的命中率:0.51104903
存储的是文档编号:0
11
IBM Computer
2333.9
null
---------
真正的命中率:0.51104903
存储的是文档编号:1
11
IBM Computer22
2333.9
null
---------
真正的命中率:0.51104903
存储的是文档编号:2
12
IBM Computer22
2333.9
null
---------
真正的命中率:0.51104903
存储的是文档编号:3
13
IBM Computer1112
2333.9
null
---------
商品编号:13,商品名称:IBM Computer1112 IBM,商品价格:2333.9,商品的详细信息:null
商品编号:11,商品名称:IBM Computer,商品价格:2333.9,商品的详细信息:null
商品编号:11,商品名称:IBM Computer22,商品价格:2333.9,商品的详细信息:null
商品编号:12,商品名称:IBM Computer22,商品价格:2333.9,商品的详细信息:null
商品编号:13,商品名称:IBM Computer1112,商品价格:2333.9,商品的详细信息:null



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值