iBATIS&Spring合奏(五)--整合lucene搜索表字段内容
发布日期:2021-07-01 05:14:48 浏览次数:3 分类:技术文章

本文共 7198 字,大约阅读时间需要 23 分钟。

     之所以加上lucene来建立搜索引擎,是因为其轻便简单以及快速。Lucene作为开源社区很好的建立搜索引擎的框架也给我们(Java程序员)提供了很友好的方式。这次介绍的也是简单的整合,对于Lucene的一些复杂特性还请大家仔细阅读文档以及书籍或求助Google以及源码。
      其实这篇博客早就要写了,只是最近遇到了非常纠结的事情,那就是要考一门我完全不会的课——计量经济学。都是模型,公式,觉得数学和经济学真的是一家人,而我和它们毫无缘分。今天公益劳动实验室的老师说搞技术的工作累,费心费脑,其实觉得也到差不多各行各业,都要承受压力,都要有自己的突破。。。God,跑题……
     下面的示例是建立在前面几篇文章的基础上的。原来的项目代码可以在前几篇下载得到,就免去看那些罗嗦了。还是一步一步开始。
     1)在数据库中建立内容字段。原来的User表中加入一个note字段就OK。因为我们要对这个字段的存储内容进行索引。有人说有数据库我可以直接搜索数据库啊,模糊查询是吧,效率不高吧,做一次IO和做一次数据库操作,还是模糊查询……当然先不考虑这么多,我们只是先借用DB的内容。到时候搜索的时候就完全操作文件了,数据库就不知道我们做了什么……
     2)调整POJO,VO结构加入note属性。
     3)建立索引。每次搜索不是全文搜索,我们要对需要被搜索的内容先建立索引,然后搜索会通过索引来查询内容。搜索的算法决定了效率。为了简便,就在JUNIT中试一下吧。不过首先我们要有个存储索引文件的路径以及一个分析器,分析器有很多种,这里选用按照词库检索的方式的分析器。当然搜索的时候也要用同样的分析器。这个后面说。准备代码如下:
@SuppressWarnings("unused")	private final String INDEXPATH = "g:\\userindex";	@SuppressWarnings("unused")	private Analyzer analyzer = new MMAnalyzer();	UserDao service;
 
     上面的UserDao可以参看前几章,是需要通过Spring注入进来滴。然后看看我们的建立索引的代码:
@Test	public void createIndex() {		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");		service = (UserDao) ctx.getBean("userDao");        List
list = service.getAllUsers(); ctx.destroy(); try { Directory directory = FSDirectory.getDirectory(INDEXPATH); IndexWriter indexWriter = new IndexWriter(directory, analyzer ,true, IndexWriter.MaxFieldLength.LIMITED); long begin = new Date().getTime(); for(User u: list) { Document doc = new Document(); String username = u.getUsername() == null ? "" : u.getUsername().trim(); String note = u.getNote() == null ? "" : u.getNote(); String company = u.getCompany() == null ? "" : u.getCompany(); doc.add(new Field("username", username, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES)); doc.add(new Field("note", note, Field.Store.COMPRESS, Field.Index.ANALYZED, Field.TermVector.NO)); doc.add(new Field("company", company, Field.Store.YES,Field.Index.NOT_ANALYZED,Field.TermVector.YES)); indexWriter.addDocument(doc); } long end = new Date().getTime(); System.out.println(">>> 1.存入索引完毕.. 共花费:" + (end - begin) +"毫秒..."); //优化,合并 indexWriter.optimize(); indexWriter.close(); }catch(Exception e){ e.printStackTrace(); } }
       4)写搜索方法。搜索方法我们返回User对象,当然是内存中的对象,我们没有填充所有的字段,取回对象只是为了在前段方便获取(因为我们用的AMF协议是建立在HTTP基础上通过序列化与反序列化传输对象的方式来进行远程访问的)。为了不造成困扰在此说明。先看下代码,代码放在了UserService类中,在前段可以直接调用,当然也是拜托Spring帮助我们来管理切面逻辑的类。方法代码如下:
public List
getUsersByQuery(String query) { try { List
qlist = new ArrayList
(); String fieldName1 = "username"; String fieldName2 = "note"; IndexSearcher indexSearcher = new IndexSearcher(INDEXPATH); // QueryParser parser = new QueryParser(fieldName, analyzer); //单字 // key 搜索 // Query queryOBJ = parser.parse(query);// System.out.println(">>> 2.开始读取索引... ... 通过关键字:【 " + query + " 】");// long begin = new Date().getTime(); // 下面的是进行username,note两个范围内进行收索. BooleanClause.Occur[] clauses = { BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD }; Query queryOBJ = MultiFieldQueryParser.parse(query, new String[] { "username","note"}, clauses, new MMAnalyzer());// parser.parse(query); Filter filter = null; // ################# 搜索相似度最高的记录 ################### TopDocs topDocs = indexSearcher.search(queryOBJ, filter, 1000); // TopDocs topDocs = indexSearcher.search(queryOBJ , 10000); System.out.println("*** 共匹配:" + topDocs.totalHits + "个 ***"); User user = null; // 输出结果--按分数计算 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { Document targetDoc = indexSearcher.doc(scoreDoc.doc); user = new User(); // 设置高亮显示格式// SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter(// "
", ""); SimpleHTMLFormatter ff=new SimpleHTMLFormatter("<",">");// /* 语法高亮显示设置 */ Highlighter highlighter = new Highlighter(ff,new QueryScorer(queryOBJ)); highlighter.setTextFragmenter(new SimpleFragmenter(100)); // 设置高亮 设置 username 字段 String username = targetDoc.get("username"); String note=targetDoc.get("note"); TokenStream titleTokenStream = analyzer.tokenStream(fieldName1, new StringReader(username)); String highLightName = highlighter.getBestFragment( titleTokenStream, username); TokenStream noteTokenStream = analyzer.tokenStream(fieldName2, new StringReader(note)); String highLightNote = highlighter.getBestFragment( noteTokenStream, note);// if (highLightName == null) highLightName = username; if(highLightNote == null) highLightNote = note; user.setUsername(highLightName); user.setNote(highLightNote); qlist.add(user); }// long end = new Date().getTime();// System.out.println(">>> 3.搜索完毕... ... 共花费:" + (end - begin)// + "毫秒..."); indexSearcher.close(); return qlist; } catch (Exception e) { e.printStackTrace(); return null; } }
      具体的lucene API就不多做解释了。因为我们用Flex做前端所以高亮器就没有设置。对于jsp还是可以应用的,所以代码注释掉了。我们可以看到MultiFieldQueryParser构造器中传入了我们建立索引时候的MMAnalyzer分析器,如前所说,他们是要一样的。就像序列化与反序列化,他们要遵循同样的规则一样。要不搜索的时候那抱歉了,我(Query)只认识MMAnalyzer分词后的索引啦。
      5)前端调用。这个和前面没什么差别了。还是要RPC对象。spritis.mxml文件的RPC配置RemoteObject只要添加一个方法如下:
{query.text}
       同样AS代码对于result事件的响应代码如下:
private function getUsersByQuery_resultHandler(event:ResultEvent):void            {                users = event.result as ArrayCollection;                            }
     接下来mxml语言发挥作用,直接调用UserService的搜索方法就好啦:
     注意到了click事件响应的东西是神马了吗,就是我们的远程服务器端方法名,传入参数是我们用户在TextInput 控件中输入的东西。
      6)运行,测试。下面的工作就是启动Tomcat,这只可爱的小猫咪,然后在搜索的输入框输入“麻花”二字,毫无悬念地,我们会在DataGrid控件中看到检索出的东西,只是作为内存中的User对象输出,而非数据库中的啦。简单看一下运行后的结果图吧,比较粗糙,情有审美眼光的童鞋谅解……我最怕写前端了。
         可以看到关键字麻花被括起来了,那是高亮器,简易版本的。因为麻花是一个词典中的词,所以被搜索出来了,如果输入“麻”就没有结果了,因为我们采用的是MM词典分词。当然lucene有很多种分词器,也可以写自己的分析器,过滤器(像百度之类的),还可以写自己的结果排序规则(像百度广告在前面显示)。这里不详述了,只做简单介绍。
         写到这里我还是很迷茫,因为明天要考计量经济学,我很佩服研究出那些模型的老外。看来还是兴趣为先驱,坚持是硬道理。现在听到了SHE的《爱就对了》,其实敲代码和恋爱都是一样的也许,都要承受一定的痛苦期,坚持,专心,过程中也会有磕磕绊绊,但是每次都克服过来,都会有很好的结果的。好吧,敲就对了……
         最后,老规矩不变,给所有可爱的猿类们一个轻松的小笑话(这个笑话有点长请大家坚持,,,):
某日,老师在课堂上想考考学生们的智商,就问一个男孩: “树上有十只鸟,开枪打死一只,还剩几只?”
男孩反问:“是无声的枪,还是其他没有声音的枪么?”
“不是.”
“枪声有多大?”
“80~100分贝.”
“那就是说会震的耳朵疼?”
“是.”
“在这个城市里打鸟犯不犯法?”
‘不犯.”
“您确定那只鸟真的被打死啦?”
“确定.”老师已经不耐烦了,”拜托,你告诉我还剩几只就行了,OK?”
“OK.鸟里有没有聋子?”
“没有.”
“有没有鸟智力有问题,呆傻到听到枪响不知道飞的?”
“没有,智商都在200以上!”
“有没有关在笼子里的?”
“没有.”
“边上还有没有其他的树,树上还有没有其他鸟?”
“没有.” “方圆十里呢?” “就这么一棵树!”
“有没有残疾或饿的飞不动的鸟?”
“没有,都身体倍棒.”
“算不算怀孕肚子里的小鸟?”
“都是公的.”
“都不可能怀孕?”
“………,决不可能.”
“打鸟的人眼里有没有花?保证是十只?”
“没有花,就十只.” 老师脑门上的汗已经流下来了,
下课铃响起,但男孩仍继续问:“有没有傻的不怕死的?”
“都怕死.”
“有没有因为情侣被打中,自己留下来的?”
“笨蛋,之前不是说都是公的嘛!”
“同志可不可以啊!”
“………….,性取向都很正常!”
“会不会一枪打死两只?”
“不会.”
“一枪打死三只呢?”
“不会.”
“四只呢?”
“更不会!”
“五只呢?”
“绝对不会!!!”
“那六只总有可能吧?”
“除非你他妈的是猪生的才有可能!一枪只能打死一只!”
“…好吧,那么所有的鸟都可以自由活动么?”
“完全可以.”
“它们受到惊吓起飞时会不会惊慌失措而互相撞上?”
“不会,每只鸟都装有卫星导航系统,而且可以自动飞行.”
“恩,如果您的回答没有骗人,”学生满怀信心的回答,“打死的鸟要是挂在树上没掉下来,那么就剩一只,如果掉下来,就一只不剩.”
老师推推眼镜,强忍着要昏倒的感觉,颤抖地说道:“你可以去当程序员了……”
  • 大小: 135 KB

转载地址:https://nanyao.blog.csdn.net/article/details/46715385 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:寒假的唠叨
下一篇:iBATIS&Spring合奏(四)--设计模式in iBATIS

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年05月06日 07时38分53秒