由於上一遍部落格中elasticsearch5.x在linux上分布式安裝(多節點) 裝的elasticsearch服務比較新,所以將elasticsearch單獨做成一個服務運行,需要用的時候用路由轉寄請求這個服務。
因為elasticsearch版本比較新,所以用到了spring data的裡程版本,具體的pom.xml配置和當前這個項目 可以去我github首頁下載(https://github.com/lh2420124680/ElasticServer)。
elasticsearch底層用的是lucene,它的搜尋速度是非常快的。
在實際的應用中我們有兩種部署方案,分別應對不同的業務情境:
①即時儲存資料入索引庫
當使用者儲存資料的時候,儲存一份到資料庫中,當資料庫儲存成功後,將儲存的資料發送到訊息隊裡中(本項目中的訊息中介軟體用的是activeMQ),然後當訊息佇列監聽到有訊息發過過來的時候,接受到訊息,也就是接受到儲存的資料,然後訊息監聽器執行elasticsearch方法將儲存到資料庫中的資料同時也儲存到對應的索引庫中。這種方法對原來的代碼侵入少,因為elasticsearch的服務是單獨的一個項目。
流程:前端將要插入的資料發送給後台 -> 插入資料庫成功 -> 前端執行發送訊息佇列的方法 -> 發送的資料進入訊息佇列 -> 訊息監聽器接收到訊息後將資料插入到對應的索引庫中。
本方法適用查詢即時性並且結構簡單的資料。
②每天定時從資料庫中將資料同步到索引庫中。
這種方法需要用到elastic旗下的一些外掛程式,可以參考本部落格中的 elasticsearch與oracle資料庫資料同步
本方法適用查詢非即時性但結構複雜的資料。
本篇部落客要用的是第一個方法,對spring data中的ElasticsearchTemplate進行了重構,因為ElasticsearchTemplate對於傳回值類型依賴實體,並且實體需要用@document註解才能使用,所以本項目中對ElasticsearchTemplate源碼進行了重構,將發送參數改為json對象的字串,返回結果為List<Map>這樣的類型,這樣就不用每次做新業務時要用到elasticsearch的時候去新增當前業務實體從而去修改代碼,重構後只用一次部署,後面就可以適用多個業務情境。
下面會列出一些需要注意的點,需要深入研究的朋友可以去我的githib上下載該項目(https://github.com/lh2420124680/ElasticServer)。
elasticsearch主要的設定檔:com-zlb-elastic.xml
這裡我沒有用ElasticsearchTemplate,而是用的自訂的elasticObjectTemplate類,讓這個類繼承ElasticsearchTemplate類,然後重寫父類。
然後修改了ElasticsearchTemplate類中的queryForList方法。(父類中的只有查詢對象和實體物件兩個參數)
本項目v1.0隻是對單個插入到索引庫的方法進行了重構,後面會慢慢重構ElasticsearchTemplate所有的主要方法。
這裡我們需要將傳過來的json對象轉為object數組,因為父類中會用到
另外我們還需要重構一個類ResultsMapper,用於索引的返回結果對象。
ResultsMapper是用於ElasticsearchTemplate類中返回結果對象的處理類,他返回的List為傳輸過來的實體泛型,例如當我查詢的時候傳輸後來為Man類,那麼他返回就是List<Man>,因為本次重構去掉了實體,用Map來代替實體模型,所以我們也許需要重寫ResultsMapper類,但是ResultsMapper是一個介面,我們找到DefaultResultMapper類實現了這個ResultsMapper介面,所有我們重寫一個DefaultResultObjectMapper類去繼承DefaultResultMapper這個類,然後主要重寫mapEntity方法。
以上就是對源碼的重寫的一些要點,雖然說的不是很清楚,但是看過幾遍ElasticsearchTemplate的源碼就會理解。
訊息監聽器,監聽到資料就調用自訂的ElasticObjectTemplate方法去插入到索引庫中。
package com.zjy.jms;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.zjy.biz.ElasticBiz;import com.zjy.helper.ElasticObjectTemplate;import com.zjy.helper.EscapeHelper;import com.zjy.helper.StringUtils;import java.io.UnsupportedEncodingException;import java.util.Map;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.TextMessage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.elasticsearch.core.query.IndexQuery;import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;import org.springframework.jms.annotation.JmsListener;import org.springframework.stereotype.Component;@Componentpublic class ElasticReceiver implements MessageListener {private ElasticObjectTemplate elasticObjectTemplate;public ElasticObjectTemplate getElasticObjectTemplate() {return elasticObjectTemplate;}public void setElasticObjectTemplate(ElasticObjectTemplate elasticObjectTemplate) {this.elasticObjectTemplate = elasticObjectTemplate;}public void onMessage(Message message) {try {String msg = ((TextMessage) message).getText();String data = java.net.URLDecoder.decode(msg, "UTF-8");String unescape = EscapeHelper.unescape(data);JSONObject parseObject = JSON.parseObject(unescape);String dataId = StringUtils.GetGUID();String indexName = parseObject.getString("indexName");String typeName = parseObject.getString("typeName");String entity = parseObject.getString("entity");IndexQuery indexQuery = new IndexQueryBuilder().withId(dataId).withIndexName(indexName).withType(typeName).withObject(entity).build(); String index = elasticObjectTemplate.index(indexQuery);System.out.println("indexName:"+indexName + index);} catch (JMSException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}
elasticsearch查詢,刪除的服務介面。
package com.zjy.service;import java.util.List;import java.util.Map;import javax.servlet.http.HttpServletRequest;import org.elasticsearch.index.query.BoolQueryBuilder;import org.elasticsearch.index.query.QueryBuilders;import org.elasticsearch.search.sort.SortBuilder;import org.elasticsearch.search.sort.SortBuilders;import org.elasticsearch.search.sort.SortOrder;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.PageRequest;import org.springframework.data.elasticsearch.core.query.IndexQuery;import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;import org.springframework.data.elasticsearch.core.query.SearchQuery;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import com.alibaba.fastjson.JSON;import com.zjy.helper.DataConvertHelper;import com.zjy.helper.ElasticObjectTemplate;import com.zjy.helper.EscapeHelper;import com.zjy.helper.ListResult;import com.zjy.helper.StringUtils;@RestController@RequestMapping(value = "/ElasticService")public class ElasticService {@Autowiredprivate ElasticObjectTemplate elasticObjectTemplate;/** * @param request * @return */@RequestMapping(value = "/queryResource.ashx", method = RequestMethod.GET)public ListResult<Map<String, Object>> queryResource(HttpServletRequest request) {Map<String, Object> where = DataConvertHelper.getRequestParams(request);String indexName = where.get("indexName").toString();String typeName = where.get("typeName").toString();// 建立搜尋條件對象(條件 參數名 + "_jz" 為精準匹配, 參數名 + "_mh" 為模糊比對)BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();String key = "";String value = "";String mode = "";String reallyParam = "";for(Map.Entry<String, Object> map : where.entrySet()) {key = map.getKey();value = map.getValue().toString();mode = key.substring(key.length()-3);reallyParam = key.substring(0, key.length()-3);if ("_jz".equals(mode)) { // 該條件為精準查詢boolQuery.must(QueryBuilders.termQuery(reallyParam, value));} else if ("_mh".equals(mode)) { // 該條件為模糊查詢boolQuery.must(QueryBuilders.matchQuery(reallyParam, value));}}// 建立排序對象 (0是倒敘,1是正序)Object sort = where.get("sort");Object sortType = where.get("sortType");SortBuilder sortBuilder = null;if (!StringUtils.IsEmptyOrNull(sort) && !StringUtils.IsEmptyOrNull(sortType)) {if ("0".equals(sortType)) { // 倒敘sortBuilder = SortBuilders.fieldSort(sort.toString()).order(SortOrder.DESC);} else { // 正序sortBuilder = SortBuilders.fieldSort(sort.toString()).order(SortOrder.ASC);}}// 分頁資料int pageIndex = StringUtils.IsEmptyOrNull(where.get("pageIndex")) ? 1 : Integer.valueOf(where.get("pageIndex").toString());int pageSize = StringUtils.IsEmptyOrNull(where.get("pageSize")) ? 10 : Integer.valueOf(where.get("pageSize").toString());// 建立SearchQuery查詢對象 判斷排序對象是否空,是空就不建立排序對象SearchQuery searchQuery = null;if (sortBuilder == null) {searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery).withPageable(PageRequest.of((pageIndex-1)*pageSize, pageSize)).build();} else {searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery).withSort(sortBuilder).withPageable(PageRequest.of(pageIndex, pageSize)).build();}List<Map<String, Object>> list = elasticObjectTemplate.queryForList(searchQuery, indexName, typeName);searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery).withIndices(indexName).withTypes(typeName).build();long count = elasticObjectTemplate.count(searchQuery);ListResult<Map<String, Object>> result = new ListResult<Map<String, Object>>();result.setRows(list);result.setTotal((int)count);result.setPageindex(pageIndex);result.setPages(pageSize);return result;}@RequestMapping(value = "/delete.ashx", method = RequestMethod.POST)public boolean insert(HttpServletRequest request) {Map<String, Object> where = DataConvertHelper.getRequestParams(request);String indexName = where.get("indexName").toString();String typeName = where.get("typeName").toString();String gid = where.get("gid").toString();elasticObjectTemplate.delete(indexName, typeName, gid);return false;}}
對於前端傳輸的參數的介面規則可以參考我寫的elasticsearch服務api介面文檔。(文檔地址見圖下面的連結)
elasticsearch服務api介面文檔下載地址:連結:https://pan.baidu.com/s/1bap8LsjH-GkoT2xJuWZcsw 密碼:6tn3
代碼地址:https://github.com/lh2420124680/ElasticServer