<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 集成 Elasticsearch 實(shí)戰(zhàn)

          共 9377字,需瀏覽 19分鐘

           ·

          2020-07-27 19:02

          今天講解下如何使用 Spring Boot 結(jié)合 ES。
          可以在 ES 官方文檔中發(fā)現(xiàn),ES 為 Java REST Client 提供了兩種方式的 Client:Java Low Level Client 和 Java High Level REST Client。
          低級(jí)別客戶端,它允許通過(guò) HTTP 請(qǐng)求與 ES 集群進(jìn)行通信,API 本身不負(fù)責(zé)數(shù)據(jù)的編碼解碼,由用戶去編碼解碼,它與所有的 ES 版本兼容。
          高級(jí)客戶端基于低級(jí)客戶端,是從 6.0 才開(kāi)始加入的,主要目標(biāo)是為了暴露各 API 特定的方法,高版本客戶端依賴于 ES 核心項(xiàng)目,將 Request 對(duì)象作為參數(shù),返回一個(gè) Response 對(duì)象,所有 API 都可以同步或異步調(diào)用。
          本文就通過(guò) Spring Boot 結(jié)合 Java High Level REST Client 來(lái)進(jìn)行一些演示。

          Spring Boot 集成 ES

          Spring Boot 集成 ES 主要分為以下三步:
          1. 加入 ES 依賴
          2. 配置 ES
          3. 演示 ES 基本操作

          加入依賴

          首先創(chuàng)建一個(gè)項(xiàng)目,在項(xiàng)目中加入 ES 相關(guān)依賴,具體依賴如下所示:


          ????org.elasticsearch
          ????elasticsearch
          ????7.1.0


          ????org.elasticsearch.client
          ????elasticsearch-rest-high-level-client
          ????7.1.0

          創(chuàng)建 ES 配置

          在配置文件 application.properties 中配置 ES 的相關(guān)參數(shù),具體內(nèi)容如下:

          elasticsearch.host=localhost
          elasticsearch.port=9200
          elasticsearch.connTimeout=3000
          elasticsearch.socketTimeout=5000
          elasticsearch.connectionRequestTimeout=500

          其中指定了 ES 的 host 和端口以及超時(shí)時(shí)間的設(shè)置,另外我們的 ES 沒(méi)有添加任何的安全認(rèn)證,因此 username 和 password 就沒(méi)有設(shè)置。
          然后在 config 包下創(chuàng)建 ElasticsearchConfiguration 類,會(huì)從配置文件中讀取到對(duì)應(yīng)的參數(shù),接著申明一個(gè) initRestClient 方法,返回的是一個(gè) RestHighLevelClient,同時(shí)為它添加 @Bean(destroyMethod = "close") 注解,當(dāng) destroy 的時(shí)候做一個(gè)關(guān)閉,這個(gè)方法主要是如何初始化并創(chuàng)建一個(gè) RestHighLevelClient

          @Configuration
          public?class?ElasticsearchConfiguration?{

          ????@Value("${elasticsearch.host}")
          ????private?String?host;

          ????@Value("${elasticsearch.port}")
          ????private?int?port;

          ????@Value("${elasticsearch.connTimeout}")
          ????private?int?connTimeout;

          ????@Value("${elasticsearch.socketTimeout}")
          ????private?int?socketTimeout;

          ????@Value("${elasticsearch.connectionRequestTimeout}")
          ????private?int?connectionRequestTimeout;

          ????@Bean(destroyMethod?=?"close",?name?=?"client")
          ????public?RestHighLevelClient?initRestClient()?{
          ????????RestClientBuilder?builder?=?RestClient.builder(new?HttpHost(host,?port))
          ????????????????.setRequestConfigCallback(requestConfigBuilder?->?requestConfigBuilder
          ????????????????????????.setConnectTimeout(connTimeout)
          ????????????????????????.setSocketTimeout(socketTimeout)
          ????????????????????????.setConnectionRequestTimeout(connectionRequestTimeout));
          ????????return?new?RestHighLevelClient(builder);
          ????}
          }

          定義文檔實(shí)體類

          首先在 constant 包下定義常量接口,在接口中定義索引的名字為 user

          public?interface?Constant?{
          ????String?INDEX?=?"user";
          }

          然后在 document 包下創(chuàng)建一個(gè)文檔實(shí)體類:

          public?class?UserDocument?{
          ????private?String?id;
          ????private?String?name;
          ????private?String?sex;
          ????private?Integer?age;
          ????private?String?city;
          ????//?省略?getter/setter
          }

          ES 基本操作

          在這里主要介紹 ES 的索引、文檔、搜索相關(guān)的簡(jiǎn)單操作,在 service 包下創(chuàng)建 UserService 類。

          索引操作

          在這里演示創(chuàng)建索引和刪除索引:
          創(chuàng)建索引
          在創(chuàng)建索引的時(shí)候可以在 CreateIndexRequest 中設(shè)置索引名稱、分片數(shù)、副本數(shù)以及 mappings,在這里索引名稱為 user,分片數(shù) number_of_shards 為 1,副本數(shù) number_of_replicas 為 0,具體代碼如下所示:

          public?boolean?createUserIndex(String?index)?throws?IOException?{
          ????CreateIndexRequest?createIndexRequest?=?new?CreateIndexRequest(index);
          ????createIndexRequest.settings(Settings.builder()
          ????????????.put("index.number_of_shards",?1)
          ????????????.put("index.number_of_replicas",?0)
          ????);
          ????createIndexRequest.mapping("{\n"?+
          ????????????"??\"properties\":?{\n"?+
          ????????????"????\"city\":?{\n"?+
          ????????????"??????\"type\":?\"keyword\"\n"?+
          ????????????"????},\n"?+
          ????????????"????\"sex\":?{\n"?+
          ????????????"??????\"type\":?\"keyword\"\n"?+
          ????????????"????},\n"?+
          ????????????"????\"name\":?{\n"?+
          ????????????"??????\"type\":?\"keyword\"\n"?+
          ????????????"????},\n"?+
          ????????????"????\"id\":?{\n"?+
          ????????????"??????\"type\":?\"keyword\"\n"?+
          ????????????"????},\n"?+
          ????????????"????\"age\":?{\n"?+
          ????????????"??????\"type\":?\"integer\"\n"?+
          ????????????"????}\n"?+
          ????????????"??}\n"?+
          ????????????"}",?XContentType.JSON);
          ????CreateIndexResponse?createIndexResponse?=?client.indices().create(createIndexRequest,?RequestOptions.DEFAULT);
          ????return?createIndexResponse.isAcknowledged();
          }

          通過(guò)調(diào)用該方法,就可以創(chuàng)建一個(gè)索引 user,索引信息如下:
          刪除索引
          DeleteIndexRequest 中傳入索引名稱就可以刪除索引,具體代碼如下所示:

          public?Boolean?deleteUserIndex(String?index)?throws?IOException?{
          ????DeleteIndexRequest?deleteIndexRequest?=?new?DeleteIndexRequest(index);
          ????AcknowledgedResponse?deleteIndexResponse?=?client.indices().delete(deleteIndexRequest,?RequestOptions.DEFAULT);
          ????return?deleteIndexResponse.isAcknowledged();
          }

          介紹完索引的基本操作,下面介紹文檔的相關(guān)操作:

          文檔操作

          在這里演示下創(chuàng)建文檔、批量創(chuàng)建文檔、查看文檔、更新文檔以及刪除文檔:
          創(chuàng)建文檔
          創(chuàng)建文檔的時(shí)候需要在 IndexRequest 中指定索引名稱,id 如果不傳的話會(huì)由 ES 自動(dòng)生成,然后傳入 source,具體代碼如下:

          public?Boolean?createUserDocument(UserDocument?document)?throws?Exception?{
          ????UUID?uuid?=?UUID.randomUUID();
          ????document.setId(uuid.toString());
          ????IndexRequest?indexRequest?=?new?IndexRequest(Constant.INDEX)
          ????????????.id(document.getId())
          ????????????.source(JSON.toJSONString(document),?XContentType.JSON);
          ????IndexResponse?indexResponse?=?client.index(indexRequest,?RequestOptions.DEFAULT);
          ????return?indexResponse.status().equals(RestStatus.OK);
          }

          下面通過(guò)調(diào)用這個(gè)方法,創(chuàng)建兩個(gè)文檔,具體內(nèi)容如下:
          批量創(chuàng)建文檔
          在一個(gè) REST 請(qǐng)求中,重新建立網(wǎng)絡(luò)開(kāi)銷是十分損耗性能的,因此 ES 提供 Bulk API,支持在一次 API 調(diào)用中,對(duì)不同的索引進(jìn)行操作,從而減少網(wǎng)絡(luò)傳輸開(kāi)銷,提升寫(xiě)入速率。
          下面方法是批量創(chuàng)建文檔,一個(gè) BulkRequest 里可以添加多個(gè) Request,具體代碼如下:

          public?Boolean?bulkCreateUserDocument(List?documents)?throws?IOException?{
          ????BulkRequest?bulkRequest?=?new?BulkRequest();
          ????for?(UserDocument?document?:?documents)?{
          ????????String?id?=?UUID.randomUUID().toString();
          ????????document.setId(id);
          ????????IndexRequest?indexRequest?=?new?IndexRequest(Constant.INDEX)
          ????????????????.id(id)
          ????????????????.source(JSON.toJSONString(document),?XContentType.JSON);
          ????????bulkRequest.add(indexRequest);
          ????}
          ????BulkResponse?bulkResponse?=?client.bulk(bulkRequest,?RequestOptions.DEFAULT);
          ????return?bulkResponse.status().equals(RestStatus.OK);
          }

          下面通過(guò)該方法創(chuàng)建些文檔,便于下面的搜索演示。
          查看文檔
          查看文檔需要在 GetRequest 中傳入索引名稱和文檔 id,具體代碼如下所示:

          public?UserDocument?getUserDocument(String?id)?throws?IOException?{
          ????GetRequest?getRequest?=?new?GetRequest(Constant.INDEX,?id);
          ????GetResponse?getResponse?=?client.get(getRequest,?RequestOptions.DEFAULT);
          ????UserDocument?result?=?new?UserDocument();
          ????if?(getResponse.isExists())?{
          ????????String?sourceAsString?=?getResponse.getSourceAsString();
          ????????result?=?JSON.parseObject(sourceAsString,?UserDocument.class);
          ????}?else?{
          ????????logger.error("沒(méi)有找到該?id?的文檔");
          ????}
          ????return?result;
          }

          下面?zhèn)魅胛臋n id 調(diào)用該方法,結(jié)果如下所示:
          更新文檔
          更新文檔則是先給 UpdateRequest 傳入索引名稱和文檔 id,然后通過(guò)傳入新的 doc 來(lái)進(jìn)行更新,具體代碼如下:

          public?Boolean?updateUserDocument(UserDocument?document)?throws?Exception?{
          ????UserDocument?resultDocument?=?getUserDocument(document.getId());
          ????UpdateRequest?updateRequest?=?new?UpdateRequest(Constant.INDEX,?resultDocument.getId());
          ????updateRequest.doc(JSON.toJSONString(document),?XContentType.JSON);
          ????UpdateResponse?updateResponse?=?client.update(updateRequest,?RequestOptions.DEFAULT);
          ????return?updateResponse.status().equals(RestStatus.OK);
          }

          下面將文檔 id 為 9b8d9897-3352-4ef3-9636-afc6fce43b20 的文檔的城市信息改為 handan,調(diào)用方法結(jié)果如下:
          刪除文檔
          刪除文檔只需要在 DeleteRequest 中傳入索引名稱和文檔 id,然后執(zhí)行 delete 方法就可以完成文檔的刪除,具體代碼如下:

          public?String?deleteUserDocument(String?id)?throws?Exception?{
          ????DeleteRequest?deleteRequest?=?new?DeleteRequest(Constant.INDEX,?id);
          ????DeleteResponse?response?=?client.delete(deleteRequest,?RequestOptions.DEFAULT);
          ????return?response.getResult().name();
          }

          介紹完文檔的基本操作,接下來(lái)對(duì)搜索進(jìn)行簡(jiǎn)單介紹:

          搜索操作

          簡(jiǎn)單的搜索操作需要在 SearchRequest 中設(shè)置將要搜索的索引名稱(可以設(shè)置多個(gè)索引名稱),然后通過(guò) SearchSourceBuilder 構(gòu)造搜索源,下面將 TermQueryBuilder 搜索查詢傳給 searchSourceBuilder,最后將 searchRequest 的搜索源設(shè)置為 searchSourceBuilder,執(zhí)行 search 方法實(shí)現(xiàn)通過(guò)城市進(jìn)行搜索,具體代碼如下所示:

          public?List?searchUserByCity(String?city)?throws?Exception?{
          ????SearchRequest?searchRequest?=?new?SearchRequest();
          ????searchRequest.indices(Constant.INDEX);
          ????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();
          ????TermQueryBuilder?termQueryBuilder?=?QueryBuilders.termQuery("city",?city);
          ????searchSourceBuilder.query(termQueryBuilder);
          ????searchRequest.source(searchSourceBuilder);
          ????SearchResponse?searchResponse?=?client.search(searchRequest,?RequestOptions.DEFAULT);
          ????return?getSearchResult(searchResponse);
          }

          該方法的執(zhí)行結(jié)果如圖所示:
          聚合搜索
          聚合搜索就是給 searchSourceBuilder 添加聚合搜索,下面方法是通過(guò) TermsAggregationBuilder 構(gòu)造一個(gè)先通過(guò)城市就行分類聚合,其中還包括一個(gè)子聚合,是對(duì)年齡求平均值,然后在獲取聚合結(jié)果的時(shí)候,可以使用通過(guò)在構(gòu)建聚合時(shí)的聚合名稱獲取到聚合結(jié)果,具體代碼如下所示:

          public?List?aggregationsSearchUser()?throws?Exception?{
          ????SearchRequest?searchRequest?=?new?SearchRequest(Constant.INDEX);
          ????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();
          ????TermsAggregationBuilder?aggregation?=?AggregationBuilders.terms("by_city")
          ????????????.field("city")
          ????????????.subAggregation(AggregationBuilders
          ????????????????????.avg("average_age")
          ????????????????????.field("age"));
          ????searchSourceBuilder.aggregation(aggregation);
          ????searchRequest.source(searchSourceBuilder);
          ????SearchResponse?searchResponse?=?client.search(searchRequest,?RequestOptions.DEFAULT);
          ????Aggregations?aggregations?=?searchResponse.getAggregations();
          ????Terms?byCityAggregation?=?aggregations.get("by_city");
          ????List?userCityList?=?new?ArrayList<>();
          ????for?(Terms.Bucket?buck?:?byCityAggregation.getBuckets())?{
          ????????UserCityDTO?userCityDTO?=?new?UserCityDTO();
          ????????userCityDTO.setCity(buck.getKeyAsString());
          ????????userCityDTO.setCount(buck.getDocCount());
          ????????//?獲取子聚合
          ????????Avg?averageBalance?=?buck.getAggregations().get("average_age");
          ????????userCityDTO.setAvgAge(averageBalance.getValue());
          ????????userCityList.add(userCityDTO);
          ????}
          ????return?userCityList;
          }

          下面是執(zhí)行該方法的結(jié)果:
          到此為止,ES 的基本操作就簡(jiǎn)單介紹完了,大家可以多動(dòng)手試試,不會(huì)的可以看下官方文檔。

          總結(jié)

          本文的完整代碼在 https://github.com/wupeixuan/SpringBoot-Learnelasticsearch 目錄下。
          Spring Boot 結(jié)合 ES 還是比較簡(jiǎn)單的,大家可以下載項(xiàng)目源碼,自己在本地運(yùn)行調(diào)試這個(gè)項(xiàng)目,更好地理解如何在 Spring Boot 中構(gòu)建基于 ES 的應(yīng)用。

          有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美日韩一区 | 无码精品一区二区三区同学聚会 | 婷婷99爱99热 | 99视频在线精品 | 中文字幕精品无码网站人口 |