lucene+jquery+struts2json插件实现搜索框自动补全
看了一堆自动补齐的例子,大多都是只有前台没有后台,有后台也是在后台写死的数据,所以不具有真实的实用性,拿来学习还是可以的,但也是仅仅限于jquery的学习。
接下来我要写一个从后台数据库生成lucene索引文件,在struts2Action中查根据key值查询索引文件,获取到部分符合关键字(key值)的数据,通过json格式返回到前台页面,在使用jquery完成自动补齐的完整例子。
首先第一步从数据库中获取索引文件。
packagecom.net263.boss.charge.action; importjava.util.ArrayList; importjava.util.List; importorg.apache.log4j.Logger; importorg.apache.lucene.analysis.standard.StandardAnalyzer; importorg.apache.lucene.document.Document; importorg.apache.lucene.document.Field; importorg.apache.lucene.index.IndexWriter; importorg.quartz.JobExecutionContext; importorg.quartz.JobExecutionException; importorg.springframework.scheduling.quartz.QuartzJobBean; importcom.net263.boss.business.service.ICustomerService; publicclassCreateSearchIndexextendsQuartzJobBean{ /* *(non-Javadoc) * *@see *org.springframework.scheduling.quartz.QuartzJobBean#executeInternal(org *.quartz.JobExecutionContext) */privateICustomerServicecustomerService; /** *@returnthecustomerService */publicICustomerServicegetCustomerService(){ returncustomerService; } /** *@paramcustomerServicethecustomerServicetoset */publicvoidsetCustomerService(ICustomerServicecustomerService){ this.customerService=customerService; } privatestaticLoggerlogger=Logger.getLogger(CreateSearchIndex.class); publicvoidexecuteInternal(JobExecutionContextcontext) throwsJobExecutionException{ Stringpath=this.getClass().getClassLoader().getResource("").getPath().replace("","\"\"")+"index"; logger.info("begin..."); //path=path.substring(1).trim(); logger.info("indexpathis:"+path); List<String>list=newArrayList<String>(); IndexWriterwriter; try{ list=customerService.queryCustomerName(); logger.info("getindexsuccessfully."); }catch(Exceptione){ e.printStackTrace(); } try{ writer=newIndexWriter(path,newStandardAnalyzer(),true); for(StringtempC:list) { if(tempC==null||"".equals(tempC)) { continue; } else{ DocumentdocA=newDocument(); FieldfieldA=newField("content",tempC,Field.Store.YES,Field.Index.TOKENIZED); docA.add(fieldA); writer.addDocument(docA); } } //如果对海量数据进行创建索引的时候,需要对索引进行优化,以便提高速度 writer.optimize(); //跟数据库类似,打开一个连接,使用完后,要关闭它 writer.close(); logger.info("end..."); }catch(Exceptione){ e.printStackTrace(); logger.info("error..."); } } }
以上代码是通过Quartz来进行启动的,每分钟进行一次索引文件的同步。
配置文件如下:
<beanid="jobCreateTask"class="org.springframework.scheduling.quartz.JobDetailBean"> <propertyname="jobClass"value="com.net263.boss.charge.action.CreateSearchIndex"/> <propertyname="jobDataAsMap"><map><entrykey="customerService"value-ref="customerService"/></map></property></bean> <beanid="CreateTasktrigger"class="org.springframework.scheduling.quartz.CronTriggerBean"> <propertyname="jobDetail"ref="jobCreateTask"/><propertyname="cronExpression"value="00/10-23**?"/> </bean>
好了,创建索引文件就基本完成了啊。
下面,我们来通过Action获取到所需的自动补全信息。
importjava.util.ArrayList; importjava.util.Date; importjava.util.List; importnet.sf.json.JSONArray; importorg.apache.log4j.Logger; importorg.apache.lucene.analysis.standard.StandardAnalyzer; importorg.apache.lucene.queryParser.QueryParser; importorg.apache.lucene.search.Hits; importorg.apache.lucene.search.IndexSearcher; importorg.apache.lucene.search.Query; importorg.apache.struts2.json.annotations.JSON; importcom.net263.boss.util.JsonUtils; importcom.opensymphony.xwork2.ActionSupport; publicclassQueryCustomerNameActionextendsActionSupport{ Stringpath=this.getClass().getClassLoader().getResource("").getPath().replace("","\"\"")+"index"; privateStringcustomerName; List<String>list=newArrayList<String>(); /** *@returnthelist */publicList<String>getList(){ returnlist; } /** *@paramlistthelisttoset */publicvoidsetList(List<String>list){ this.list=list; } privatestaticfinalLoggerLOGGER=Logger.getLogger(QueryCustomerNameAction.class); publicvoidsetKey(StringcustomerName){ this.customerName=customerName; } publicQueryqueryParser(Stringkey){ //content为默认搜索的Field列名 QueryParserqueryParser=newQueryParser("content",newStandardAnalyzer()); try{ Queryquery=queryParser.parse(key); returnquery; }catch(Exceptione){ e.printStackTrace(); } returnnull; } publicStringsearchName() { DatestartTime,endTime; try{ IndexSearchersearch=newIndexSearcher(path); startTime=newDate(); //抽象的查询对象 if(customerName!=null&&!("".equals(customerName))) { Queryquery=queryParser(customerName); //搜索结果集Hits Hitshits=search.search(query); for(inti=0;i<hits.length();i++){ intbeginIndex=hits.doc(i).toString().indexOf(":"); intendIndex=hits.doc(i).toString().indexOf(">"); list.add(hits.doc(i).toString().substring(beginIndex+1,endIndex)); System.out.println(hits.doc(i).toString().substring(beginIndex+1,endIndex)); if(i>8) { break; } } } endTime=newDate(); System.out.println("本次搜索用时:"+(endTime.getTime()-startTime.getTime())+"毫秒"); LOGGER.info("本次搜索用时:"+(endTime.getTime()-startTime.getTime())+"毫秒"); LOGGER.info("list:"+JsonUtils.listToJson(list)); }catch(Exceptione){ LOGGER.info("没有搜索到对应分词"); //e.printStackTrace(); } return"success"; } }
这里面用到了struts2的json插件,这个插件很好用,它会把Action中所要返回的数据自动封装成json格式的,本例子中需要返回list,就会讲list中存放的数据json化。
需要特别提示的是:
<packagename="searchname"namespace="/business/customer"extends="json-default"> <actionname="search"class="com.net263.boss.business.action.QueryCustomerNameAction"method="searchName"> <!--返回List对象的--><resultname="success"type="json"><paramname="root">list</param></result> </action></package>
需要继承json-default
这样,我们的后台基本就写完了。现在总结一下:后台主要完成的功能就是数据库到索引文件的同步和通过key查询索引文件返回json结果集的这么一个过程。
最后,我们来写前台jquery,这个大部分例子有用了,我就不再详细叙述了,只把代码贴出来。还要说明一点,大多数例子所捕获的键盘信息严重不足,我多加捕获了一些,包括输入汉字时输入法前的数字1-5,当然可以捕获更多。
varhighlightindex=-1; $(document).ready(function(){ varwordInput=$("#customerName"); varwordInputOffset=wordInput.offset(); varauto=$("#autoComplete").hide().css("border","3pxwhitesolid").css("background-color","white") .css("position","absolute") .css("top",wordInputOffset.top+wordInput.height()+12+"px") .css("left",wordInputOffset.left+"px").width(wordInput.width()+6); wordInput.keyup(function(event){ varmyEvent=event||window.event; varkeyCode=myEvent.keyCode; varwordText=$("#customerName").val(); varactionurl="search.action"; if(keyCode>=65&&keyCode<=90||keyCode==8||keyCode==46||keyCode==32||keyCode==49||keyCode==50||keyCode==51||keyCode==52||keyCode==53) { if(wordText!=""){ $.ajax({ url:actionurl, data:{key:wordText}, type:'POST', dataType:'json', success:function(data){ $(auto).empty(); $.each(data,function(index,term){ varnewDwNode=$("<div>").attr("id",index); newDwNode.html(term).appendTo(auto); //鼠标移入 newDwNode.mouseover(function(){ if(highlightindex!=-1){ varautoNodes=$(auto).children("div"); autoNodes.eq(highlightindex).css("background-color","white").css("font-weight","normal"); } highlightindex=$(this).attr("id"); $(this).css("background-color","#CCCCFF").css("font-weight","800"); }); //鼠标移出 newDwNode.mouseout(function(){ $(this).css("background-color","white").css("font-weight","normal"); }); //鼠标单击 newDwNode.click(function(){ vardataText=$(this).text(); wordInput.val(dataText); $(auto).hide(); }); }); if(data.length>0) { $(auto).show(); } else{ $(auto).hide(); parseInt(highlightindex)=-1; } } }); } else{ $(auto).hide(); parseInt(highlightindex)=-1; } } elseif(keyCode==38||keyCode==40) { if(keyCode==38)//向上 { varautoNodes=$(auto).children("div"); if(highlightindex!=-1){ autoNodes.eq(highlightindex).css("background-color","white").css("font-weight","normal"); highlightindex=highlightindex-1; }else{ parseInt(highlightindex)=autoNodes.length-1; } if(highlightindex==-1){ highlightindex=autoNodes.length-1; } vardataText=autoNodes.eq(highlightindex).css("background-color","#CCCCFF").css("font-weight","800").text(); wordInput.val(dataText); } if(keyCode==40)//向下 { varautoNodes=$(auto).children("div"); if(parseInt(highlightindex)!=-1){ autoNodes.eq(highlightindex).css("background-color","white").css("font-weight","normal"); } highlightindex=parseInt(highlightindex)+1; if(highlightindex==autoNodes.length){ highlightindex=0; } vardataText=autoNodes.eq(highlightindex).css("background-color","#CCCCFF").css("font-weight","800").text(); wordInput.val(dataText); } }elseif(keyCode==13)//回车 { $(auto).hide(); highlightindex=-1; } }) })
最后显示效果如下:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。