詳解spring中使用solr的代碼實(shí)現(xiàn)
在介紹solr的使用方法之前,我們需要安裝solr的服務(wù)端集群?;旧暇褪前惭bzookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可。由于本人并沒有具體操作過如何進(jìn)行solr集群的搭建。所以關(guān)于如何搭建solr集群,讀者可以去網(wǎng)上查看其它資料,有很多可以借鑒。這里只介紹搭建完solr集群之后,我們客戶端是如何訪問solr集群的。
之前介紹過,spring封裝nosql和sql數(shù)據(jù)庫的使用,都是通過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進(jìn)行訪問。
首先:
引入solr的原生代碼api的jar包
<dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>4.7.2</version> </dependency>
其次:
在spring的配置文件中配置我們solr的FactoryBean類,此類是作為我們編寫自己業(yè)務(wù)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中的數(shù)據(jù)結(jié)構(gòu)bean
再次:
編寫我們的SolrCloudServerFactoryBean類,其中使用了spring的FactoryBean<SolrServer>,和InitializingBean。關(guān)于這兩者的含義讀者可以參考其他資料,基本意思是spring容器在注冊該bean之前,需要進(jìn)行的一些初始化操作。通過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;
}
}
最后:
現(xiàn)在就可以編寫我們的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;
/**
* 創(chuàng)建索引
*
* @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值進(jìn)行主鍵查詢。
* @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;
}
}
當(dāng)然solr的api不止于此,我們此處只是羅列了一些比較常用的使用方法。對于solr的查詢,有以下幾點(diǎn)需要注意。
1. solr生成查詢語句的時候,是有q查詢和fq查詢之分的。哪些查詢條件放在q查詢里,哪些查詢條件放在fq查詢里,對查詢的效率還是有較大的影響的。一般固定不變的查詢條件放在q查詢里,經(jīng)常變化的查詢條件放在fq里。上述代碼中validTag:1就放在了q查詢里,循環(huán)里的字符串filter則放在了我們的fq查詢里。
2. solr查詢時,要了解solr服務(wù)器集群的配置文件中使用的是什么樣的分詞器,不同分詞器對模糊查詢的結(jié)果是有影響的。比如常見的IK分詞器和標(biāo)準(zhǔn)分詞器(如果我們有一個字段的名稱為:我是中國人,ik分詞器在solr里的存儲就成為了“我”,“中國人”,“中國”,“國人”。而標(biāo)準(zhǔn)分詞器則會存儲為“我”,“是”,“中”,“國”,“人”。如果我們使用全稱查詢,即查詢:我是中國人,兩者是沒有問題的。但是使用模糊查詢,比如查詢“*我是*”,則兩個分詞器分詞都查不出來結(jié)果,而如果我們的查詢條件是“*中國人*”則在ik分詞器下可以查詢出結(jié)果,在標(biāo)準(zhǔn)分詞器下查不出結(jié)果。)
3. 使用solr的過程中,需要定時執(zhí)行solr的optimize函數(shù)來清理磁盤碎片,否則會影響讀寫效率。對于optimize的參數(shù)建議為(false,false,5)。
4. 寫solr數(shù)據(jù)的時候,盡量使用createIndexBatch方法,這是因為solr在執(zhí)行寫入的時候,寫入一條數(shù)據(jù)和寫入多條數(shù)據(jù)都需要全量建索引,其執(zhí)行時間是差不多的。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解析使用jdbc,hibernate處理clob/blob字段的詳解
本篇是對使用jdbc,hibernate處理clob/blob字段進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
springboot項目中mybatis-plus@Mapper注入失敗問題
這篇文章主要介紹了springboot項目中mybatis-plus@Mapper注入失敗問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
Java 中 getClass() 方法的使用與原理深入分析(對象類型信息)
在 Java 編程中,getClass() 是一個非常重要的方法,它用于獲取對象的運(yùn)行時類信息,無論是調(diào)試代碼、反射操作,還是類型檢查,getClass() 都扮演著關(guān)鍵角色,本文將深入探討 getClass() 的使用方法、底層原理以及實(shí)際應(yīng)用場景,感興趣的朋友一起看看吧2024-12-12
Java編程實(shí)現(xiàn)調(diào)用com操作Word方法實(shí)例代碼
這篇文章主要介紹了Java編程實(shí)現(xiàn)調(diào)用com操作Word方法實(shí)例代碼,代碼注釋很詳細(xì),在這里分給大家,需要的朋友可以參考下。2017-09-09
聊一聊SpringBoot服務(wù)監(jiān)控機(jī)制
這篇文章主要介紹了聊一聊SpringBoot服務(wù)監(jiān)控機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Spring強(qiáng)大事務(wù)兼容數(shù)據(jù)庫多種組合解決業(yè)務(wù)需求
這篇文章主要為大家介紹了Spring強(qiáng)大事務(wù)兼容數(shù)據(jù)庫多種組合解決業(yè)務(wù)需求示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
spring mvc 和ajax異步交互完整實(shí)例代碼
本篇文章主要介紹了spring mvc 和ajax異步交互完整實(shí)例代碼,簡單的AJAX+SpringMVC的異步交互小例子,有興趣的可以了解一下。2017-02-02

