springboot整合es6.4

之前一直沒有寫博客的習慣,但看到無數小夥伴都有自己的博客,加上最近開始做微服務的實戰項目了,縷縷續續的會搭建一些環境,心裏有點蠢蠢欲動.....記錄下幾周前搭建的過程,先從es開始寫,照着別人的從環境搭建到postman測試踩了不少坑。

centos7上的環境搭建:

1. docker pull elasticsearch:6.4.0

2. 運行es

docker run -d --name es -p 9200:9200 -p 9300:9300 -e ES_JAVA_OPTS="-Xms300m -Xmx300m" -e "discovery.type=single-node" docker.io/elasticsearch:6.4.0

-xms 初始內存 -xmx最大內存 ,我的是學生機1核2G之前按照默認的來會爆炸.....

 

3. 進入es修改配置文件:

docker exec -it es /bin/bash

cd /usr/share/elasticsearch/config/

vi elasticsearch.yml

cluster.name: recommend     
network.host: 0.0.0.0    //不寫0.0.0.0有可能在idea上報錯,玄學問題.
http.port: 9200
transport.tcp.port: 9300
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["xx.xxx.xxx.xxx:9300"]
discovery.zen.minimum_master_nodes: 1
node.ingest: true
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2
http.cors.enabled: true
http.cors.allow-origin: /.*/

4.安裝ik分詞器

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.0/elasticsearch-analysis-ik-6.4.0.zip

重啓下es

es環境搭建好了,開始在idea上配置

1.導入依賴

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.4.0</version>  
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>6.4.0</version>
</dependency>
2. application.yml的配置

3.獲取yml中的配置信息:
@Component
@ConfigurationProperties(prefix = "elasticsearch")
@Data
public class ESProperties {

     private String ip;
     private String port;
     private String pool;
     private String clusterName;
     private String nodeName;

 }

 

4.配置類:

@Configuration
public class ESConfig {
    @Autowired
     private ESProperties esProperties;


     /**
      * @author 劉其佳
      * @description
      *      創建並初始化TransportClient對象,使用該對象對ES進行增刪查改
      *      cluster.name:集羣名字
      *      node.name:節點名字
      *      client.transport.sniff:客戶端(Java項目)一致監視ES的節點狀態(節點數),不再需要手動添加節點,如果有新的節點產生了,會自動加載進項目中
      *      thread_pool.search.size:線程池
      *
      * @param * param *:
      * @date 2019/9/18
      * @return org.elasticsearch.client.transport.TransportClient
      * @throws
      */
     @Bean("transportClient")
     public TransportClient getTransportClient(){
         //1、創建TransportClient對象
         TransportClient transportClient=null;
         try{
                //2、設置Java對ES的集羣信息
                 Settings settings=Settings.builder().put("cluster.name",esProperties.getClusterName())
                         .put("node.name", esProperties.getNodeName())
			之前按照別人的配置的,一直報錯,花了一上午的時間,才注意到這裏要爲false,搭建es集羣,爲true
                         .put("client.transport.sniff", false) 
                         .put("thread_pool.search.size", esProperties.getPool()).build();
                 //3、初始化TransportClient對象
                 transportClient= new PreBuiltTransportClient(settings);
                 //4、配置對ES的連接信息
                 TransportAddress transportAddress=new TransportAddress(InetAddress.getByName(esProperties.getIp()),Integer.parseInt(esProperties.getPort()));
                 //5、把對ES的連接對象放到transportClient對象中
                 transportClient.addTransportAddress(transportAddress);
             }catch (UnknownHostException e){
                 e.printStackTrace();
             }
         return transportClient;
     }
}
5.工具類
import com.recommend.common.dto.Result;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;


public class ESUtil {

    @Resource
    private TransportClient client;

          /**
    * 創建索引
    *
    * @param index
    * @return
    */
      public  void createIndex(String index) {
         // isIndexExist:判斷索引是否存在
         if (isIndexExist(index)) {
             throw new RuntimeException("重複創建索引");
         }
         CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet();
         // indexresponse.isAcknowledged():創建索引是否成功,return Boolean類型(true:表示成功,false:失敗)
         if(!indexresponse.isAcknowledged()) {
             throw new RuntimeException("創建索引失敗");
         }
     }

