Solr封装的Java客户端工具类




import com.finest.ms.address.util.AddrMatchConstant;
import com.finest.ms.address.util.ConfigUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.*;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;


public class SolrClient {


private Logger log = LoggerFactory.getLogger(SolrClient.class);

/** ID唯一标识 */
public static final String ID = "ID";

/**
* solr服务哈希表,key为(格式为“http://ip:端口/core”)路径名称,value为HttpSolrServer对象
*/
private HashMap<String, SolrServer> solrServers = new HashMap<String, SolrServer>();

/** UTF-8编码 */
public static final String UTF8_CHARSET_NAME = "UTF-8";

/** 拆分词组URL,用于拆分词组 */
public static final String URL_SPLIT_WORDS = "splitwords";

/** 代码词组的URL,用于根据代码得到标准词组 */
public static final String URL_CODE_WORDS = "codewords";

/** 字段通用名称--冒号 */
public static final String FIELD_COLON = ":";

/** 字段通用名称--双引号 */
public static final String FIELD_DOUBLE_QUOTATION_MARKS = "\"";

/** 注解字段常量--otherFields */
public static final String ANNOTATION_OTHERFIELDS = "otherFields";

/** solr的URL地址,目前只有两种,标准地址的solrurl地址和业务数据的solrurl地址,在config.properties配置 */
private String solrUrl;

private HttpClient client;

/** solr的循环次数集合,key为solr的url地址, value为循环次数 */
private Map<String, Integer> solrLoopNumMap = new HashMap<String, Integer>();

/** solr网络重连次数,默认为5 */
private int pingLoopNum;

/** solr网络重连睡眠时间,以毫秒为单位,默认为1000毫秒 */
private long pingSleepTime;

// /** debug模式下 - 统计solr访问次数 ,多线程下此值会存在不准确情况 */public static long total = 0;

/**
* 构造函数
*
*
@param solrUrl solr的URL,目前只有两种,标准地址的solrurl地址和业务数据的solrurl地址,在config.properties配置
*/
public SolrClient(String solrUrl) {
this.solrUrl = solrUrl;
}

public SolrClient(String solrUrl, HttpClient client){
this.solrUrl = solrUrl;
this.client = client;
}

/**
* 根据core名称得到SolrServer对象,如果不存在,就创建
*
*
@param core solr的core名称
*
@return
*/
public SolrServer getSolrServer(String core) throws Exception {
// 对solrUrl和core为空判断,当为空时,直接返回SolrHttpException异常
if (StringUtils.isEmpty(solrUrl) || StringUtils.isEmpty(core)) {
String error = "solr工具类-根据core名称得到SolrServer对象方法出现异常!solrUrl或core名称为空!";
log.warn(error);
throw new SolrHttpException(error); // 异常自定义的异常,可以很清楚知道该异常是http连接出现问题导致
}
SolrServer solrServer = null;
// 根据格式为“http://ip:端口/core”组成的key,存放至solr服务哈希表。
String solrCoreUrl = solrUrl + "/" + core;
boolean isNeedAuth = "true".equalsIgnoreCase(ConfigUtil.getPropsValueByKey("ac.solr.isNeedAuth"));
if (solrServers.containsKey(solrCoreUrl)&&!isNeedAuth) {
solrServer = solrServers.get(solrCoreUrl);
} else {
// 判断是否需要验证用户名密码
if (isNeedAuth) {
String solrUser = ConfigUtil.getPropsValueByKey("ac.solr.username");
String solrPass = ConfigUtil.getPropsValueByKey("ac.solr.password");
ModifiableSolrParams params = new ModifiableSolrParams();
params.set(HttpClientUtil.PROP_MAX_CONNECTIONS, 128);
params.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, 32);
params.set(HttpClientUtil.PROP_FOLLOW_REDIRECTS, false);
params.set(HttpClientUtil.PROP_BASIC_AUTH_USER, solrUser);
params.set(HttpClientUtil.PROP_BASIC_AUTH_PASS, solrPass);
HttpClient httpClient = HttpClientUtil.createClient(params);
solrServer = new HttpSolrExServer(solrCoreUrl, httpClient);
} else {
solrServer = new HttpSolrExServer(solrCoreUrl);
}
// solrServer的ping调用
pingSolrServer(solrServer);
//solrServers.put(solrCoreUrl, solrServer);
}
return solrServer;
}

/**
* 获取得到SolrDocument的数据列表
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@return
*/
public List<SolrDocument> getDocs(String criteria, String coreName, int rows, int start, String sortParams)
throws Exception {
return getDocs(criteria, coreName, rows, start, sortParams, null, null, null);
}

/**
* 获取得到SolrDocument的数据列表<br>
* 使用场景:<br>
* 1.当没有过滤显示字段数组,直接返回所有solr原始字段的SolrDocument数据。<br>
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,返回过滤字段后的solr原始字段的SolrDocument数据。<br>
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,长度顺序一致,返回过滤字段后的以别名为key的SolrDocument数据。<br>
* 4.当有过滤显示字段数组并且有过滤显示字段别名数组,顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,返回过滤字段后,有别名的以别名为key、没有别名的以字段名为key的SolrDocument数据。<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@param coreUrls core Url数组,用于跨core查询
*
@return
*/
public List<SolrDocument> getDocs(String criteria, String coreName, int rows, int start, String sortParams,
String[] fields, String[] aliasFields, String[] coreUrls) throws Exception {
List<SolrDocument> docs = new ArrayList<SolrDocument>();
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, fields, aliasFields,
null, null, coreUrls);

if (response.getResults() != null) {
docs.addAll(response.getResults());
}
}
return docs;
}

/**
* 得到分组统计后的文档列表<br>
* 与数据库group by分组返回结果集一致,支持多个字段的分组<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param facetParams 分组参数字符串,格式以逗号隔开,如name,age
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与facetParams分组参数字符串拆分后得到数组的顺序一一对应,长度可不相同,<br>
* 可短于facetParams分组参数字符串拆分后得到数组,也可长于facetParams分组参数字符串拆分后得到数组。<br>
* 当短时,那些没有设置别名的,将取回facetParams分组对应的字段名;当长时,除了全部用别名之外,还将对统计别名的设置。
*
@return
* @throws Exception
*/
public List<SolrDocument> getFacetDocs(String criteria, String coreName, String facetParams, String[] aliasFields)
throws Exception {
// 当查询条件,或者core名称,或者分组参数为空时,直接返回空列表
if (StringUtils.isEmpty(criteria) || StringUtils.isEmpty(coreName) || StringUtils.isEmpty(facetParams)) {
return Collections.emptyList();
}

// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, 0, 0, null, null, aliasFields, facetParams, null,
null);

