JAVA_WEB项目之Lucene使用中文分词器IKAnalyzer3.2.8

来源:互联网 发布:淘宝店铺海报制作教程 编辑:程序博客网 时间:2024/05/13 20:33

上一篇介绍了JAVA_WEB项目之Lucene检索框架中的IndexWriter、IndexSearch优化,使用的都是标准的分词器,也就是老外的以一套分词器,主要是以英文的以空格等标准进行分词,和中文分词相差甚远。下面贴出介绍一下中文分词的类别:

1、最大词长分词:

ikanalyzer |  是  |  一个  |  开源  |  的  |  基于  | java |  诧言  |  开发  |  的  |  轻量级  |  的  |  中
文  |  分词  |  工具包  |  从  | 2006 |  年    | 12 |  月  |  推出  | 1.0 |  版  |  开始  | ikanalyzer | 
已经  |  推出  |  出了  |    3  |    个    |    大    |    版本

2、细粒度分词:

ikanalyzer |  是  |  一个  |  一  |  个  |  开源  |  的  |  基于  |    java    |  诧言  |  开发  |  的  |  轻
量级  |  量级  |  的  |  中文  |  分词  |  工具包  |  工具  |  从  | 2006 |  年    | 12 |  月  |  推出  | 
1.0 |  版  |  开始  | ikanalyzer |  已经  |  推出  |  出了  |    3  |    个    |    大    |    版本

其中IKAnalyzer的构造方法中:

new IKAnalyzer();//表示细粒度分词

new IKAnalyzer();//表示最大词长分词

下面依然使用上一篇实例的代码:

依然是一个实体类Goods:

package com.shop.demo2;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;}}

ConfigureLucene:

package com.shop.demo2;import java.io.File;import java.io.IOException;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 org.apache.lucene.util.Version;import org.wltea.analyzer.lucene.IKAnalyzer;public class ConfigureLucene {private ConfigureLucene(){}//创建索引库private static Directory dir=null;//创建分词器private static Analyzer ana=null;static{//根据指定的路径创建索引库,如果路径不存在就会创建try {dir=FSDirectory.open(new File("c:/demo"));//不同的分词器的版本不同,分词的算法不同,StandardAnalyzer只适用于英文//ana=new StandardAnalyzer(Version.LUCENE_30);ana=new IKAnalyzer(true);//使用最大词长分词} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}public static Directory getDir() {return dir;}public static Analyzer getAna() {return ana;}}
DocumentUtil:

package com.shop.demo2;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;public class DocumentUtil {private DocumentUtil(){}/** * 把goods对象转为document对象 */public static Document goodsToDocument(Goods goods){//把goods对象转为documentDocument doc=new Document();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));return doc;}/** * 把document对象转为goods对象 */public static Goods documentToGoods(Document doc){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"));return goods;}}
LuceneUtil:

package com.shop.demo2;import java.io.IOException;import org.apache.lucene.index.CorruptIndexException;import org.apache.lucene.index.IndexWriter;import org.apache.lucene.index.IndexWriter.MaxFieldLength;import org.apache.lucene.search.IndexSearcher;import org.apache.lucene.store.LockObtainFailedException;import org.wltea.analyzer.lucene.IKAnalyzer;public class LuceneUtil {//项目启动的时候创建,关闭的时候销毁private static IndexWriter indexWriter=null;private static IndexSearcher indexSearcher=null;static{//类加载的时候初始化indexWritertry {indexWriter=new IndexWriter(ConfigureLucene.getDir(), ConfigureLucene.getAna(), MaxFieldLength.LIMITED);//在项目销毁的时候关闭indexWriter,每个应用程序对应一个RuntimeRuntime.getRuntime().addShutdownHook(new Thread(){@Overridepublic void run() {// TODO Auto-generated method stubtry {System.out.println("--J2SE 资源销毁的代码在此处执行--");indexWriter.close();} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}});} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}/** * 在用的时候取indexWriter,关闭IndexSearcher,因为我们知道如果不关闭IndexSearcher,下次取得IndexSearcher是从内存中取得,并没有同步到索引库 * 因此会导致我们刚插入的数据在用IndexSearcher查询的时候会查询不得到刚插入的数据 * @return */public static IndexWriter getIndexWriter() {closeIndexSearcher();//关键代码return indexWriter;}public static IndexSearcher getIndexSearcher() {//作用: 避免其他线程等待,意思是指如果有一个线程执行到创建indexSearcher之后,那么下一个或者多个线程就不用在进入到synchronized里面if(indexSearcher==null){synchronized (LuceneUtil.class) {//如果没有其他线程创建了indexSearcher,只允许一个线程进入到里面创建if(indexSearcher==null){//作用:是否需要创建indexSearchertry {indexSearcher=new IndexSearcher(ConfigureLucene.getDir());} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}}return indexSearcher;}public static void closeIndexWriter(){if(indexWriter!=null){try {indexWriter.close();indexWriter=null;} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}public static void closeIndexSearcher(){if(indexSearcher!=null){try {indexSearcher.close();indexSearcher = null;} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}}


实现类:

package com.shop.demo2;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.CorruptIndexException;import org.apache.lucene.index.IndexWriter;import org.apache.lucene.index.IndexWriter.MaxFieldLength;import org.apache.lucene.index.Term;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;import org.wltea.analyzer.lucene.IKQueryParser;/** *  此案例实现Lucene向索引库中添加索引和查询索引的功能 * @author Administrator * */public class HelloWordLucene {/** * 把good商品对象添加到索引库中 * @param goods */public void addDocument(Goods goods){//创建indexWriterIndexWriter indexwriter=null;try {indexwriter=LuceneUtil.getIndexWriter();//把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(DocumentUtil.goodsToDocument(goods));// 如果没有提交,在没有异常的情况close()之前会自动提交indexwriter.commit();} catch (Exception e) {try {indexwriter.rollback();throw new RuntimeException(e);} catch (IOException e1) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}/** * 根据指定的条件查询, * @param name 指定的关键字 * @return 封装了goods对象的list集合 */public List<Goods> queryGoods(String name){List<Goods> goodsList=new ArrayList<Goods>();//创建查询对象IndexSearcher searcher=null;try {searcher=LuceneUtil.getIndexSearcher();//  指定查询的关键字到索引库查询Query query=IKQueryParser.parse("name", 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(DocumentUtil.documentToGoods(doc));}} catch (Exception e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);} return goodsList;}public void deleteDocument(int id){//创建indexWriterIndexWriter indexwriter=null;try {indexwriter=LuceneUtil.getIndexWriter();// 一般来说都是通过id来删除,所以即使是通过name查询,ID也要建索引,因为更新和删除需要id// 根据ID把符合条件的document对象删除掉,但是索引(term) 并没有删除indexwriter.deleteDocuments(new Term("id", id+""));//同步删除索引库中的索引部分indexwriter.optimize();// 如果没有提交,在没有异常的情况close()之前会自动提交indexwriter.commit();} catch (Exception e) {try {indexwriter.rollback();throw new RuntimeException(e);} catch (IOException e1) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}public void updateDocument(Goods goods){IndexWriter indexwriter=null;try{indexwriter=LuceneUtil.getIndexWriter();indexwriter.updateDocument(new Term("id", goods.getId().toString()), DocumentUtil.goodsToDocument(goods));indexwriter.optimize();indexwriter.commit();}catch (Exception e) {// TODO: handle exceptiontry {indexwriter.rollback();throw new RuntimeException(e);} catch (IOException e1) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}/** * 实现分页的功能   * @param name 查询的关键字 * @param currentPage 当前的页数 * @return 记录数 */public List<Goods> queryByPage(String name,int currentPage){int number=5; // 每页显示5条List<Goods> goodsList=new ArrayList<Goods>();IndexSearcher indexSearcher=null;try {// 创建查询对象indexSearcher=LuceneUtil.getIndexSearcher();// 指定查询的关键字Query query=IKQueryParser.parse("name", name);TopDocs topDocs=indexSearcher.search(query,currentPage*number);// 此变量/每页显示的记录数就是总页数System.out.println("真正命中的结果数:" + topDocs.totalHits);int totalPage=0;if(topDocs.totalHits%number!=0){totalPage=topDocs.totalHits/number+1;}else{totalPage=topDocs.totalHits/number;}System.out.println("通过系统的总结果数/每页显示的数量=总页数" + totalPage);// 返回的是符合条件的文档编号,并不是文档本事ScoreDoc[] scoreDocs = topDocs.scoreDocs;// 去期望值  和实际值的 最小值System.out.println("真正查询出来的数组的长度:" + scoreDocs.length);for(int i=(currentPage-1)*number;i<scoreDocs.length;i++){ScoreDoc scoreDoc=scoreDocs[i];System.out.println("存储了命中率积分:" + scoreDoc.score);System.out.println("存储的是文档编号:" + scoreDoc.doc);// 第二次查询: 通过文档的编号,查询真正的文档信息Document document=indexSearcher.doc(scoreDoc.doc);goodsList.add(DocumentUtil.documentToGoods(document));}} catch (Exception e) {throw new RuntimeException(e);}return goodsList;}}

测试类:

/** * 测试中文分词器IKAnalyzer3.2.8添加 */@Testpublic void test8() {Goods goods=new Goods();goods.setId(11);goods.setName("IBM是全球知名的电脑厂商,现在已经专门做服务的,已经把生产版权转给联想了");goods.setPrice(2333.9);goods.setRemark("IBM有质量保证,耐用");hellowod.addDocument(goods);}/** * 测试中文分词器IKAnalyzer3.2.8的检索 */@Testpublic void test10() {List<Goods> list= hellowod.queryGoods("电脑");for(Goods good:list){System.out.println("商品编号:"+good.getId()+",商品名称:"+good.getName()+",商品价格:"+good.getPrice()+",商品的详细信息:"+good.getRemark());}}

测试结果:

真正命中的结果数:1真正的命中率:0.37037593存储的是文档编号:311IBM是全球知名的电脑厂商,现在已经专门做服务的,已经把生产版权转给联想了2333.9null---------商品编号:11,商品名称:IBM是全球知名的电脑厂商,现在已经专门做服务的,已经把生产版权转给联想了,商品价格:2333.9,商品的详细信息:null--J2SE 资源销毁的代码在此处执行--




0 0
原创粉丝点击