          /**
    * 刪除索引
    *
    * @param index
    * @return
    */
      public void deleteIndex(String index) {
         if (!isIndexExist(index)) {
             throw new RuntimeException("索引不存在");
         }
         DeleteIndexResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet();
         if (!dResponse.isAcknowledged()) {
             throw new RuntimeException("刪除索引失敗");
         }
     }

         /**
    * 判斷索引是否存在
    *
    * @param index
    * @return
    */
     public boolean isIndexExist(String index) {
         IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)).actionGet();
         return inExistsResponse.isExists();
     }

         /**
    * @Author: LX
    * @Description: 判斷index下指定type是否存在
    * @Date: // :
    * @Modified by:
    */
     public  boolean isTypeExist(String index, String type) {
         return isIndexExist(index)
                 ? client.admin().indices().prepareTypesExists(index).setTypes(type).execute().actionGet().isExists()
                 : false;
     }

             /**
        * 數據添加,正定ID
        *
        * @param mapObj 要增加的數據
        * @param index      索引,類似數據庫
        * @param type       類型,類似表
        * @param id         數據ID
        * @return
        */
     public void addData(Map<String, Object> mapObj, String index, String type, String id) {
         IndexResponse response = client.prepareIndex(index, type, id).setSource(mapObj).get();
         // response.getId():就是添加數據後ES爲這條數據所生成的id
         // 需要返回添加數據是否成功
         String status = response.status().toString();
         // 添加數據後所返回的狀態(如果成功就是code:-->OK)
         // eq:sacii --> 小寫字母和大寫字母不一樣
         // status:-->OK
         // ok
         if("OK".equals(status.toUpperCase())||"CREATED".equals(status.toUpperCase())) {

         } else {
             throw new RuntimeException("添加數據失敗");
         }
     }

         /**
    * 數據添加
    *
    * @param mapObj 要增加的數據
    * @param index      索引,類似數據庫
    * @param type       類型,類似表
    * @return
    */
     public void addData(Map<String, Object> mapObj, String index, String type) {
         addData(mapObj,index, type, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
     }

         /**
    * @author 劉其佳
    * @description
    *      將對象轉化爲map類型
    * @param * param *:object
    * @date //
    * @return java.util.Map<java.lang.String,java.lang.Object>
    * @throws
    */
     public Map<String, Object> objectTurnMap(Object object){
         Map<String, Object> result = new HashMap<String, Object>();
         //獲得類的屬性名  數組
         Field[] fields = object.getClass().getDeclaredFields();
         try {
                 for (Field field : fields) {
                         field.setAccessible(true);
                         String name = new String(field.getName());
                         result.put(name, field.get(object));
                     }
             }catch (Exception e){
                 e.printStackTrace();
             }
         return result;
     }

         /**
    * 通過ID刪除數據
    *
    * @param index 索引,類似數據庫
    * @param type  類型,類似表
    * @param id    數據ID
    */
     public void deleteDataById(String index, String type, String id) {

         DeleteResponse response = client.prepareDelete(index, type, id).execute().actionGet();
         if(!"OK".equals(response.status().toString().toUpperCase())) {
                throw new RuntimeException("刪除數據失敗或沒有該數據");
         }
     }

         /**
    * 通過ID 更新數據
    *
    * @param mapObj 要增加的數據
    * @param index      索引,類似數據庫
    * @param type       類型,類似表
    * @param id         數據ID
    * @return
    */
     public void updateDataById(Map<String, Object> mapObj, String index, String type, String id) {

         UpdateRequest updateRequest = new UpdateRequest();

         updateRequest.index(index).type(type).id(id).doc(mapObj);

         ActionFuture<UpdateResponse> update = client.update(updateRequest);

         if(!"OK".equals(update.actionGet().status().toString().toUpperCase())) {
             throw new RuntimeException("更新數據失敗");
         }
     }

         /**
    * 通過ID獲取數據
    *
    * @param index  索引,類似數據庫
    * @param type   類型,類似表
    * @param id     數據ID
    * @param fields 需要顯示的字段,逗號分隔(缺省爲全部字段)
    * @return
    */
     public Map<String, Object> searchDataById(String index, String type, String id, String fields) {

         GetRequestBuilder getRequestBuilder = client.prepareGet(index, type, id);

         if (StringUtils.isNotEmpty(fields)) {
                 getRequestBuilder.setFetchSource(fields.split(","), null);
             }

         GetResponse getResponse = getRequestBuilder.execute().actionGet();

         return getResponse.getSource();
     }

         /**
    * 使用分詞查詢
    *
    * @param index          索引名稱
    * @param type           類型名稱,可傳入多個type逗號分隔
    * @param query          查詢條件
    * @param size           文檔大小限制
    * @param fields         需要顯示的字段,逗號分隔(缺省爲全部字段)
    * @param sortField      排序字段
    * @param highlightField 高亮字段
    * @return
    */
     public List<Map<String, Object>> searchListData(String index, String type, QueryBuilder query, Integer size, String fields, String sortField, String highlightField) {
         SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index);
         if (StringUtils.isNotEmpty(type)) {
                 searchRequestBuilder.setTypes(type.split(","));
             }

         if (StringUtils.isNotEmpty(highlightField)) {
                 HighlightBuilder highlightBuilder = new HighlightBuilder();
                 // 設置高亮字段
                 highlightBuilder.field(highlightField);
                 searchRequestBuilder.highlighter(highlightBuilder);
             }

         searchRequestBuilder.setQuery(query);

         if (StringUtils.isNotEmpty(fields)) {
                 searchRequestBuilder.setFetchSource(fields.split(","), null);
             }
         searchRequestBuilder.setFetchSource(true);

         if (StringUtils.isNotEmpty(sortField)) {
                 searchRequestBuilder.addSort(sortField, SortOrder.DESC);
             }

         if (size != null && size > 0 ) {
                 searchRequestBuilder.setSize(size);
             }

         //打印的內容 可以在 Elasticsearch head 和 Kibana  上執行查詢

         SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();

         long totalHits = searchResponse.getHits().totalHits;
         long length = searchResponse.getHits().getHits().length;

         if (searchResponse.status().getStatus() == 200 ) {
                 // 解析對象
                 return setSearchResponse(searchResponse, highlightField);
             }
         return null;

     }


         /**
    * 高亮結果集 特殊處理
    *
    * @param searchResponse
    * @param highlightField
    */
     private List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) {
         List<Map<String, Object>> sourceList = new ArrayList<Map<String, Object>>();
         StringBuffer stringBuffer = new StringBuffer();

         for (SearchHit searchHit : searchResponse.getHits().getHits()) {
                 searchHit.getSourceAsMap().put("id", searchHit.getId());

                 if (StringUtils.isNotEmpty(highlightField)) {

//                         System.out.println("遍歷 高亮結果集,覆蓋 正常結果集" + searchHit.getSourceAsMap());
                         Text[] text = searchHit.getHighlightFields().get(highlightField).getFragments();

                         if (text != null) {
                                 for (Text str : text) {
                                         stringBuffer.append(str.string());
                                     }
                                 //遍歷 高亮結果集,覆蓋 正常結果集
                                 searchHit.getSourceAsMap().put(highlightField, stringBuffer.toString());
                             }
                     }
                 sourceList.add(searchHit.getSourceAsMap());
             }
         return sourceList;
     }
}