List<PivotField> pivotFieldList = null;
// 根据分组结果,设置返回值
NamedList<List<PivotField>> facetPivot = response.getFacetPivot();
if (facetPivot != null) {
pivotFieldList = facetPivot.get(facetParams);
}
// 调用得到分组的solr文档对象列表方法,返回结果
return getFacetSolrDocumentList(pivotFieldList, aliasFields);
}

/**
* 获取得到对象化的数据列表
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
Class<T> clazz) throws Exception {
return getBeans(criteria, coreName, rows, start, sortParams, clazz, null);
}

/**
* 获取得到对象化的数据列表<br>
* 使用场景:<br>
* 1.当没有过滤显示字段数组,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,过滤字段后,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,不需要转换对象的Class注解有@Field字段:<br>
* 1)长度顺序一致,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
* 2)顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值,找不到相同的属性名则不做赋值处理。<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名,并且返回对象的属性名与别名一致。
*
@param coreUrls core Url数组,用于跨core查询
*
@return
*/
public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
Class<T> clazz, String[] fields, String[] aliasFields, String[] coreUrls) throws Exception {
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, fields, aliasFields,
null, null, coreUrls);

if (response.getResults() != null) {
// 如果设置有别名,根据转换对象的Class,通过反射设置得到对应的转换对象列表,并返回
// 如果没有设置别名,返回solr原始getBeans方法得到的对象列表值。该getBeans方法是通过注解的方式设置映射关系
if (fields != null && fields.length > 0 && aliasFields != null && aliasFields.length > 0) {
return this.getBeansByDocsAndClazz(response.getResults(), clazz);
} else {
try {
return response.getBeans(clazz);
} catch (Exception e) {
String error = "获取得到对象化的数据列表方法,在执行QueryResponse的getBeans时出现异常!";
log.error(error, e);
throw new SolrBeanBindingException(error);
}
}
}
}
return Collections.emptyList();
}

/**
* 获取得到对象化的数据列表<br>
* 使用场景:<br>
* 1.当fieldsMap字段Map为空,以转换对象的Class注解有@Field关联字段值进行映射,返回对象数据列表。<br>
* 2.当fieldsMap字段Map不为空情况下,不需要转换对象的Class注解有@Field字段:<br>
* 1)所有键与值都有值,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
* 2)转换对象的Class注解有@Field("otherFields")的Map属性,所有找不到别名名称相同的转换对象Class的属性名,把那些没有设置到属性里的值,全部加入到注解有@Field(
* "otherFields")的Map对象中<br>
* 3)如果没有找到注解有@Field("otherFields")的Map,所有找不到别名名称相同的转换对象Class的属性名,把那些没有设置到属性里的值全部丢弃掉<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@param fieldsMap 字段Map,key为字段名,value为字段别名,别名可以为空,取回key对应的字段名为别名
*
@return
*/
public <T extends Object> List<T> getBeans(String criteria, String coreName, int rows, int start, String sortParams,
Class<T> clazz, Map<String, String> fieldsMap) throws Exception {
// 过滤显示字段数组
String[] fields = null;
// 过滤显示字段别名数组
String[] aliasFields = null;

// 通过字段map,得到字段名数组和别名数组,并设置
if (fieldsMap != null) {
// 字段名拼接字符串,字段名之间以逗号隔开
StringBuilder fieldsSb = new StringBuilder();
// 字段别名拼接字符串,字段别名之间以逗号隔开
StringBuilder aliasFieldsSb = new StringBuilder();

for (Entry<String, String> entry : fieldsMap.entrySet()) {
fieldsSb.append(entry.getKey()).append(",");
if (StringUtils.isNotEmpty(entry.getValue())) {
aliasFieldsSb.append(entry.getValue().trim()).append(",");
} else {
aliasFieldsSb.append(",");
}
}
if (fieldsSb.length() > 0) {
fieldsSb.delete(fieldsSb.length() - 1, fieldsSb.length());
}
if (aliasFieldsSb.length() > 0) {
aliasFieldsSb.delete(aliasFieldsSb.length() - 1, aliasFieldsSb.length());
}

fields = fieldsSb.toString().split(" *, *");
aliasFields = aliasFieldsSb.toString().split(" *, *");
}

return getBeans(criteria, coreName, rows, start, sortParams, clazz, fields, aliasFields, null);
}

/**
* 获取得到对象化的数据对象<br>
* 使用场景:<br>
* 1.当没有过滤显示字段数组,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据。<br>
* 2.当有过滤显示字段数组并且没有过滤显示字段别名数组,过滤字段后,转换对象的Class注解有@Field关联字段值进行映射,返回对象数据。<br>
* 3.当有过滤显示字段数组并且有过滤显示字段别名数组,不需要转换对象的Class注解有@Field字段:<br>
* 1)长度顺序一致,即都设置有别名情况下,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值。<br>
* 2)顺序一致,但长度不一致,即有部分设置别名,有部分没有设置别名,设置对应的别名,并且能找到与别名名称相同的转换对象Class的属性名,通过反射直接赋值,找不到相同的属性名则不做赋值处理。<br>
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名,并且返回对象的属性名与别名一致。
*
@param coreUrls core Url数组,用于跨core查询
*
@return
*/
public <T extends Object> T getBean(String criteria, String coreName, String sortParams, Class<T> clazz,
String[] fields, String[] aliasFields, String[] coreUrls) throws Exception {
List<T> list = getBeans(criteria, coreName, 1, 0, sortParams, clazz, fields, aliasFields, coreUrls);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}

