详解spring中使用solr的代码实现
在介绍solr的使用方法之前,我们需要安装solr的服务端集群。基本上就是安装zookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可。由于本人并没有具体操作过如何进行solr集群的搭建。所以关于如何搭建solr集群,读者可以去网上查看其它资料,有很多可以借鉴。这里只介绍搭建完solr集群之后,我们客户端是如何访问solr集群的。
之前介绍过,spring封装nosql和sql数据库的使用,都是通过xxxtemplate。solr也不例外。
我们需要引入solr的jar包
<dependency> <groupid>org.springframework.data</groupid> <artifactid>spring-data-solr</artifactid> <version>1.0.0.release</version> </dependency>
然后引入solr在spring中封装的配置
<bean id="orderinfosolrserver" class="com.xxxx.solrcloudserverfactorybean"> <property name="zkhost" value="${solr.zkhost}"/> <property name="defaultcollection" value="orderinfo"/> <property name="zkclienttimeout" value="6000"/> </bean> <bean id="solrtemplate" class="org.springframework.data.solr.core.solrtemplate" scope="singleton"> <constructor-arg ref="orderinfosolrserver" /> </bean> <bean id="solrservice" class="com.xxxx.solrserviceimpl"> <property name="solroperations" ref="solrtemplate" /> </bean>
然后重写我们的solrserviceimpl就可以了。
但是,本文我们不用spring中封装的xxxtemplate这种格式做讲解。个人在使用spring封装solr的方式的时候遇到了各种各样的问题,可能是能力太low架控不了吧。下面我们主要讲解下如何使用solr的原生api进行访问。
首先:
引入solr的原生代码api的jar包
<dependency> <groupid>org.apache.solr</groupid> <artifactid>solr-solrj</artifactid> <version>4.7.2</version> </dependency>
其次:
在spring的配置文件中配置我们solr的factorybean类,此类是作为我们编写自己业务service类的属性来操作solr。
<bean id="orderinfosolrserver" class="com.xxxx.solrcloudserverfactorybean"> <property name="zkhost" value="${solr.zkhost}"/> <property name="defaultcollection" value="orderinfo"/> <property name="zkclienttimeout" value="6000"/> </bean>
solr.zkhost是我们配置的zookeeper集群
orderinfo是我们存储在solr中的数据结构bean
再次:
编写我们的solrcloudserverfactorybean类,其中使用了spring的factorybean<solrserver>,和initializingbean。关于这两者的含义读者可以参考其他资料,基本意思是spring容器在注册该bean之前,需要进行的一些初始化操作。通过afterpropertiesset方法可以看到我们在使用solr之前做的一些初始化操作。
package com.jd.fms.prism.solr.service; import org.apache.http.client.httpclient; /** * solrj spring integration * * @author bjchenrui */ public class solrcloudserverfactorybean implements factorybean<solrserver>, initializingbean { private cloudsolrserver cloudsolrserver; private string zkhost; private string defaultcollection; private int maxconnections = 1000; private int maxconnectionsperhost = 500; private int zkclienttimeout = 10000; private int zkconnecttimeout = 10000; private lock lock = new reentrantlock(); public solrserver getobject() throws exception { return cloudsolrserver; } public class<solrserver> getobjecttype() { return solrserver.class; } public boolean issingleton() { return true; } public void afterpropertiesset() throws exception { modifiablesolrparams params = new modifiablesolrparams(); params.set(httpclientutil.prop_max_connections, maxconnections); params.set(httpclientutil.prop_max_connections_per_host, maxconnectionsperhost); httpclient client = httpclientutil.createclient(params); lbhttpsolrserver lbserver = new lbhttpsolrserver(client); lock.lock(); try { if(cloudsolrserver == null) { cloudsolrserver = new cloudsolrserver(zkhost, lbserver); } } finally { lock.unlock(); } cloudsolrserver.setdefaultcollection(defaultcollection); cloudsolrserver.setzkclienttimeout(zkclienttimeout); cloudsolrserver.setzkconnecttimeout(zkconnecttimeout); } public void setcloudsolrserver(cloudsolrserver cloudsolrserver) { this.cloudsolrserver = cloudsolrserver; } public void setzkhost(string zkhost) { this.zkhost = zkhost; } public void setdefaultcollection(string defaultcollection) { this.defaultcollection = defaultcollection; } public void setmaxconnections(int maxconnections) { this.maxconnections = maxconnections; } public void setmaxconnectionsperhost(int maxconnectionsperhost) { this.maxconnectionsperhost = maxconnectionsperhost; } public void setzkclienttimeout(int zkclienttimeout) { this.zkclienttimeout = zkclienttimeout; } public void setzkconnecttimeout(int zkconnecttimeout) { this.zkconnecttimeout = zkconnecttimeout; } }
最后:
现在就可以编写我们的service类了,这里就是我们具体如何操作solr的地方。
package com.jd.fms.prism.solr.service.impl; import com.jd.fms.prism.common.utils.dateutil; @service("orderinfosolrservice") public class orderinfonativesolrserviceimpl { private static simpledateformat simpledateformat = new simpledateformat(dateutil.formater11); private static simpledateformat simpledateformat1 = new simpledateformat(dateutil.formater4); @resource(name = "orderinfosolrserver") private solrserver solrserver; /** * 创建索引 * * @param orderinfo */ public void creatindex(orderinfo orderinfo) throws ioexception, solrserverexception { solrserver.addbean(orderinfo); solrserver.commit(); } /** * 查询条件的生成。支持字段的精确查询,模糊查询,范围查询。 * @param orderidfilter * @param queryobj * @param querytimelist * @param sorts * @return * @throws exception */ public solrquery inifilter(string orderidfilter,orderinfo queryobj,list<querytime> querytimelist, sort... sorts) throws exception { solrquery squery = new solrquery(); string queryq = "validtag:1"; squery.setquery(queryq); stringbuilder filter = new stringbuilder(); if(null != orderidfilter){ filter.append(orderidfilter); queryobj.setorderid(null); } //添加过滤条件 field[] fields = queryobj.getclass().getdeclaredfields(); string fieldname = ""; string fieldvalue = ""; for (field field:fields){ if(field.isannotationpresent(org.apache.solr.client.solrj.beans.field.class)){ field.setaccessible(true); fieldname = field.getname(); fieldvalue = string.valueof(field.get(queryobj)); if (null != fieldvalue && !"null".equals(fieldvalue) && !"".equals(fieldvalue) && !"0.0".equals(fieldvalue)){ //如果是会员类型,则添加模糊查询 if(fieldname.equals("memberid") || fieldname.equals("ordertype")){ fieldvalue = "*" + fieldvalue + "*"; } filter.append(fieldname + ":" + fieldvalue).append(" and "); } } } if(querytimelist!=null && querytimelist.size() > 0) { iterator<querytime> iterator = querytimelist.iterator(); while(iterator.hasnext()) { querytime querytime = iterator.next(); string begindate = simpledateformat.format(querytime.getbegintime().gettime()); string enddate = simpledateformat.format(querytime.getendtime().gettime()); filter.append(querytime.getfieldname() + ":" + "[" + begindate + " to " + enddate + "] and "); } } if(filter.length()>0){ filter.delete(filter.length()-5, filter.length()); } squery.addfilterquery(filter.tostring()); if(squery.tostring().equals("")){ squery.setquery("*:*"); } return squery; } /** * 查询代码,可以看到我们可以在solr中做聚合,做排序。而且整个过程都是秒级的。 * @param map * @param queryobj * @param querytimelist * @param page * @param sorts * @return * @throws exception */ public page<orderinfo> query(map map,orderinfo queryobj, list<querytime> querytimelist, pageable page, sort... sorts) throws exception { solrquery squery = inifilter(null,queryobj,querytimelist); //添加分页 if(page != null){ squery.setstart(page.getpagenumber()*page.getpagesize()); squery.setrows(page.getpagesize()); } //添加排序 /*if (null != sorts){ squery.setsort("orderid",solrquery.order.asc); }*/ queryresponse response = null; squery.setgetfieldstatistics("orderprice"); squery.setgetfieldstatistics("dueprice"); squery.setgetfieldstatistics("diffprice"); try { response = solrserver.query(squery); } catch (solrserverexception e) { e.printstacktrace(); } solrdocumentlist list = response.getresults(); map<string, fieldstatsinfo> mapsum = response.getfieldstatsinfo(); string orderpricesum = null; if(mapsum.get("orderprice") != null && !mapsum.get("orderprice").tostring().equals("") ){ orderpricesum = mapsum.get("orderprice").getsum().tostring(); } string duepricesum = null; if(mapsum.get("dueprice") != null && !mapsum.get("dueprice").tostring().equals("") ){ duepricesum = mapsum.get("dueprice").getsum().tostring(); } string diffpricesum = null; if(mapsum.get("diffprice") != null && !mapsum.get("diffprice").tostring().equals("") ){ diffpricesum = mapsum.get("diffprice").getsum().tostring(); } list<orderinfo> list1 = new arraylist<orderinfo>(); documentobjectbinder binder = new documentobjectbinder(); iterator iterator = list.iterator(); while(iterator.hasnext()){ orderinfo orderinfo = binder.getbean(orderinfo.class, (solrdocument) iterator.next()); list1.add(orderinfo); } map.put("orderpricesum", orderpricesum); map.put("duepricesum", duepricesum); map.put("diffpricesum", diffpricesum); page<orderinfo> pagelist = new pageimpl<orderinfo>(list1,page,list.getnumfound()); return pagelist; } /** * 我们可以按照key值进行主键查询。 * @param id * @return * @throws exception */ public list<orderinfo> querybyorderid(string id) throws exception { solrquery squery = new solrquery(); string filter = "orderid" + ":" + id; squery.setquery(filter); queryresponse response = null; try { response = solrserver.query(squery); } catch (solrserverexception e) { e.printstacktrace(); } solrdocumentlist list = response.getresults(); list<orderinfo> list1 = new arraylist<orderinfo>(); documentobjectbinder binder = new documentobjectbinder(); iterator iterator = list.iterator(); while(iterator.hasnext()){ orderinfo orderinfo = binder.getbean(orderinfo.class, (solrdocument) iterator.next()); list1.add(orderinfo); } return list1; } public void deleteall(orderinfo orderinfo) throws ioexception, solrserverexception { string squery = "*:*"; solrserver.deletebyquery(squery); } public void deletebyid(string id) { } public void createindexbatch(list<orderinfo> orderinfolist) throws ioexception, solrserverexception { solrserver.addbeans(orderinfolist); solrserver.commit(); } public void deletebysolrquery(string solrquery) throws ioexception, solrserverexception { solrserver.deletebyquery(solrquery); solrserver.commit(); } public solrserver getsolrserver() { return solrserver; } public void setsolrserver(solrserver solrserver) { this.solrserver = solrserver; } }
当然solr的api不止于此,我们此处只是罗列了一些比较常用的使用方法。对于solr的查询,有以下几点需要注意。
1. solr生成查询语句的时候,是有q查询和fq查询之分的。哪些查询条件放在q查询里,哪些查询条件放在fq查询里,对查询的效率还是有较大的影响的。一般固定不变的查询条件放在q查询里,经常变化的查询条件放在fq里。上述代码中validtag:1就放在了q查询里,循环里的字符串filter则放在了我们的fq查询里。
2. solr查询时,要了解solr服务器集群的配置文件中使用的是什么样的分词器,不同分词器对模糊查询的结果是有影响的。比如常见的ik分词器和标准分词器(如果我们有一个字段的名称为:我是中国人,ik分词器在solr里的存储就成为了“我”,“中国人”,“中国”,“国人”。而标准分词器则会存储为“我”,“是”,“中”,“国”,“人”。如果我们使用全称查询,即查询:我是中国人,两者是没有问题的。但是使用模糊查询,比如查询“*我是*”,则两个分词器分词都查不出来结果,而如果我们的查询条件是“*中国人*”则在ik分词器下可以查询出结果,在标准分词器下查不出结果。)
3. 使用solr的过程中,需要定时执行solr的optimize函数来清理磁盘碎片,否则会影响读写效率。对于optimize的参数建议为(false,false,5)。
4. 写solr数据的时候,尽量使用createindexbatch方法,这是因为solr在执行写入的时候,写入一条数据和写入多条数据都需要全量建索引,其执行时间是差不多的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。