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();
}