/**
* 获取得到对象化的数据对象
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> T getBean(String criteria, String coreName, String sortParams, Class<T> clazz)
throws Exception {
return getBean(criteria, coreName, sortParams, clazz, null, null, null);
}

/**
* 根据ID获取得到对象化的数据对象
*
*
@param id ID唯一标识
*
@param coreName core的名称
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> T getBeanById(String id, String coreName, Class<T> clazz) throws Exception {
StringBuilder querySb = new StringBuilder();
querySb.append("ID:").append(id);
return getBean(querySb.toString(), coreName, null, clazz);
}

/**
* 获取得到对象化的数据Solr文档
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@return
*/
public SolrDocument getDoc(String criteria, String coreName, String[] fields, String[] aliasFields)
throws Exception {
List<SolrDocument> list = getDocs(criteria, coreName, 1, 0, null, fields, aliasFields, null);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}

/**
* 获取得到对象化的数据Solr文档
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@return
*/
public SolrDocument getDoc(String criteria, String coreName) throws Exception {
return getDoc(criteria, coreName, null, null);
}

/**
* 获取总条数
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@return
*/
public long getNumberFound(String criteria, String coreName) throws Exception {
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, 0, 0, null, null, null, null, null, null);
return response.getResults().getNumFound();
}
return 0;
}

/**
* 添加文档
*
*
@param document solr的输入文档对象
*
@param coreName core的名称
*/
public void addDoc(SolrInputDocument document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, false);
}

/**
* 添加文档并马上提交
*
*
@param document solr的输入文档对象
*
@param coreName core的名称
*/
public void addDocByHardCommit(SolrInputDocument document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, true);
}

/**
* 添加Bean
*
*
@param document Bean对象
*
@param coreName core的名称
*/
public void addBean(Object document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, false);
}

/**
* 添加Bean并马上提交
*
*
@param document Bean对象
*
@param coreName core的名称
*/
public void addBeanByHardCommit(Object document, String coreName) throws Exception {
addDocumentIntoSolr(document, coreName, true);
}

/**
* 添加文档至solr
*
*
@param object object对象,可能是SolrInputDocument、也可能是bean对象
*
@param coreName core名称
*
@param isHardCommit 是否马上提交
*
@throws Exception
*/
private void addDocumentIntoSolr(Object object, String coreName, boolean isHardCommit) throws Exception {
if (object != null && StringUtils.isNotEmpty(coreName)) {
SolrServer solrServer = getSolrServer(coreName);
try {
if (object instanceof SolrInputDocument) {
solrServer.add((SolrInputDocument) object);
} else {
solrServer.addBean(object);
}
if (isHardCommit) {
solrServer.commit();
}
} catch (SolrServerException | IOException e) {
String error = "solr工具类-添加文档至solr方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}
}

/**
* 添加bean列表
*
*
@param documents
*
@param coreName
*/
@SuppressWarnings("rawtypes")
public void addBeans(List documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, false);
}

/**
* 添加bean列表并马上提交
*
*
@param documents
*
@param coreName
*/
@SuppressWarnings("rawtypes")
public void addBeansByHardCommit(List documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, true);
}

/**
* 添加文档列表
*
*
@param documents
*
@param coreName
*/
public void addDocs(List<SolrInputDocument> documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, false);
}

/**
* 添加文档列表并马上提交
*
*
@param documents
*
@param coreName
*/
public void addDocsByHardCommit(List<SolrInputDocument> documents, String coreName) throws Exception {
addDocumentsIntoSolr(documents, coreName, true);
}

/**
* 添加文档(对象)列表到solr
*
*
@param documents
*
@param coreName
*
@param isHardCommit
*
@throws Exception
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addDocumentsIntoSolr(List documents, String coreName, boolean isHardCommit) throws Exception {
if (documents != null && documents.size() > 0 && StringUtils.isNotEmpty(coreName)) {
try {
SolrServer solrServer = getSolrServer(coreName);
if (documents.get(0) instanceof SolrInputDocument) {
solrServer.add(documents);
} else {
solrServer.addBeans(documents);
}
if (isHardCommit) {
solrServer.commit();
}
} catch (SolrServerException | IOException e) {
String error = "solr工具类-添加文档至solr方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}
}

/**
* 根据ID更新solr字段值
*
*
@param inputDoc
*
@param id
*
@param fields
*
@param values
*
@return
*/
public SolrInputDocument updateSolrFieldsWithId(SolrInputDocument inputDoc, Object id, List<String> fields,
Object[] values) {
inputDoc = this.updateSolrFields(inputDoc, fields, values);
inputDoc.setField(ID, id);
return inputDoc;
}

/**
* 更新solr字段值
*
*
@param inputDoc
*
@param fields
*
@param values
*
@return
*/
public SolrInputDocument updateSolrFields(SolrInputDocument inputDoc, List<String> fields, Object[] values) {
if (fields != null && values != null && fields.size() <= values.length) {
for (int i = 0; i < fields.size(); i++) {
Map<String, Object> updater = new HashMap<String, Object>();
updater.put("set", values[i]);
inputDoc.setField(fields.get(i), updater);
}
}

return inputDoc;
}

/**
* 更新solr字段值
*
*
@param inputDoc
*
@param fields
*
@param values
*
@return
*/
public SolrInputDocument updateSolrFields(SolrInputDocument inputDoc, List<String> fields, SolrDocument values) {
if (fields != null && values != null) {
for (int i = 0; i < fields.size(); i++) {
Map<String, Object> updater = new HashMap<String, Object>();
updater.put("set", values.getFieldValue(fields.get(i)));
inputDoc.setField(fields.get(i), updater);
}
}

return inputDoc;
}

