在介紹solr的使用方法之前,我們需要安裝solr的服務(wù)端集群。基本上就是安裝zookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可。由于本人并沒有具體操作過(guò)如何進(jìn)行solr集群的搭建。所以關(guān)于如何搭建solr集群,讀者可以去網(wǎng)上查看其它資料,有很多可以借鑒。這里只介紹搭建完solr集群之后,我們客戶端是如何訪問(wèn)solr集群的。
之前介紹過(guò),spring封裝nosql和sql數(shù)據(jù)庫(kù)的使用,都是通過(guò)xxxTemplate。solr也不例外。
我們需要引入solr的jar包
1
2
3
4
5
|
< dependency > < groupId >org.springframework.data</ groupId > < artifactId >spring-data-solr</ artifactId > < version >1.0.0.RELEASE</ version > </ dependency > |
然后引入solr在spring中封裝的配置
1
2
3
4
5
6
7
8
9
10
11
12
|
< 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這種格式做講解。個(gè)人在使用spring封裝solr的方式的時(shí)候遇到了各種各樣的問(wèn)題,可能是能力太low架控不了吧。下面我們主要講解下如何使用solr的原生api進(jìn)行訪問(wèn)。
首先:
引入solr的原生代碼api的jar包
1
2
3
4
5
|
< dependency > < groupId >org.apache.solr</ groupId > < artifactId >solr-solrj</ artifactId > < version >4.7.2</ version > </ dependency > |
其次:
在spring的配置文件中配置我們solr的FactoryBean類,此類是作為我們編寫自己業(yè)務(wù)service類的屬性來(lái)操作solr。
1
2
3
4
5
|
< 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是我們存儲(chǔ)在solr中的數(shù)據(jù)結(jié)構(gòu)bean
再次:
編寫我們的SolrCloudServerFactoryBean類,其中使用了spring的FactoryBean<SolrServer>,和InitializingBean。關(guān)于這兩者的含義讀者可以參考其他資料,基本意思是spring容器在注冊(cè)該bean之前,需要進(jìn)行的一些初始化操作。通過(guò)afterPropertiesSet方法可以看到我們?cè)谑褂胹olr之前做的一些初始化操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
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的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
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 ); } //添加過(guò)濾條件 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)){ //如果是會(huì)員類型,則添加模糊查詢 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中做聚合,做排序。而且整個(gè)過(guò)程都是秒級(jí)的。 * @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); //添加分頁(yè) 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不止于此,我們此處只是羅列了一些比較常用的使用方法。對(duì)于solr的查詢,有以下幾點(diǎn)需要注意。
1. solr生成查詢語(yǔ)句的時(shí)候,是有q查詢和fq查詢之分的。哪些查詢條件放在q查詢里,哪些查詢條件放在fq查詢里,對(duì)查詢的效率還是有較大的影響的。一般固定不變的查詢條件放在q查詢里,經(jīng)常變化的查詢條件放在fq里。上述代碼中validTag:1就放在了q查詢里,循環(huán)里的字符串filter則放在了我們的fq查詢里。
2. solr查詢時(shí),要了解solr服務(wù)器集群的配置文件中使用的是什么樣的分詞器,不同分詞器對(duì)模糊查詢的結(jié)果是有影響的。比如常見的IK分詞器和標(biāo)準(zhǔn)分詞器(如果我們有一個(gè)字段的名稱為:我是中國(guó)人,ik分詞器在solr里的存儲(chǔ)就成為了“我”,“中國(guó)人”,“中國(guó)”,“國(guó)人”。而標(biāo)準(zhǔn)分詞器則會(huì)存儲(chǔ)為“我”,“是”,“中”,“國(guó)”,“人”。如果我們使用全稱查詢,即查詢:我是中國(guó)人,兩者是沒有問(wèn)題的。但是使用模糊查詢,比如查詢“*我是*”,則兩個(gè)分詞器分詞都查不出來(lái)結(jié)果,而如果我們的查詢條件是“*中國(guó)人*”則在ik分詞器下可以查詢出結(jié)果,在標(biāo)準(zhǔn)分詞器下查不出結(jié)果。)
3. 使用solr的過(guò)程中,需要定時(shí)執(zhí)行solr的optimize函數(shù)來(lái)清理磁盤碎片,否則會(huì)影響讀寫效率。對(duì)于optimize的參數(shù)建議為(false,false,5)。
4. 寫solr數(shù)據(jù)的時(shí)候,盡量使用createIndexBatch方法,這是因?yàn)閟olr在執(zhí)行寫入的時(shí)候,寫入一條數(shù)據(jù)和寫入多條數(shù)據(jù)都需要全量建索引,其執(zhí)行時(shí)間是差不多的。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/antao592/article/details/52807653