Lucene7.1案例
使用Lucene7.1、jdk1.8
1、创建maven项目、导入依赖
org.apache.lucene
lucene-core
7.1.0
org.apache.lucene
lucene-analyzers-smartcn
7.1.0
org.apache.lucene
lucene-queryparser
7.1.0
org.apache.lucene
lucene-highlighter
7.1.0
junit
junit
4.12
2、创建包、创建实体类
package com.mrzhong.pojo;
import java.io.Serializable;
public class Goods implements Serializable {
private static final long serialVersionUID = 5251314995707817799L;
private Integer goodsId;
private String goodsName;
private Double goodsPrice;
private String goodsRemark;
public Goods() {
super();
}
public Goods(Integer goodsId, String goodsName, Double goodsPrice,
String goodsRemark) {
super();
this.goodsId = goodsId;//商品ID
this.goodsName = goodsName;//商品名称
this.goodsPrice = goodsPrice;//商品价格
this.goodsRemark = goodsRemark;//商品备注、描述
}
@Override
public String toString() {
return "Goods [goodsId=" + goodsId + ", goodsName=" + goodsName
+ ", goodsPrice=" + goodsPrice + ", goodsRemark=" + goodsRemark
+ "]";
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public Double getGoodsPrice() {
return goodsPrice;
}
public void setGoodsPrice(Double goodsPrice) {
this.goodsPrice = goodsPrice;
}
public String getGoodsRemark() {
return goodsRemark;
}
public void setGoodsRemark(String goodsRemark) {
this.goodsRemark = goodsRemark;
}
}
3、编写保存商品、查询商品的持久层类
package com.mrzhong.MyLucene;
import com.mrzhong.pojo.Goods;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
public class FirstLuceneDaoImpl {
/**
* 保存一个商品
* @throws IOException
*/
public void saveGoods(Goods goods) throws IOException {
//保存路径
final Path path = Paths.get("./src/main/dir");
//构建索引库
Directory directory = FSDirectory.open(path);
//设置分词器
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
//设置打开索引库读写的方式:CREATE->覆盖原来索引,APPEND->追加到原来的索引。
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
//创建增删改索引库的操作对象,添加文档并提交
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//创建文档对象
Document document = new Document();
document.add(new TextField("goodsId", goods.getGoodsId().toString(), Field.Store.YES));
document.add(new TextField("goodsName", goods.getGoodsName().toString(), Field.Store.YES));
document.add(new TextField("goodsRemark", goods.getGoodsRemark().toString(), Field.Store.YES));
document.add(new TextField("goodsPrice", goods.getGoodsPrice().toString(), Field.Store.YES));
indexWriter.addDocument(document);
indexWriter.close();
}
public List searchGoods(String goodsName) throws IOException, org.apache.lucene.queryparser.classic.ParseException {
List reGoods = new ArrayList<>();
//String queryString = "全文检索";
String queryString = goodsName;
//多条件
//Query q = MultiFieldQueryParser.parse(new String[]{},new String[]{},new StandardAnalyzer());
//设置路径
final Path path = Paths.get("./src/main/dir");
//打开索引库
Directory directory = FSDirectory.open(path);
Analyzer analyzer = new StandardAnalyzer();
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//单条件查询--创建查询解析器
QueryParser queryParser = new QueryParser("goodsName",analyzer);
Query query = queryParser.parse(queryString);
//执行查询--设置返回最多条数
TopDocs topDocs = indexSearcher.search(query,10);
long conut = topDocs.totalHits;
System.out.println("检索总条数:"+conut);
//根据文档编号遍历真正的文档
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
Goods goods = new Goods();
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println("相关度:"+scoreDoc.score+"-----time:"+document.get("time"));
goods.setGoodsId(Integer.parseInt(document.get("goodsId")));
goods.setGoodsName(document.get("goodsName"));
goods.setGoodsPrice(Double.parseDouble(document.get("goodsPrice")));
goods.setGoodsRemark(document.get("goodsRemark"));
reGoods.add(goods);
}
return reGoods;
}
}
4、测试类
package com.mrzhong.MyLucene;
import com.mrzhong.pojo.Goods;
import org.apache.lucene.queryparser.classic.ParseException;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class FirstLunceneDaoImplTest {
public static void main(String[] args) {}
@Test
public void addTest(){
Goods goods=new Goods(2,"OPPO高端手机001",11.1,"OPPO手机就是666,全国第一品牌");
FirstLuceneDaoImpl firstLuceneDaoImpl = new FirstLuceneDaoImpl();
try {
firstLuceneDaoImpl.saveGoods(goods);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("-------保存成功!-------");
}
@Test
public void searchTest(){
String goodsName = "手机";
FirstLuceneDaoImpl firstLuceneDaoImpl = new FirstLuceneDaoImpl();
try {
List re = firstLuceneDaoImpl.searchGoods(goodsName);
re.forEach(v->System.out.println(v.toString()));
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
工具类编写
配置信息类
package com.mrzhong.LuceneUtil;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import java.nio.file.Paths;
/**
* 配置信息类
*/
public class Configuration {
private Configuration(){}
public static final Analyzer ANALYZER=new StandardAnalyzer();
private static Directory DIRECTORY;
private static String PATH="./src/main/dir";
static{
try{
DIRECTORY= FSDirectory.open(Paths.get(PATH));
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static Directory getDIRECTORY() {
return DIRECTORY;
}
}
编写javaBean和document互转的工具类
package com.mrzhong.LuceneUtil;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.TextField;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static com.sun.org.glassfish.gmbal.ManagedObjectManagerFactory.getMethod;
public class BeanUtils {
/**
* 普通javaBean转成Lucene文档类
* @param obj
* @return
* @throws Exception
*/
public static Document beanToDocument(Object obj) throws Exception {
//创建文档类
Document document = new Document();
//获取javaBean的字节码
Class clazz = obj.getClass();
//获取Field对象数组
Field reflectFields[] = clazz.getDeclaredFields();
//遍历
for (Field field: reflectFields) {
//设置特权访问
field.setAccessible(true);
//获取字段名
String fieldName = field.getName();
//根据字段、合成get方法--javaBean编写规范:字段首字母小写xxXxx,get、set方法驼峰式getXxxXx()
String init = fieldName.substring(0, 1).toUpperCase();
String methodName = "get" + init + fieldName.substring(1);
//根据字段的get方法名,获取反射的方法类
Method reflectMethod = getMethod(clazz, methodName);
//判断、获取方法返回的结果
if (reflectMethod!=null){
//执行、返回结果
Object returnValue = reflectMethod.invoke(obj,null);
//判断返回结果是否为空,避免为空
if(returnValue == null){
continue;
}
//将方法的返回结果添加到文档对象中
document.add(new TextField(fieldName,returnValue.toString(), org.apache.lucene.document.Field.Store.YES));
}else {
continue;
}
}
return document;
}
public static Object documentToBean(Document document,Class clazz) throws Exception {
Object obj = clazz.newInstance();
//根据javaBean字节码获取字段数组
Field[] reflectField = clazz.getDeclaredFields();
//遍历
for(Field field: reflectField){
//设置特权访问
field.setAccessible(true);
//获取字段的名字
String fieldName = field.getName();
String documentFieldValue = document.get(fieldName);
if(documentFieldValue == null ){
continue;
}
//给属性赋值
org.apache.commons.beanutils.BeanUtils.setProperty(obj, fieldName,
documentFieldValue);
}
return obj;
}
}
编写IndexWriter封装工具类
package com.mrzhong.LuceneUtil;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import java.io.IOException;
public class IndexWriterUtils {
private IndexWriterUtils(){}
private static IndexWriter indexWriter;
static{
try {
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Configuration.ANALYZER);
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.APPEND);
indexWriter=new IndexWriter(Configuration.getDIRECTORY(), indexWriterConfig);
//注册事件,应用关闭时释放资源;addShutdownHook可以添加JVM停止时要处理事件。
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run(){
try {
indexWriter.close();
System.out.println("indexWriter is close");
} catch (CorruptIndexException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static IndexWriter getIndexWriter(){
return indexWriter;
}
}
测试工具类
package com.mrzhong.LuceneUtil;
import com.mrzhong.pojo.Goods;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.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.junit.Test;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
/**
* 工具测试类
*/
public class LunceneUtilTest {
@Test
public void testBeanToDocument() throws Exception {
Goods goods=new Goods(5,"华为荣耀6Plus",11.1,"华为手机就是666,全国第二品牌");
//创建增删改索引库的操作对象,添加文档并提交
IndexWriter indexWriter = IndexWriterUtils.getIndexWriter();
//创建文档对象
Document document = BeanUtils.beanToDocument(goods);
//提交文档对象
indexWriter.addDocument(document);
indexWriter.close();
System.out.println("保存成功---------");
}
@Test
public void testDocumentToBean() throws Exception {
List reGoods = new ArrayList<>();
//String queryString = "全文检索";
String queryString = "华为荣耀6Plus";
//多条件
//Query q = MultiFieldQueryParser.parse(new String[]{},new String[]{},new StandardAnalyzer());
//获取索引库
Directory directory = Configuration.getDIRECTORY();
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//单条件查询--创建查询解析器
QueryParser queryParser = new QueryParser("goodsName",Configuration.ANALYZER);
Query query = queryParser.parse(queryString);
//执行查询--设置返回最多条数
TopDocs topDocs = indexSearcher.search(query,10);
long conut = topDocs.totalHits;
System.out.println("检索总条数:"+conut);
//根据文档编号遍历真正的文档
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println("相关度:"+scoreDoc.score);
//将文档对象转成javaBean
Goods goods = (Goods) BeanUtils.documentToBean(document,Goods.class);
reGoods.add(goods);
}
reGoods.forEach(v->System.out.println(v.toString()));
}
}
简单CRUD
Dao层接口
package com.mrzhong.LuceneCrud;
import java.io.IOException;
import java.util.List;
/**
* LuceneDao层接口
*/
public interface LuceneDao {
//查询接口--多条件查询:参数1:-查询关键字,参数2:参与查询的字段
public List search(String[] queryies, String[] fields,Class clazz) throws IOException, Exception;
//保存接口
public void save(Object object) throws Exception;
//修改接口:参数1-检索的字段名,参数2:更新的对象
public Boolean update(String fieldName,Object object);
//删除接口
public Boolean delete(String fieldName, String fieldValue);
}
Dao层接口实现类
package com.mrzhong.LuceneCrud;
import com.mrzhong.LuceneUtil.BeanUtils;
import com.mrzhong.LuceneUtil.Configuration;
import com.mrzhong.LuceneUtil.IndexWriterUtils;
import com.mrzhong.pojo.Goods;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.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 java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import static com.sun.org.glassfish.gmbal.ManagedObjectManagerFactory.getMethod;
public class LuceneDaoImpl implements LuceneDao{
private Directory directory = Configuration.getDIRECTORY();
@Override
public List
接口实现类测试
package com.mrzhong.LuceneCrud;
import com.mrzhong.pojo.Goods;
import com.mrzhong.pojo.Person;
import org.junit.Test;
import java.util.List;
/**
* 测试类
*/
public class LuceneDaoImplTest {
LuceneDao luceneDao = new LuceneDaoImpl();
//测试保存
@Test
public void testSave() throws Exception {
Goods goods=new Goods(2,"小米电视2s",11.1,"全面屏,带你体验不一样的视觉盛宴.2s");
luceneDao.save(goods);
}
//测试查询
@Test
public void testSearch() throws Exception {
List reGoods = luceneDao.search(new String[]{"小米"},new String[]{"goodsName"},Goods.class);
reGoods.forEach(v->System.out.println(v.toString()));
}
//测试更新
@Test
public void testUpdate(){
Goods goods=new Goods(1,"小米电视1s",11.1,"全面屏,带你体验不一样的视觉盛宴。价格优惠");
luceneDao.update("goodsId",goods);
}
//测试删除
@Test
public void testDelete(){
luceneDao.delete("goodsId","1");
}
}
分页查询
Dao层添加接口
//分页查询接口--多条件查询
public List searchByPage(String[] queryies, String[] fields,int pageIndex,int pageSize,Class clazz) throws IOException, Exception;
接口实现类
@Override
public List searchByPage(String[] queryies, String[] fields, int pageIndex, int pageSize, Class clazz) throws IOException, Exception {
List reObject = new ArrayList<>();
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
if(pageIndex<1){
pageIndex=1;
}
if(pageSize<1){
pageSize=1;
}
//查询页的开始记录数的序号
int startRecord=(pageIndex-1)*pageSize;
//查询页的结束记录数序号
int endRecord=pageIndex*pageSize;
//多条件
Query query = MultiFieldQueryParser.parse(queryies,fields,Configuration.ANALYZER);
//单条件查询--创建查询解析器
/*QueryParser queryParser = new QueryParser("goodsName",Configuration.ANALYZER);
Query query = queryParser.parse(qureyString);*/
//执行查询--设置返回最多条数
TopDocs topDocs = indexSearcher.search(query,endRecord);
long conut = topDocs.totalHits;
System.out.println("检索总条数:"+conut);
if(topDocs.totalHits<=startRecord){
System.out.println("没有记录了!");
return null;
}
if(endRecord>topDocs.totalHits){
endRecord= (int) topDocs.totalHits;
}
//根据文档编号遍历真正的文档
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (int i=startRecord;i
测试类
//测试分页查询
@Test
public void testSearchByPage() throws Exception {
List reGoods = luceneDao.searchByPage(new String[]{"小米"},new String[]{"goodsName"},2,2,Goods.class);
reGoods.forEach(v->System.out.println(v.toString()));
}
结果高亮显示
高亮工具类
package com.mrzhong.LuceneUtil;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.highlight.*;
import java.util.List;
public class HighLightUtil {
public static String doHighLight(Query query, String fieldName, String fieldValue, int size){
String result="";
try {
//设置高亮的格式
Formatter formatter=new SimpleHTMLFormatter("","");
//指定高亮的查询条件
Scorer scorer=new QueryScorer(query);
Highlighter highlighter=new Highlighter(formatter, scorer);
//指定高亮后的长度,需要SimpleFragmenter类型参数,这里指定长度为参数size(结尾为标点符号或不够长的词一般不显示出来,所以一般会少几个字)
highlighter.setTextFragmenter(new SimpleFragmenter(size));
//设置对哪个字段进行高亮操作,返回高亮后的结果
result=highlighter.getBestFragment(Configuration.ANALYZER, fieldName, fieldValue);
//把高亮后的值重新赋给字段
if(result==null){
if(fieldValue!=null&&fieldValue.length()>=size){
result=fieldValue.substring(0, size);
}else{
result=fieldValue;
}
}
System.out.println("util.reuslt:"+result);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}
Dao层接口
//查询接口-查询结果高亮
public List searchResultHighLight(String[] queryies, String[] fields,Class clazz) throws IOException, Exception;
接口实现
@Override
public List searchResultHighLight(String[] queryies, String[] fields, Class clazz) throws IOException, Exception {
List reObject = new ArrayList<>();
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//多条件
Query query = MultiFieldQueryParser.parse(queryies,fields,Configuration.ANALYZER);
//单条件查询--创建查询解析器
/*QueryParser queryParser = new QueryParser("goodsName",Configuration.ANALYZER);
Query query = queryParser.parse(qureyString);*/
//执行查询--设置返回最多条数
TopDocs topDocs = indexSearcher.search(query,10);
long conut = topDocs.totalHits;
System.out.println("检索总条数:"+conut);
//根据文档编号遍历真正的文档
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println("相关度:"+scoreDoc.score);
//将文档对象转成javaBean
Object object = BeanUtils.documentToBean(document,clazz);
//设置高亮
for(String fieldName : fields){
String highLightResult = HighLightUtil.doHighLight(query,fieldName, document.get(fieldName), 5);
org.apache.commons.beanutils.BeanUtils.setProperty(object, fieldName,
highLightResult);
}
reObject.add(object);
}
return reObject;
}
测试
//测试高亮显示查询
@Test
public void testSearchResultHighLight() throws Exception {
List reGoods = luceneDao.searchResultHighLight(new String[]{"小米"},new String[]{"goodsName"},Goods.class);
reGoods.forEach(v->System.out.println(v.toString()));
}