/**
* 根据查询条件删除solr数据
*
*
@param query 查询条件
*
@param coreName core名称
*/
public void deleteByQuery(String query, String coreName) throws Exception {
try {
SolrServer solrServer = getSolrServer(coreName);
solrServer.deleteByQuery(query);
solrServer.commit();
} catch (SolrServerException | IOException e) {
String error = "solr工具类-根据查询条件删除solr数据方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}

/**
*
*
@param coreName
*
@param termsField
*
@param prefix
*
@param fetchRows
*
@return
*/
public List<TermsResponse.Term> suggest(String coreName, String termsField, String prefix, int fetchRows)
throws Exception {
SolrQuery solrQuery = new SolrQuery();
solrQuery.addTermsField(termsField);
solrQuery.setTerms(true);
solrQuery.setTermsLimit(fetchRows);
solrQuery.setTermsSortString("index");
solrQuery.setTermsPrefix(prefix);
solrQuery.setRequestHandler("/terms");
QueryResponse queryResponse = getSolrServer(coreName).query(solrQuery);
return queryResponse.getTermsResponse().getTerms(termsField);
}

/**
* 拼写
*
*
@param coreName
*
@param queryString
*
@param fetchRows
*
@param fetchStartAt
*
@param sortParams
*
@param params
*
@return
*/
public <T extends Object> SolrDocumentList spell(String coreName, String queryString, int fetchRows,
int fetchStartAt, String sortParams, String params) throws Exception {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(queryString, coreName, fetchRows, fetchStartAt, sortParams, null,
null, null, "spell", null);

SolrDocumentList results = response.getResults();
if (null == results && null != response.getGroupResponse()) {
results = new SolrDocumentList();
for (GroupCommand groupCommand : response.getGroupResponse().getValues()) {
for (Group group : groupCommand.getValues()) {
results.addAll(group.getResult());
}
}
}
return results;
}

/**
* 拆分词组,返回匹配到的词组
*
*
@param criteria 查询条件
*
@param coreName core名称
*
@param requestHandler 请求句柄
*
@param rows 条数,可用于分页
*
@param start 开始查询位置,可用于分页
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@return
*/
public <T extends Object> List<T> splitwWordsBeans(String criteria, String coreName, String requestHandler,
int rows, int start, String sortParams, Class<T> clazz) throws Exception {
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)
&& StringUtils.isNotEmpty(requestHandler)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, null, null, null,
requestHandler, null);

if (response.getResults() != null) {
try {
return response.getBeans(clazz);
} catch (Exception e) {
String error = "拆分词组返回匹配到的词组方法,在执行QueryResponse的getBeans操作时出现异常!";
log.error(error, e);
throw new SolrBeanBindingException(error);
}
}
}
return Collections.emptyList();
}

/**
* 拆分词组,返回匹配到SolrDocument的词组
*
*
@param criteria 查询条件
*
@param coreName core名称
*
@param requestHandler 请求句柄
*
@param rows 条数,可用于分页
*
@param start 开始查询位置,可用于分页
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@return
*/
public List<SolrDocument> splitwWordsDocs(String criteria, String coreName, String requestHandler, int rows,
int start, String sortParams) throws Exception {
List<SolrDocument> docs = new ArrayList<SolrDocument>();
if (StringUtils.isNotEmpty(criteria) && StringUtils.isNotEmpty(coreName)
&& StringUtils.isNotEmpty(requestHandler)) {
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(criteria, coreName, rows, start, sortParams, null, null, null,
requestHandler, null);

if (response.getResults() != null) {
docs.addAll(response.getResults());
}
}
return docs;
}

/**
* 反向匹配,返回转换后的结果对象
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param returnDistanceName 返回时距离的名称,与tClass距离属性名称要一致,如不填,tClass要有distance距离属性,不然将无法得到距离值
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> T reverseMatchBean(String coreName, double lon, double lat, double radius, Class<T> clazz,
String returnDistanceName) throws Exception {
List<T> list = this.reverseMatchBeans(coreName, lon, lat, radius, 1, 0, clazz, returnDistanceName);
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}

/**
* 反向匹配,返回转换后的结果对象列表
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param start 开始位置
*
@param rows 查询的行数
*
@param returnDistanceName 返回时距离的名称,与tClass距离属性名称要一致,如不填,tClass要有distance距离属性,不然将无法得到距离值
*
@param clazz 要转换对象的Class
*
@return
*/
public <T extends Object> List<T> reverseMatchBeans(String coreName, double lon, double lat, double radius,
int rows, int start, Class<T> clazz, String returnDistanceName) throws Exception {
if (StringUtils.isNotEmpty(coreName)) {
// 得到反向solr查询对象
SolrQuery solrQuery = getReverseSolrQuery(coreName, lon, lat, radius, rows, start, returnDistanceName);
// 得到solr的查询返回结果对象
if(!coreName.equals(AddrMatchConstant.CORE_NAME_STANDARDADDRESS)) {
coreName = AddrMatchConstant.CORE_NAME_STANDARDADDRESS;
}
QueryResponse response = getQueryResponse(coreName, solrQuery);

if (response.getResults() != null) {
try {
return response.getBeans(clazz);
} catch (Exception e) {
String error = "反向匹配返回转换后的结果对象列表方法,在执行QueryResponse的getBeans时出现异常!";
log.error(error, e);
throw new SolrBeanBindingException(error);
}
}
}
return Collections.emptyList();
}

/**
* 反向匹配,返回SolrDocument列表
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param start 开始位置
*
@param rows 查询的行数
*
@param returnDistanceName 返回时距离的名称
*
@return
*/
public List<SolrDocument> reverseMatchDocs(String coreName, double lon, double lat, double radius, int rows,
int start, String returnDistanceName) throws Exception {
List<SolrDocument> docs = new ArrayList<SolrDocument>();
if (StringUtils.isNotEmpty(coreName)) {
// 得到反向solr查询对象
SolrQuery solrQuery = getReverseSolrQuery(coreName, lon, lat, radius, rows, start, returnDistanceName);
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(coreName, solrQuery);

if (response.getResults() != null) {
docs.addAll(response.getResults());
}
}
return docs;
}

/**
* 得到反向solr查询对象
*
*
@param coreName core名称
*
@param lon 经度
*
@param lat 纬度
*
@param radius 搜索半径,单位km
*
@param start 开始位置
*
@param rows 查询的行数
*
@param returnDistanceName 返回时距离的名称
*
@return
*/
private SolrQuery getReverseSolrQuery(String coreName, double lon, double lat, double radius, int rows, int start,
String returnDistanceName) {
SolrQuery solrQuery = new SolrQuery();
if(coreName.equals(AddrMatchConstant.CORE_NAME_INFOPOINT)) {
System.out.println("信息点请求");
solrQuery.set("q", "YSLXDM:12");
}else if(coreName.equals(AddrMatchConstant.CORE_NAME_AEINCLUSIONRELATION)) {
System.out.println("街路巷查询");
solrQuery.set("q", "YSLXDM:6");
}else {
solrQuery.set("q", "*:*");
}
solrQuery.set("fq", "{!geofilt}"); // 距离过滤函数
solrQuery.set("pt", lat + "," + lon); // 当前坐标
solrQuery.set("sfield", "GEO"); // 经纬度字段,默认约定geo名称
solrQuery.set("d", "" + radius); // 搜索半径,单位km
solrQuery.set("sort", "geodist() asc"); // 根据距离排序:由近到远
solrQuery.set("start", start); // 记录开始位置
solrQuery.set("rows", rows); // 查询的行数
if (StringUtils.isEmpty(returnDistanceName)) {
returnDistanceName = "distance";
}
solrQuery.set("fl", "*," + returnDistanceName + ":geodist()"); // 查询的结果中添加距离
return solrQuery;
}