5.測試

實體類和mapper懶得放上來了,只要把article和mapper換成對應的操作就行,拿的是之前社團的接口做的測試....

使用之前要在啓動類bean注入esutil

sevice層

@Service
@Transactional
public class TestEsServiceImpl implements TestEsService {
    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ESUtil esUtil;

    @Override
    public void createIndex(String index) {
        esUtil.createIndex(index);
    }

    @Override
    public void deleteIndex(String index) {
        esUtil.deleteIndex(index);
    }

    @Override
    public Result addDataBySql(String id) {
        Example example = new Example(Article.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("id", id);
        Article article = articleMapper.selectByExample(example).get(0);
        if(article!=null){
             Map<String, Object> dataMap=new HashMap<String, Object>();
             dataMap.put("id",article.getId());
             dataMap.put("title",article.getTitle());
             dataMap.put("createtime",article.getCreatetime());
             dataMap.put("hearts",article.getHearts());
             esUtil.addData(dataMap,"article","game",id);
             return Result.ok();
        }else{
             return Result.not_ok("不存在該文章數據");
        }
    }

    @Override
    public Result addAllData(String index) {
        List<Article> articlesList = articleMapper.selectAll();
        if(articlesList.size()>0){
             for (Article article : articlesList) {
                     Map<String , Object> mapObj=esUtil.objectTurnMap(article);
                     esUtil.addData(mapObj,index,"game",article.getId());
             }
        }
        return Result.ok();
    }

    @Override
    public Result deleteDataById(String id) {
        esUtil.deleteDataById("article", "game", id);
        return Result.ok();
    }

    /**
       QueryBuilder:定義了查詢條件(是全部查詢 還是模糊查詢 還是分頁查詢。。。。)
        size:所要查詢出的條數
        field:所查詢的字段(如果查詢所有就直接寫null)
        sortField:id,age...(根據字段進行排序,如果不需要設置則傳null)
        highlightField:把搜索關鍵字進行高亮顯示(如果不需要則傳null)
     **/
    @Override
    public List<Map<String, Object>> selectAll() {
        //1、創建QueryBuilder對象(BoolQueryBuilder是Builder的實現類)
        BoolQueryBuilder boolQueryBuilder= QueryBuilders.boolQuery();
        //2、創建所要搜索到額條件(查詢所有數據)
        MatchAllQueryBuilder matchAllQueryBuilder= QueryBuilders.matchAllQuery();
        //3、把搜索的條件放入到BoolQueryBuilder中
        BoolQueryBuilder must = boolQueryBuilder.must(matchAllQueryBuilder);
        //4、返回
        return esUtil.searchListData("article","game",must,100,null,null,null);
    }

    @Override
    public Map<String, Object> selectOneById(String id) {
        return esUtil.searchDataById("article","game",id,null);
    }

    @Override
    public List<Map<String, Object>> selectLikeAll(String title) {
        //1、創建QueryBuilder對象
         BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
         //2、創建查詢條件
         //  matchPhraseQuery有兩個參數:
             //name:字段名字
             //text:所需要模糊匹配的值(也就是SQL語句中like後面所匹配的值)
        //        MatchPhraseQueryBuilder matchPhraseQueryBuilder=QueryBuilders.matchPhraseQuery("username","zhang");
         MatchPhraseQueryBuilder matchPhraseQueryBuilder=QueryBuilders.matchPhraseQuery("title",title);
         //3、把查詢條件放到BoolQueryBuilder對象中
         BoolQueryBuilder must=boolQueryBuilder.must(matchPhraseQueryBuilder);
         return esUtil.searchListData("article","game",must,10,null,null,"title");

    }

controller層:

@RestController
@RequestMapping("/es")
public class TestEsController {

    @Autowired
    private TestEsService testEsService;


    @PostMapping("/createIndex/{index}")
    public Result createIndex(@PathVariable String index) {
        testEsService.createIndex(index);
        return Result.ok();
    }

    @DeleteMapping("/deleteIndex/{index}")
    public Result deleteIndex(@PathVariable String index) {
        testEsService.deleteIndex(index);
        return Result.ok();
    }

    @PostMapping("/data")
    public Result addData() {
        testEsService.addData();
        return Result.ok();
    }

    @PostMapping("/data/{articleId}")
    public Result addDataBySql(@PathVariable String articleId) {
        testEsService.addDataBySql(articleId);
        return Result.ok();
    }

    @PostMapping("/data/all/{index}")
    public Result addAllArticleToIndex(@PathVariable String index) {
        testEsService.addAllData(index);
        return Result.ok();
    }

    @DeleteMapping("/delete/{articleId}")
    public Result deleteDataById(@PathVariable String articleId) {
        testEsService.deleteDataById(articleId);
        return Result.ok();
    }

  
    @GetMapping("/select")
    public Result selectAll() {
        List<Map<String, Object>> maps = testEsService.selectAll();
        return Result.ok(maps);
    }

    @GetMapping("/select/{articleId}")
    public Result selectByOne(@PathVariable String articleId) {
        Map<String, Object> map = testEsService.selectOneById(articleId);
        return Result.ok(map);
    }

    @GetMapping("/select/like/{title}")
    public Result selectLike(@PathVariable String title) {
        List<Map<String, Object>> maps = testEsService.selectLikeAll(title);
        return Result.ok(maps);
    }
}

最後在postman測試

先創建索引,我之前已經創建過了..

加入數據

搜索文章.

 

待優化的問題

1.es 7.x已經移除了type,封裝esutil的工具包依舊有用type,待更新

2. es依賴的包中提供的TransportClient,將在未來es 8.x移除

3.可採用springboot提供的es簡便方式

<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

類似Jpa的操作,和封裝es提供的接口,各有利弊....

參考了無數大佬的文章,才終於爬出一個個坑.....

end.

 

 

 


 
發佈了6 篇原創文章 · 獲贊 0 · 訪問量 1457
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章