1、查询
(1)获取cloud solr server
Collapse source
/** * 获取cloud solr server * @author caibosi * @created 2013-12-04 */ public enum SolrServerManager { CONTRACT_LIST("collection1"), REQUEST_AUDIT_LIST("collection2"); String serverName; Logger logger = LoggerFactory.getLogger(SolrServerManager.class); final int zkClientTimeout = 20000; // 心跳20秒 final int zkConnectTimeout = 10000; // 连接时间10秒 //如果采用的cloud server, 这个地址为zookeeper的地址,192.168.4.129:9983 final String zkHost = "ct.solrcloud.host"; CloudSolrServer cloudSolrServer = null; private SolrServerManager(String serverName){ this.serverName = serverName; try { cloudSolrServer = new CloudSolrServer(ConfigUtil.getValueByKey(this.zkHost)); logger.info("### get solr server"); } catch (MalformedURLException e) { logger.error("connect solr server error",e); } cloudSolrServer.setDefaultCollection(this.serverName); cloudSolrServer.setZkClientTimeout(this.zkClientTimeout); cloudSolrServer.setZkConnectTimeout(this.zkConnectTimeout); } public CloudSolrServer getSolrServer(){ return cloudSolrServer; } }
(2)查询实例
Collapse source
/** * 封装solr查询方法 * @author caibosi * @created 2013-12-05 * @param query * @return * @throws SolrServerException */ public SolrDocumentList getSolrDocumentList(SolrQuery query) throws SolrServerException { CloudSolrServer server = SolrServerManager.CONTRACT_LIST.getSolrServer(); QueryResponse response = server.query(query); SolrDocumentList docs = response.getResults(); logger.info("### query:"+ decodeByUTF8(query.toString())); logger.info("### solr response:"+ToStringBuilder.reflectionToString(response)); return docs; } // 将字符串 UTF-8 解码 public String decodeByUTF8(String str) { String target; try { target = URLDecoder.decode(str, "UTF-8"); } catch (Exception e) { logger.error("解码出错!", e); return str; } return target; } /** * contract/list * 根据合同编号 或 合同名称查询 * * 抛出RuntimeException的话 * 会导致事务的回滚 * 影响到页面报错 * * 因而抛出Exception * http://xdy2008.iteye.com/blog/1973725 * * @author caibosi * @created 2013-12-05 * @param keyword * @param page * @return */ public List<Contract> getContractListByKeyword(String keyword,Page page) throws Exception { boolean solrEnabled = ConfigUtil.getValue2BooleanByKey("solr.enabled"); if(solrEnabled == false) throw new Exception("solr service disabled"); long start = System.currentTimeMillis(); logger.info("### query contract list using solr, keyword :"+keyword); SolrQuery query = new SolrQuery(); query.setQuery("contractName:"+keyword+" OR contractNum:"+keyword); SolrDocumentList docs = getSolrDocumentList(query); List<String> contractIds = new ArrayList<String>(); if(CollectionUtils.isEmpty(docs) == false){ for(SolrDocument doc : docs){ String contractId = MapUtils.getString(doc,"id"); if(contractId!=null) contractIds.add(contractId); } } List<Contract> contractList = new ArrayList<Contract>(); if(CollectionUtils.isEmpty(contractIds) == false){ contractList = contractDao.findByIds(contractIds); } long end = System.currentTimeMillis(); logger.info("### get by solr,cost:"+(end-start)); return contractList; }
2、植入业务代码
植入方案:
(1)在原有的代码里头增加solr查询逻辑
/** * @param contractListBean * @param page * @return */ @SolrCached public List<Contract> findContractListByContractListBean(ContractListBean contractListBean, Page page) { if (StringUtil.isNotBlank(contractListBean.getKeyword())) { // 有关键字,先等值匹配,再like long start = System.currentTimeMillis(); /** * spring aop 不支持拦截方法内部再调同一类的其他方法 * 先写在这里测试下solr功能 */ try { List<Contract> list = solrService.getContractListByKeyword(contractListBean.getKeyword(),page); return list; } catch (Exception e) { logger.error("get from solr by keyword error,get from db instead",e); } logger.info("### query key word from db :"+contractListBean.getKeyword()); List<Contract> list = findContractListByContractListBeanInternal(contractListBean, page, true); long end = System.currentTimeMillis(); logger.info("### cost:"+(end-start)); // if (list.size() == 0) { // return findContractListByContractListBeanInternal(contractListBean, page, true); // } else { // return list; // } return list; } else { // 没关键字,只查一次 return findContractListByContractListBeanInternal(contractListBean, page, true); } }
Collapse source
优点:明了,直接,程序员看着直白,知道这里有经过solr的缓存
缺点:在编程的时候植入,业务逻辑与缓存逻辑混杂在一起
(2)利用aop,动态植入
原有的方法仅仅加一个注解
Collapse source
/** * @param contractListBean * @param page * @return */ @SolrCached(SolrCachedMethod.CONTRACT_LIST_KEYWORD) public List<Contract> findContractListByContractListBean(ContractListBean contractListBean, Page page) { if (StringUtil.isNotBlank(contractListBean.getKeyword())) { // 有关键字,先等值匹配,再like long start = System.currentTimeMillis(); List<Contract> list = contractService.findContractListByContractListBeanInternal(contractListBean, page, true); long end = System.currentTimeMillis(); logger.info("### cost:"+(end-start)); // if (list.size() == 0) { // return findContractListByContractListBeanInternal(contractListBean, page, true); // } else { // return list; // } return list; } else { // 没关键字,只查一次 return contractService.findContractListByContractListBeanInternal(contractListBean, page, true); } }
在拦截器里头处理
/** * 拦截service相关方法 * 使用solr进行查询 * @author caibosi * @created 2013-12-05 */ @Aspect @Component public class SolrInterceptor { private final Logger logger = LoggerFactory.getLogger(SolrInterceptor.class); @Resource private SolrService solrService; /** * 拦截带有SolrCached的方法 * @param pjp * @return * @throws Throwable */ @Around("@annotation(solrCached)") public Object getResultBySolr(ProceedingJoinPoint pjp,SolrCached solrCached) throws Throwable { try{ if(solrCached.value() == SolrCachedMethod.CONTRACT_LIST_KEYWORD){ Object[] args = pjp.getArgs(); ContractListBean queryBean = (ContractListBean) args[0]; Page page = (Page) args[1]; if(queryBean!=null && queryBean.getKeyword()!=null){ return solrService.getContractListByKeyword(queryBean.getKeyword(),page); } logger.info("### into findContractListByContractListBean"); } }catch (Exception e){ logger.error("query solr error,query db instead",e); } return pjp.proceed(pjp.getArgs()); } }
Collapse source
优点:与业务逻辑分离开
缺点:程序员可能不知道此处会被拦截,进行solr查询
因而,采用注解的形式告知,但spring aop不支持同一类A方法内部调B方法中B方法的拦截,因而找到最细粒度的方法进行拦截可能优点困难,因而这个方法可能没办法拦截到。
解决方法:1、改造原来方法(移动其他类,但是改动大) 2、采用兼容模式,无法被拦截的,直接在代码里头写,可以被拦截的就在interceptor里头写、
3、同步索引
索引的同步与业务逻辑关联比较大,每个solr缓存的数据的触发条件都不大一样。
(1)增加
根据contract的add_time,对solr进行增量同步
方案1:采用delta import, 在配置文件里配置,同时使用crontab 定时执行该命令的url
方案2:在java里头增量同步,可以在数据库里记录同步,把失败的再重来
public SolrImortResult importContractListByPage(Date start,Date end,Page page){ List<Contract> contractList = contractDao.findByAddTime(start,end,page); if(CollectionUtils.isEmpty(contractList)){ return new SolrImortResult(new Date(),new Date(),0); }else{ logger.info("#### begin to update count:"+contractList.size()); } List<SolrInputDocument> inputDocs = new ArrayList<SolrInputDocument>(); SolrImortResult result = new SolrImortResult(); int updateCount = -1; for(Contract ct : contractList){ SolrInputDocument doc = new SolrInputDocument(); doc.addField("contractId",ct.getContractId()); doc.addField("contractNum",ct.getContractNum()); doc.addField("contractName",ct.getContractName()); inputDocs.add(doc); } CloudSolrServer server = SolrServerManager.CONTRACT_LIST.getSolrServer(); try { server.connect(); server.add(inputDocs); UpdateResponse response = server.commit(); if(response!=null){ logger.info("### update to solr for page:"+page.getPageNo()+";response:"+ToStringBuilder.reflectionToString(response)); updateCount = page.getPageSize(); } } catch (Exception e) { logger.error("update contract list to solr error",e); updateCount = -1; } Date startTime = contractList.get(0).getAddTime(); Date endTime = contractList.get(contractList.size()-1).getAddTime(); result.setStartTime(startTime); result.setEndTime(endTime); result.setUpdateCount(updateCount); return result; }
Collapse source
(2)更新
add方法添加字段,set方法更新
package com.persia.solrj; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrServer; import org.apache.solr.common.SolrInputDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.*; /** * solr服务 * 获取solr server * @author caibosi * @created 2013-12-04 */ @Service public class SolrTestService { private static final Logger logger = LoggerFactory.getLogger(SolrTestService.class); /** * 这种有效果,删除再添加 * 但不知道与map方式的区别 * 如果是add全部字段,其实是没有区别滴 * 如果add部分字段,没有add的字段会丢失掉 * @throws java.io.IOException * @throws org.apache.solr.client.solrj.SolrServerException */ public void updateByAdd() throws IOException, SolrServerException { CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer(); server.connect(); SolrInputDocument doc = new SolrInputDocument(); doc.addField("contractId","74acd736-f84e-11e2-8dea-00222822153f"); doc.addField("contractName","测试update操作23"); doc.addField("contractNum","测试update操作33"); server.add(doc); server.commit(); logger.info("### update success,contractId:{283a8436-481c-11e3-8dea-00222822153f}"); } /** * 下面这种纯属字面上的误会 * 实际上会更新整个文档 * 假设文档原来十几个字段 * 这种方式会替换为3个字段(如下) * http://osdir.com/ml/solr-user.lucene.apache.org/2012-07/msg01065.html * * not setting internal flags for the indexer to treat the SolrInputDocument differently based on if set or add was called * * @throws java.io.IOException * @throws org.apache.solr.client.solrj.SolrServerException */ public void updateBySet() throws IOException, SolrServerException { CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer(); server.connect(); SolrInputDocument doc = new SolrInputDocument(); //如果id也用setfield的话,当文档不存在的时候,也会创建 doc.addField("contractId", "74acd736-f84e-11e2-8dea-00222822153f"); doc.setField("contractNum","234254646664c1set"); doc.setField("contractName", "测试update操作3set"); server.add(doc); server.commit(); logger.info("### update success,contractId:{283a8436-481c-11e3-8dea-00222822153f}"); } /** * 下面这种才是正确的update方式 * { *"add" : { *"doc" : { *"id":"12345", *"foo":{"set":null}, *"bar":{"set":"baz"} *} *} *} * @throws java.io.IOException * @throws org.apache.solr.client.solrj.SolrServerException */ public void updateByMap() throws IOException, SolrServerException { CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer(); server.connect(); SolrInputDocument doc = new SolrInputDocument(); doc.addField("contractId","8b0e8874-5640-11e3-8dea-00222822153f"); Map<String,Object> value = new HashMap<String,Object>(); value.put("set","ww测试partial"); doc.addField("contractName",value); Map<String,Object> value1 = new HashMap<String,Object>(); value1.put("set","wwc987654321"); doc.addField("contractNum",value1); server.add(doc); server.commit(); logger.info("### update success,contractId:{283a8436-481c-11e3-8dea-00222822153f}"); } public void test() throws IOException, SolrServerException { CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer(); server.connect(); // ZkStateReader zkStateReader = server.getZkStateReader(); // ClusterState state = zkStateReader.getClusterState(); // System.out.println("### "+state); // // SolrQuery query = new SolrQuery(); // query.setQuery("contractName:测试选择商户改进"); // try { // QueryResponse response = server.query(query); // SolrDocumentList docs = response.getResults(); // System.out.println("### count:"+docs.getNumFound()); // System.out.println("### cost:"+response.getQTime()); // // for(SolrDocument doc:docs){ // String name = (String) doc.getFieldValue("contractName"); // String id = (String)doc.getFieldValue("contractId"); // System.out.println("### id:"+id+";name:"+name); // } // } catch (SolrServerException e) { // e.printStackTrace(); // } SolrInputDocument doc = new SolrInputDocument(); doc.addField("contractId","g283a8436-481c-11e3-8dea-00222822153f"); doc.addField("contractNum", "g234254646664c1"); doc.addField("contractName", "g测试update操作3"); // Map<String,Object> value = new HashMap<String,Object>(); // value.put("set","测试partial"); // doc.addField("contractName",value); server.add(doc); server.commit(); logger.info("### update success,contractId:{283a8436-481c-11e3-8dea-00222822153f}"); // server.shutdown(); // CloudSolrServer server1 = SolrServerManager.CONTRACT_LIST.getSolrServer(); // System.out.println("### server1:"+server1.toString()); // // CloudSolrServer server2 = SolrServerManager.CONTRACT_LIST.getSolrServer(); // System.out.println("### server2:"+server2.toString()); } public static void main(String[] args){ } }
(3)删除
Collapse source
public void clearContractSearch() throws IOException, SolrServerException { CloudSolrServer server = SolrServerManager.CONTRACT_SEARCH.getSolrServer(); server.connect(); server.deleteByQuery( "*:*" );// delete everything! server.commit(); }