/**
* 得到街路巷查询对象
*
*
@return
*/
private SolrQuery getStreetSolrQuery(String coreName, int rows, int start,
String ssqx) {
String q = "(SJDM:"+ssqx+" OR SJMC:"+ssqx+" OR SJQHDM:"+ssqx+" OR SJQHMC:"+ssqx+" OR QXDM:"+ssqx+" OR QXMC:"+ssqx+") AND " +
"(YSLXMC:"+coreName+" OR JLXDM:"+coreName+")";

SolrQuery solrQuery = new SolrQuery();
solrQuery.set("q", q);
// solrQuery.set("sort", "geodist() asc"); // 根据距离排序:由近到远
solrQuery.set("start", start); // 记录开始位置
solrQuery.set("rows", rows); // 查询的行数

return solrQuery;
}

/**
* solr导入数据操作,返回solr的查询返回结果对象<br>
* 根据参数不同,该方法不一定就是进行solr同步数据。 比如获取solr导入数据状态<br>
*
*
@param coreName core的名称
*
@param paramsDataMap 参数数据Map
*
@return
*/
private QueryResponse importData(String coreName, Map<String, String> paramsDataMap) throws Exception {
// 得到反向solr查询对象
SolrQuery solrQuery = new SolrQuery();
solrQuery.setRequestHandler("/dataimport");
if (paramsDataMap != null) { // 设置相关参数
for (Map.Entry<String, String> entry : paramsDataMap.entrySet()) {
solrQuery.setParam(entry.getKey(), entry.getValue());
}
}
// 得到solr的查询返回结果对象
QueryResponse response = getQueryResponse(coreName, solrQuery);
return response;
}

/**
* 压缩
*
*
@param content
*
@param name
*
@return
* @throws IOException
*/
public static byte[] zip(String content, String name) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = null;
DataOutputStream dataOutputStream = null;
try {
zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
zipOutputStream.putNextEntry(new ZipEntry(name));
dataOutputStream = new DataOutputStream(zipOutputStream);
dataOutputStream.write(content.getBytes(UTF8_CHARSET_NAME));
dataOutputStream.flush();
zipOutputStream.flush();
zipOutputStream.finish();
} finally {
dataOutputStream.close();
zipOutputStream.close();
}
return byteArrayOutputStream.toByteArray();
}

/**
* 解压
*
*
@param value
*
@return
*/
public String unZip(byte[] value) throws Exception {
if (null == value) {
return null;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(value);
ZipInputStream zipInputStream = null;
String sourceText = null;
try {
zipInputStream = new ZipInputStream(byteArrayInputStream);
zipInputStream.getNextEntry();
final int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int n;
while ((n = zipInputStream.read(buffer, 0, bufferSize)) != -1) {
byteArrayOutputStream.write(buffer, 0, n);
}
sourceText = byteArrayOutputStream.toString("UTF-8");
} catch (IOException e) {
String error = "solr工具类-解压方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
} finally {
try {
if (null != zipInputStream) {
zipInputStream.close();
zipInputStream = null;
}
if (null != byteArrayInputStream) {
byteArrayInputStream.close();
byteArrayInputStream = null;
}
if (null != byteArrayOutputStream) {
byteArrayOutputStream.close();
byteArrayOutputStream = null;
}
} catch (IOException e) {
String error = "solr工具类-解压方法出现异常!";
log.error(error, e);
throw new SolrServerIoException(error);
}
}
return sourceText;
}

/**
* 设置排序参数,并返回SolrQuery对象
*
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param solrQuery solr查询对象
*
@return
*/
private SolrQuery setSortParams(String sortParams, SolrQuery solrQuery) {
SolrQuery returnSolrQuery = solrQuery;
if (StringUtils.isNotEmpty(sortParams)) {
String[] sortFields = sortParams.trim().split(" *, *");
for (String sortField : sortFields) {
String[] sortFieldAndOrder = sortField.split(" +");
if (2 == sortFieldAndOrder.length) {
returnSolrQuery.addSort(sortFieldAndOrder[0],
SolrQuery.ORDER.valueOf(sortFieldAndOrder[1].toLowerCase()));
} else if (1 == sortFieldAndOrder.length) {
returnSolrQuery.addSort(sortFieldAndOrder[0], SolrQuery.ORDER.asc);
}
}
}
return returnSolrQuery;
}

/**
* 得到设置过虑字段参数,如果有别名,则设置别名
*
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*/
private String[] getAliasFieldsArray(String[] fields, String[] aliasFields) {
if (fields == null || fields.length <= 0) {
return null;
}

// 判断字段别名数组不为空且长度与字段数组相等
if (aliasFields != null && aliasFields.length > 0) {
StringBuilder fieldSb = new StringBuilder();
// 别名的格式是: 别名:真实名
for (int i = 0; i < fields.length; i++) {
fieldSb.delete(0, fieldSb.length());
if (aliasFields.length - 1 >= i) {
if (StringUtils.isEmpty(aliasFields[i])) {
aliasFields[i] = fields[i];
}
fieldSb.append(aliasFields[i]).append(":").append(fields[i]);
} else {
fieldSb.append(fields[i]).append(":").append(fields[i]);
}
fields[i] = fieldSb.toString();
}
}
return fields;
}

/**
* 设置分组参数,并返回SolrQuery对象<br>
* 主要用于数据统计,对应数据库中的group by
*
*
@param facetParams 分组参数字符串,格式以逗号隔开,如name,age
*
@param solrQuery solr查询类
*/
private SolrQuery setFacetParams(String facetParams, SolrQuery solrQuery) {
SolrQuery returnSolrQuery = solrQuery;
if (StringUtils.isNotEmpty(facetParams)) {
returnSolrQuery.setFacet(true);
returnSolrQuery.addFacetPivotField(facetParams);
returnSolrQuery.setFacetMissing(false); // 不统计null的值
}
return returnSolrQuery;
}

/**
* 根据solr文档列表和转换对象的Class,通过反射设置得到对应的转换对象列表<br>
* 1.主要用于当已经设置过滤返回对象和别名后得到的docs文档列表,根据转换对象的Class,设置与文档key一致的属性值<br>
* 2.当转换对象的Class注解有@Field("otherFields"),把那些没有设置到属性里的值,全部加入到注解有@Field("otherFields")的Map对象中<br>
* 3.如果没有找到注解有@Field("otherFields")的Map,那些没有设置到属性里的值全部丢弃掉<br>
*
*
@param docs solr文档对象列表
*
@param clazz 要转换对象的Class
*
@return
*/
private <T extends Object> List<T> getBeansByDocsAndClazz(List<SolrDocument> docs, Class<T> clazz) {
// solr文档对象列表为空,直接返回空列表
if (docs == null || docs.size() <= 0) {
return Collections.emptyList();
}

// 得到所有属性列表
Field[] declaredFields = clazz.getDeclaredFields();
// 对象实例
T obj = null;
// 其他字段值map
Map<String, String> otherFieldValueMap = null;
// solr字段Object对象变量
Object fieldValueObj = null;
// solr字段字符串变量
String fieldValueStr = null;
// 返回列表
List<T> rtnList = new ArrayList<T>();
// 是否有相同的字段名称
boolean hasSameFieldName = false;
// 日期格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

for (SolrDocument doc : docs) {
// fieldValueMap = doc.getFieldValueMap();
try {
hasSameFieldName = false;
otherFieldValueMap = new HashMap<String, String>();
// 创建实例
obj = clazz.newInstance();
// 循环反射得到的字段列表,比较字段名是否一致,一致的话则赋值给对象。
for (Entry<String, Object> entry : doc.entrySet()) {
fieldValueObj = entry.getValue();

for (Field field : declaredFields) {
// 字段名一致
if (field.getName().equals(entry.getKey())) {
field.setAccessible(true);

// 类型转换,如果是solr文档对象是日期类型,并且与clazz属性类型不一致,则做日期格式转换
if (fieldValueObj instanceof Date) {
if (field.getType() == Date.class) {
field.set(obj, fieldValueObj);
} else {
field.set(obj, dateFormat.format(fieldValueObj));
}
} else {
// 除了日期类型之外,其他类型solr对象与bean对象属性类型一致,按原类型设置值
if (fieldValueObj.getClass() == field.getType()) {
field.set(obj, fieldValueObj);
} else {
field.set(obj, fieldValueObj.toString());
}
}
hasSameFieldName = true;
break;
}
}

if (!hasSameFieldName) {
// 那些没有找到相同属性名的值,全部加入Map对象中
if (fieldValueObj instanceof Date) {
fieldValueStr = dateFormat.format(fieldValueObj);
} else {
// 除了日期类型之外,其他类型按字符串类型设置值
fieldValueStr = fieldValueObj.toString();
}
otherFieldValueMap.put(entry.getKey(), fieldValueStr);
}

} // end-for (Entry<String, Object> entry : doc.entrySet())

// 通过反射,设置其他字段值map到对象实例
setOtherFieldValueMap(declaredFields, obj, otherFieldValueMap);

rtnList.add(obj);
} catch (InstantiationException | IllegalAccessException e) {
// 出现异常,记录日志,不直接抛出中断流程
log.error("通过转换得到对应的转换对象列表方法时,出现异常!", e);
}
}
return rtnList;
}

/**
* 通过反射,设置其他字段值map到对象实例
*
*
@param declaredFields 所有属性字段的列表
*
@param obj 要转换对象Class的对象实例
*
@param otherFieldValueMap 其他字段值map
*
@return
*/
private <T extends Object> T setOtherFieldValueMap(Field[] declaredFields, T obj,
Map<String, String> otherFieldValueMap) {

for (Field field : declaredFields) {
if (field.isAnnotationPresent(org.apache.solr.client.solrj.beans.Field.class)
&& field.getType() == Map.class) {

org.apache.solr.client.solrj.beans.Field annotationField = field
.getAnnotation(org.apache.solr.client.solrj.beans.Field.class);

// 注解的字段名是否为otherFields,则把除了有设置别名之外的需要返回的字段值,赋值给该字段值上
if (ANNOTATION_OTHERFIELDS.equals(annotationField.value())) {
try {
field.setAccessible(true);
field.set(obj, otherFieldValueMap);
} catch (IllegalArgumentException | IllegalAccessException e) {
// 出现异常,记录日志,不直接抛出中断流程
log.error("通过反射设置其他字段值map到对象实例方法时,出现异常!", e);
}
break;
}
}
}
return obj;
}

/**
* 触发批量更新,针对批量<br>
* 全库导入,同步的速度是最快,增量导入,需要逐条比对,比较慢。<br>
*
*
@param coreName core的名称
*
@param hasFullImport 是否全库导入,第一次抽取匹配时设置为true,增量抽取匹配时设置为false
*
@return Long 更新条数
*/
@SuppressWarnings("unchecked")
public Long triggerBatchUpdate(String coreName, Boolean hasFullImport) throws Exception {
// 命令参数
String commandParam = (hasFullImport != null && hasFullImport) ? "full-import" : "delta-import";
// 同步参数设置
Map<String, String> paramsDataMap = new HashMap<String, String>();
paramsDataMap.put("command", commandParam);
paramsDataMap.put("clean", "false");
paramsDataMap.put("commit", "true");

// 根据参数执行solr导入同步数据操作
importData(coreName, paramsDataMap);

// 状态参数设置
Map<String, String> statusParamsDataMap = new HashMap<String, String>();
statusParamsDataMap.put("command", "status");

int num = 0;
QueryResponse response; // 查询结果对象
NamedList<Object> resMap; // 查询结果Map
String status; // 状态值,有两种,idle和busy。
LinkedHashMap<String, String> statusMessages; // 状态消息对象

while (true) {
// solr导入数据操作
response = importData(coreName, statusParamsDataMap);

// 获取得到更新条数。
resMap = response.getResponse();
status = resMap.get("status") == null ? "" : resMap.get("status").toString();
log.debug("~~~~~获取数据同步至solr状态!状态为:" + status);

if ("idle".equalsIgnoreCase(status)) { // 状态已完成
if (resMap.get("statusMessages") != null) { // 更新条数是在statusMessages对象里
try {
statusMessages = (LinkedHashMap<String, String>) resMap.get("statusMessages");
num = Integer.parseInt(statusMessages.get("Total Documents Processed"));
} catch (Exception e1) {
num = 0;
}
}
break;
}
TimeUnit.MILLISECONDS.sleep(1000); // 睡眠1秒
}

return (long) num;
}

/**
* 得到分组的solr文档对象列表
*
*
@param pivotFieldList 枢轴字段对象列表
*
@param aliasFields 过滤显示字段别名数组
*
@return
*/
private List<SolrDocument> getFacetSolrDocumentList(List<PivotField> pivotFieldList, String[] aliasFields) {
List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
List<SolrDocument> returnSolrDocList = new ArrayList<SolrDocument>();
// 调用递归枢轴字段方法
recursiveFacetPivotField(pivotFieldList, null, results);
if (CollectionUtils.isEmpty(results)) {
return returnSolrDocList;
}

// 将List<Map<String, Object>> 转换为List<SolrDocument>结果
SolrDocument solrDoc = null;
int aliasFieldIndex;
String pivotFieldKey;
for (Map<String, Object> pivotFieldAndValueMap : results) {
solrDoc = new SolrDocument();
aliasFieldIndex = 0;
for (Entry<String, Object> entry : pivotFieldAndValueMap.entrySet()) {
// 别名处理
if (aliasFields != null && aliasFields.length > 0 && aliasFieldIndex < aliasFields.length) {
pivotFieldKey = aliasFields[aliasFieldIndex];
aliasFieldIndex++;
} else {
pivotFieldKey = entry.getKey();
}
solrDoc.setField(pivotFieldKey, entry.getValue());
}
returnSolrDocList.add(solrDoc);
}
return returnSolrDocList;
}

/**
* 递归分组的枢轴字段方法<br>
* PivotField为solr特殊类型<br>
*
*
@param pivotFieldList 枢轴字段对象列表
*
@param pivotFieldAndValueMap 枢轴字段和值的Map对象
*
@param results 结果集,类似于数据库group by后,得到的结果集
*/
private void recursiveFacetPivotField(List<PivotField> pivotFieldList, Map<String, Object> pivotFieldAndValueMap,
List<Map<String, Object>> results) {
if (pivotFieldList == null || pivotFieldList.size() <= 0) {
return;
}
if (pivotFieldAndValueMap == null) {
pivotFieldAndValueMap = new LinkedHashMap<String, Object>();
}

for (PivotField pivotField : pivotFieldList) {
pivotFieldAndValueMap.put(pivotField.getField(), pivotField.getValue());
if (pivotField.getPivot() == null || pivotField.getPivot().size() <= 0) {
// TODO 设定统计值key和值,key暂时未设置为常量
pivotFieldAndValueMap.put("count", pivotField.getCount());
results.add(new LinkedHashMap<String, Object>(pivotFieldAndValueMap));
} else {
recursiveFacetPivotField(pivotField.getPivot(), pivotFieldAndValueMap, results);
}
}
}

/**
* 得到solr的查询返回结果对象
*
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@param facetParams 分组参数字符串,格式以逗号隔开,如name,age
*
@param requestHandler 请求句柄,如分词为splitwords
*
@param coreUrls core Url数组,用于跨core查询
*
@return
* @throws Exception
*/
private QueryResponse getQueryResponse(String criteria, String coreName, Integer rows, Integer start,
String sortParams, String[] fields, String[] aliasFields, String facetParams, String requestHandler,
String[] coreUrls) throws Exception {
// 跨core查询
if (coreUrls != null && coreUrls.length > 1) {
return getMoreCoreQueryResponse(coreUrls, criteria, coreName, rows, start, sortParams, fields, aliasFields);
}

SolrQuery solrQuery = new SolrQuery().setQuery(criteria).setRows(rows).setStart(start);
solrQuery = setSortParams(sortParams, solrQuery);
solrQuery = setFacetParams(facetParams, solrQuery);
fields = this.getAliasFieldsArray(fields, aliasFields);
solrQuery.setFields(fields);
if (StringUtils.isNotEmpty(requestHandler)) {
solrQuery.setRequestHandler("/" + requestHandler);
}

return this.getQueryResponse(coreName, solrQuery);
}

/**
* 得到solr的查询返回结果对象
*
*
@param coreName core名称
*
@param solrQuery solr查询对象
*
@return
* @throws Exception
*/
public QueryResponse getQueryResponse(String coreName, SolrQuery solrQuery) throws Exception {
SolrServer solrServer = getSolrServer(coreName);
// 设置分组参数
QueryRequest queryRequest = new QueryRequest(solrQuery);
queryRequest.setMethod(SolrRequest.METHOD.POST);
try {
// if (log.isDebugEnabled()) {total++;}
QueryResponse response = queryRequest.process(solrServer);
return response;
} catch (SolrServerException | HttpSolrServer.RemoteSolrException e) {
String error = "solr工具类-执行得到solr的查询返回结果对象方法出现异常!";
Throwable throwable = e.getCause();
if (throwable instanceof SocketException || throwable instanceof IOException) {
try {
// solrServer的ping调用
pingSolrServer(solrServer);
} catch (Exception ex) {
throw ex;
}
return getQueryResponse(coreName, solrQuery);
}
log.error(error, e);
throw new SolrServerIoException(error);
}
}

/**
* 得到跨core查询返回的结果对象
*
*
@param coreUrls core Url数组,用于跨core查询
*
@param criteria 查询条件
*
@param coreName core的名称
*
@param rows 查询条数
*
@param start 开始查询的位置
*
@param sortParams 排序参数,以逗号隔开。如id desc,name desc
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@return
* @throws Exception
*/
private QueryResponse getMoreCoreQueryResponse(String[] coreUrls, String criteria, String coreName, int rows,
int start, String sortParams, String[] fields, String[] aliasFields) throws Exception {
SolrServer solrServer = getSolrServer(coreName);
// 获取多core查询参数
ModifiableSolrParams solrParams = getMoreCoreModifiableSolrParams(coreUrls, criteria, rows, start, sortParams,
fields, aliasFields);
QueryRequest queryRequest = new QueryRequest(solrParams);
queryRequest.setMethod(SolrRequest.METHOD.POST);
try {
// if (log.isDebugEnabled()) {total++;}
QueryResponse response = queryRequest.process(solrServer);
return response;
} catch (SolrServerException | HttpSolrServer.RemoteSolrException e) {
String error = "solr工具类-执行得到solr的查询返回结果对象方法出现异常!";
Throwable throwable = e.getCause();
if (throwable instanceof SocketException || throwable instanceof IOException) {
try {
// solrServer的ping调用
pingSolrServer(solrServer);
} catch (Exception ex) {
throw ex;
}
return getMoreCoreQueryResponse(coreUrls, criteria, coreName, rows, start, sortParams, fields,
aliasFields);
}
log.error(error, e);
throw new SolrServerIoException(error);
}
}

/**
* 获取多core查询参数
*
*
@param coreUrls core Url数组,用于跨core查询
*
@param criteria 查询条件
*
@param rows 查询的行数
*
@param start 开始位置
*
@param sortParams 排序参数
*
@param fields 过滤显示字段数组,如果设置,只显示数组内的字段,数组为空,显示全部
*
@param aliasFields 过滤显示字段别名数组,数组的顺序与fields字段数组的顺序一一对应,长度可不相同,当不相同时,取回fields对应的字段名为别名
*
@return
*/
private ModifiableSolrParams getMoreCoreModifiableSolrParams(String[] coreUrls, String criteria, int rows,
int start, String sortParams, String[] fields, String[] aliasFields) {
ModifiableSolrParams solrParams = new ModifiableSolrParams();
solrParams.set("q", criteria);
solrParams.set("sort", sortParams); // 排序
solrParams.set("start", start); // 记录开始位置
solrParams.set("rows", rows); // 查询的行数
solrParams.set("shards.info", true);
solrParams.set("shards", StringUtils.join(coreUrls, ","));
fields = this.getAliasFieldsArray(fields, aliasFields);
if (fields != null && fields.length > 0) {
solrParams.set("fl", StringUtils.join(fields, ",")); // 设置过滤字段
}

return solrParams;
}

/**
* 得到solrClient的solrUrl
*
*
@return
*/
public String getSolrUrl() {
return solrUrl;
}

/**
* solrServer的ping调用<br>
* 网络断开连接不上时,调用ping方法检查网络情况。在设置的次数范围之内能ping通网络,则继续进行往下执行地址匹配流程,否则抛出异常。
*
*
@param solrServer solr服务对象
*
@return
* @throws Exception
*/
private synchronized SolrServer pingSolrServer(SolrServer solrServer) throws Exception {
try {
solrServer.ping();
} catch (SolrServerException | IOException | HttpSolrServer.RemoteSolrException e) {
// 循环次数,网络出现异常情况下,循环设置的次数回调ping方法。超过设置的次数,将直接抛出异常。
int loopNum = 0;
if (solrLoopNumMap.containsKey(solrUrl)) {
loopNum = solrLoopNumMap.get(solrUrl) + 1;
} else {
loopNum++;
}
solrLoopNumMap.put(solrUrl, loopNum);

// TODO 由于类代码长度限制1500行,目前暂时以简单方式读取配置值,后期再优化
// solr的ping循环次数,默认为5
if (pingLoopNum <= 0) {
try {
pingLoopNum = Integer.parseInt(ConfigUtil.getPropsValueByKey("ac.httpSolr.pingLoopNum"));
//pingLoopNum = Integer.parseInt(CommonString.pingLoopNum);
pingLoopNum = pingLoopNum <= 0 ? 5 : pingLoopNum;
} catch (NumberFormatException ex) {
ex.printStackTrace();
pingLoopNum = 5;
}
}
// solr的ping睡眠时间,以毫秒为单位,默认为10秒
if (pingSleepTime <= 0) {
try {
pingSleepTime = Long.parseLong(ConfigUtil.getPropsValueByKey("ac.httpSolr.pingSleepTime"));
//pingSleepTime = Long.parseLong(CommonString.pingSleepTime);
pingSleepTime = pingSleepTime <= 0 ? 10000 : pingSleepTime;
} catch (NumberFormatException ex) {
ex.printStackTrace();
pingSleepTime = 10000;
}
}
// sol的ping循环次数超过设置的值,抛出SolrHttpException异常
if (loopNum > pingLoopNum) {
log.warn("执行solrServer的ping方法超过" + pingLoopNum + "次仍然出现网络异常!调用URL为:" + solrUrl);
String error = String.format("solr工具类-执行solrServer的ping方法出现异常!原因是solr【%s】没有启动或网络不通", solrUrl + "/");
log.error(error, e);
throw new SolrHttpException(error);
}
// 睡眠,单位为毫秒
TimeUnit.MILLISECONDS.sleep(pingSleepTime);

log.info("网络出现异常!再次执行solrServer的ping方法,调用次数为:" + loopNum + ",调用URL为:" + solrUrl);
return pingSolrServer(solrServer);
}
solrLoopNumMap.put(solrUrl, 0);
return solrServer;
}

/**
* 空间检索
*
@param coreName
*
@param solrQuery
*
@return
* @throws Exception
*/
public QueryResponse spaceSearch(String coreName, SolrQuery solrQuery) throws Exception {
if (StringUtils.isNotEmpty(coreName)) {
try {
QueryResponse response = getQueryResponse(coreName, solrQuery);
return response;
} catch (SolrServerException | HttpSolrServer.RemoteSolrException e) {
String error = "solr工具类-执行得到solr的查询返回结果对象方法出现异常!";
Throwable throwable = e.getCause();
if (throwable instanceof SocketException || throwable instanceof IOException) {
try {
SolrServer solrServer = getSolrServer(coreName);
// solrServer的ping调用
pingSolrServer(solrServer);
} catch (Exception ex) {
throw ex;
}
return spaceSearch(coreName, solrQuery);
}
log.error(error, e);
throw new SolrServerIoException(error);
}
}
return null;
}




}

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。更多免费资源在http://www.gitweixin.com/?p=2627

发表评论

邮箱地址不会被公开。 必填项已用*标注