ElasticSearch最新搞定版

漂吧發表於2020-04-09

ElasticSearch概述

Lucene 和 ElasticSearch 關係:

ElasticSearch 是基於 Lucene 做了一些封裝和增強

Elaticsearch,簡稱為es, es是一個開源的高擴充套件的分散式全文檢索引擎,它可以近乎實時的儲存、檢 索資料;本身擴充套件性很好,可以擴充套件到上百臺伺服器,處理PB級別(大資料時代)的資料。es也使用 Java開發並使用Lucene作為其核心來實現所有索引和搜尋的功能,但是它的目的是通過簡單的RESTful API來隱藏Lucene的複雜性,從而讓全文搜尋變得簡單。

用途

1、維基百科,類似百度百科,全文檢索,高亮,搜尋推薦/2 (權重,百度!)

2、The Guardian(國外新聞網站),類似搜狐新聞,使用者行為日誌(點選,瀏覽,收藏,評論)+社交 網路資料(對某某新聞的相關看法),資料分析,給到每篇新聞文章的作者,讓他知道他的文章的公眾 反饋(好,壞,熱門,垃圾,鄙視,崇拜)

3、Stack Overflow(國外的程式異常討論論壇),IT問題,程式的報錯,提交上去,有人會跟你討論和 回答,全文檢索,搜尋相關問題和答案,程式報錯了,就會將報錯資訊貼上到裡面去,搜尋有沒有對應 的答案

4、GitHub(開原始碼管理),搜尋上千億行程式碼

5、電商網站,檢索商品

6、日誌資料分析,logstash採集日誌,ES進行復雜的資料分析,ELK技術, elasticsearch+logstash+kibana

7、商品價格監控網站,使用者設定某商品的價格閾值,當低於該閾值的時候,傳送通知訊息給使用者,比如 說訂閱牙膏的監控,如果高露潔牙膏的家庭套裝低於50塊錢,就通知我,我就去買。

8、BI系統,商業智慧,Business Intelligence。比如說有個大型商場集團,BI,分析一下某某區域最近 3年的使用者消費金額的趨勢以及使用者群體的組成構成,產出相關的數張報表,**區,最近3年,每年消費 金額呈現100%的增長,而且使用者群體85%是高階白領,開一個新商場。ES執行資料分析和挖掘, Kibana進行資料視覺化

9、國內:站內搜尋(電商,招聘,門戶,等等),IT系統搜尋(OA,CRM,ERP,等等),資料分析 (ES熱門 的一個使用場景)

ES和solr的差別

Lucene簡介

Lucene是apache軟體基金會4 jakarta專案組的一個子專案,是一個開放原始碼的全文檢索引擎工具 包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引 引擎,部分文字分析引擎(英文與德文兩種西方語言)。Lucene的目的是為軟體開發人員提供一個簡單 易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此為基礎建立起完整的全文檢索 引擎。Lucene是一套用於全文檢索和搜尋的開源程式庫,由Apache軟體基金會支援和提供。Lucene提 供了一個簡單卻強大的應用程式介面,能夠做全文索引和搜尋。在Java開發環境裡Lucene是一個成熟的 免費開源工具。就其本身而言,Lucene是當前以及最近幾年最受歡迎的免費Java資訊檢索程式庫。人們 經常提到資訊檢索程式庫,雖然與搜尋引擎有關,但不應該將資訊檢索程式庫與搜尋引擎相混淆。

Lucene是一個全文檢索引擎的架構。那什麼是全文搜尋引擎? 全文搜尋引擎是名副其實的搜尋引擎,國外具代表性的有Google、Fast/AllTheWeb、AltaVista、 Inktomi、Teoma、WiseNut等,國內著名的有百度(Baidu)。它們都是通過從網際網路上提取的各個網 站的資訊(以網頁文字為主)而建立的資料庫中,檢索與使用者查詢條件匹配的相關記錄,然後按一定的 排列順序將結果返回給使用者,因此他們是真正的搜尋引擎。

從搜尋結果來源的角度,全文搜尋引擎又可細分為兩種,一種是擁有自己的檢索程式(Indexer),俗稱 “蜘蛛”(Spider)程式或“機器人”(Robot)程式,並自建網頁資料庫,搜尋結果直接從自身的資料庫中 呼叫,如上面提到的7家引擎;另一種則是租用其他引擎的資料庫,並按自定的格式排列搜尋結果,如 Lycos引擎。

比較

1、es基本是開箱即用(解壓就可以用 ! ),非常簡單。Solr安裝略微複雜一丟丟!

2、Solr 利用 Zookeeper 進行分散式管理,而 Elasticsearch 自身帶有分散式協調管理功能。

3、Solr 支援更多格式的資料,比如JSON、XML、CSV,而 Elasticsearch 僅支援json檔案格式

4、Solr 官方提供的功能更多,而 Elasticsearch 本身更注重於核心功能,高階功能多有第三方外掛提 供,例如圖形化介面需要kibana友好支撐~!

5、Solr 查詢快,但更新索引時慢(即插入刪除慢),用於電商等查詢多的應用;

  • ES建立索引快(即查詢慢),即實時性查詢快,用於facebook新浪等搜尋。
  • Solr 是傳統搜尋應用的有力解決方案,但 Elasticsearch 更適用於新興的實時搜尋應用。

6、Solr比較成熟,有一個更大,更成熟的使用者、開發和貢獻者社群,而 Elasticsearch相對開發維護者較少,更新太快,學習使用成本較高。(趨勢!)

ElasticSearch安裝

宣告:JDK1.8 ,最低要求! ElasticSearch 客戶端,介面工具!

1、下載

官網

下載地址

2、熟悉目錄

bin 啟動檔案
config 配置檔案
    log4j2 日誌配置檔案
    jvm.options java 虛擬機器相關的配置
    elasticsearch.yml elasticsearch 的配置檔案! 預設 9200 埠! 跨域!
lib 相關jar包
logs 日誌!
modules 功能模組
plugins 外掛!

3、啟動,訪問9200

4、安裝視覺化外掛

安裝視覺化介面 es head的外掛

1、下載地址

2、啟動

npm install
npm run start

3、連線測試發現,存在跨域問題:配置es

http.cors.enabled: true
http.cors.allow-origin: "*"

4、重啟es伺服器,然後再次連線

就把es當做一個資料庫! (可以建立索引(庫),文件(庫中的資料!)) 這個head我們就把它當做資料展示工具!我們後面所有的查詢,Kibana!

瞭解 ELK

ELK是Elasticsearch、Logstash、Kibana三大開源框架首字母大寫簡稱。市面上也被成為Elastic Stack。其中Elasticsearch是一個基於Lucene、分散式、通過Restful方式進行互動的近實時搜尋平臺框 架。像類似百度、谷歌這種大資料全文搜尋引擎的場景都可以使用Elasticsearch作為底層支援框架,可見Elasticsearch提供的搜尋能力確實強大,市面上很多時候我們簡稱Elasticsearch為es。

Logstash是ELK 的中央資料流引擎,用於從不同目標(檔案/資料儲存/MQ)收集的不同格式資料,經過過濾後支援輸出 到不同目的地(檔案/MQ/redis/elasticsearch/kafka等)。Kibana可以將elasticsearch的資料通過友好 的頁面展示出來,提供實時分析的功能。

市面上很多開發只要提到ELK能夠一致說出它是一個日誌分析架構技術棧總稱,但實際上ELK不僅僅適用 於日誌分析,它還可以支援其它任何資料分析和收集的場景,日誌分析和收集只是更具有代表性。並非 唯一性。

在這裡插入圖片描述

官網

Kibana 版本要和 Es 一致!

啟動測試:

1、解壓後端的目錄

2、啟動

3、訪問測試

4、開發工具! (Post、curl、head、谷歌瀏覽器外掛測試!)

5、漢化!自己修改kibana配置即可! zh-CN!

ES核心概念

1、索引

2、欄位型別(mapping)

3、文件(documents)

elasticsearch是面向文件,關係行資料庫 和 elasticsearch 客觀的對比!一切都是JSON

Relational DBElasticsearch
資料庫(database)索引(indices)
表(tables)types
行(rows)documents
欄位(columns)fields

elasticsearch(叢集)中可以包含多個索引(資料庫),每個索引中可以包含多個型別(表),每個型別下又包 含多 個文件(行),每個文件中又包含多個欄位(列)。

物理設計:

elasticsearch 在後臺把每個索引劃分成多個分片,每分分片可以在叢集中的不同伺服器間遷移 一個人就是一個叢集!預設的叢集名稱就是 elaticsearh

邏輯設計:

一個索引型別中,包含多個文件,比如說文件1,文件2。 當我們索引一篇文件時,可以通過這樣的一各 順序找到 它: 索引 ▷ 型別 ▷ 文件ID ,通過這個組合我們就能索引到某個具體的文件。 注意:ID不必是整 數,實際上它是個字 符串。

文件

就是我們的一條資料

之前說elasticsearch是面向文件的,那麼就意味著索引和搜尋資料的最小單位是文件,elasticsearch 中,文件有幾個 重要屬性 :

  • 自我包含,一篇文件同時包含欄位和對應的值,也就是同時包含 key:value!
  • 可以是層次型的,一個文件中包含自文件,複雜的邏輯實體就是這麼來的! {就是一個json物件! fastjson進行自動轉換!}
  • 靈活的結構,文件不依賴預先定義的模式,我們知道關係型資料庫中,要提前定義欄位才能使用, 在elasticsearch中,對於欄位是非常靈活的,有時候,我們可以忽略該欄位,或者動態的新增一個 新的欄位。

儘管我們可以隨意的新增或者忽略某個欄位,但是,每個欄位的型別非常重要,比如一個年齡欄位類 型,可以是字元 串也可以是整形。因為elasticsearch會儲存欄位和型別之間的對映及其他的設定。這種 對映具體到每個對映的每種型別,這也是為什麼在elasticsearch中,型別有時候也稱為對映型別 。

型別

型別是文件的邏輯容器,就像關係型資料庫一樣,表格是行的容器。 型別中對於欄位的定義稱為對映, 比如 name 映 射為字串型別。 我們說文件是無模式的,它們不需要擁有對映中所定義的所有欄位, 比如新增一個欄位,那麼elasticsearch是怎麼做的呢?elasticsearch會自動的將新欄位加入對映,但是這 個欄位的不確定它是什麼型別,elasticsearch就開始猜,如果這個值是18,那麼elasticsearch會認為它 是整形。 但是elasticsearch也可能猜不對, 所以最安全的方式就是提前定義好所需要的對映,這點跟關 系型資料庫殊途同歸了,先定義好欄位,然後再使用,別 整什麼么蛾子

索引

就是資料庫!

索引是對映型別的容器,elasticsearch中的索引是一個非常大的文件集合。索引儲存了對映型別的欄位 和其他設定。 然後它們被儲存到了各個分片上了。 我們來研究下分片是如何工作的。

一個叢集至少有一個節點,而一個節點就是一個elasricsearch程式,節點可以有多個索引預設的,如果你建立索引,那麼索引將會有個5個分片 ( primary shard ,又稱主分片 ) 構成的,每一個主分片會有一個 副本 ( replica shard ,又稱複製分片 )

倒排

索引 elasticsearch使用的是一種稱為倒排索引的結構,採用Lucene倒排索作為底層。這種結構適用於快速的全文搜尋, 一個索引由文件中所有不重複的列表構成,對於每一個詞,都有一個包含它的文件列表。 例 如,現在有兩個文件, 每個文件包含如下內容:

Study every day, good good up to forever # 文件1包含的內容
To forever, study every day, good good up # 文件2包含的內容

為了建立倒排索引,我們首先要將每個文件拆分成獨立的詞(或稱為詞條或者tokens),然後建立一個包含所有不重 復的詞條的排序列表,然後列出每個詞條出現在哪個文件

termdoc_1doc_2
Study×
Toxx
every
foreve
day×
study
good
every
to×
up

現在,我們試圖搜尋 to forever,只需要檢視包含每個詞條的文件 score

termdoc_1doc_2
to×
forever
total21

如果沒有別的條件,現在,這兩個包含關鍵字的文件都將返回。

在elasticsearch中, 索引 (庫)這個詞被頻繁使用,這就是術語的使用。 在elasticsearch中,索引被 分為多個分片,每份 分片是一個Lucene的索引。所以一個elasticsearch索引是由多個Lucene索引組成 的。別問為什麼,誰讓elasticsearch使用Lucene作為底層呢! 如無特指,說起索引都是指elasticsearch 的索引。

IK分詞器外掛

什麼是IK分詞器?

分詞:即把一段中文或者別的劃分成一個個的關鍵字,我們在搜尋時候會把自己的資訊進行分詞,會把資料庫中或者索引庫中的資料進行分詞,然後進行一個匹配操作,預設的中文分詞是將每個字看成一個 詞,比如 “我愛狂神” 會被分為"我",“愛”,“狂”,“神”,這顯然是不符合要求的,所以我們需要安裝中文分詞 器ik來解決這個問題。

如果要使用中文,建議使用ik分詞器!

IK提供了兩個分詞演算法:ik_smart 和 ik_max_word,其中 ik_smart 為最少切分,ik_max_word為最細 粒度劃分!

安裝

1、https://github.com/medcl/elasticsearch-analysis-ik

2、下載完畢之後,放入到我們的elasticsearch 外掛即可!

3、重啟觀察ES,可以看到ik分詞器被載入了!

4、elasticsearch-plugin 可以通過這個命令來檢視載入進來的外掛

5、使用kibana測試!

安裝外掛錯誤

Plugin [analysis-pinyin] was built for Elasticsearch version 7.6.1 but versin 7.6.2 is running

解決辦法

進入 plugin的descriptor.properties

修改 elasticsearch.version為你自己的版本

檢視不同的分詞效果

GET  _analyze
{
  "analyzer": "ik_smart",
  "text": "中國共產黨"
}

GET  _analyze
{
  "analyzer": "ik_max_word",
  "text": "中國共產黨"
}

其中 ik_smart 為最少切分

ik_max_word為最細粒度劃分!窮盡詞庫的可能!字典!

ik 分詞器增加自己的配置!

有可能詞庫不認得單詞才分成一個一個的字

沒有成功

Rest風格說明

一種軟體架構風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和伺服器交 互類的軟體。基於這個風格設計的軟體可以更簡潔,更有層次,更易於實現快取等機制。

基本Rest命令說明:

methodurl地址描述
PUTlocalhost:9200/索引名稱/型別名稱/文件id建立文件(指定文件id)
POSTlocalhost:9200/索引名稱/型別名稱建立文件(隨機文件id)
_updatelocalhost:9200/索引名稱/型別名稱/文件id/_update修改文件
DELETElocalhost:9200/索引名稱/型別名稱/文件id刪除文件
GETlocalhost:9200/索引名稱/型別名稱/文件id查詢文件通過文件id
POSTlocalhost:9200/索引名稱/型別名稱/_search查詢所有資料

關於索引的基本操作

1、建立一個索引

PUT /索引名/~型別名~/文件id
{請求體}

在這裡插入圖片描述

3、需要指定型別的啊 !

字串型別

text 、 keyword

數值型別

long, integer, short, byte, double, float, half_float, scaled_float

日期型別

te布林值型別

boolean

二進位制型別

binary 等等…

增刪改查

PUT /test1/type1/1
{
  "name":"狂神說",
   "age": 3
} 


PUT  /test2
{
  "mappings":{
    "properties" :{
      "name":{
        "type" : "text"
      },
      "age":{
        "type":"long"
      },
      "birthday":{
        "type":"date"
      }
    }
  }
}

GET  test2

PUT  /test3/_doc/1
{
  "name":"陳玲琦",
  "age": 13,
  "birth" : "1994-05-14"
}
GET test3

GET  _cat/health

GET   _cat/indices?v

GET test3

POST  /test3/_doc/1/_update
{
  "doc":{
    "name":"法外狂徒張三"
  }
}
DELETE test1

DELETE test1/type1/1

關於文件的基本操作(重點)

基本操作

1、新增資料

PUT /kuangshen/user/1
{
"name": "狂神說",
"age": 23,
"desc": "一頓操作猛如虎,一看工資2500",
"tags": ["技術宅","溫暖","直男"]
}

2、獲取資料 GET

GET /kuangshen/user/1

3、更新資料 PUT

- 必須對應的值分別都需要傳值,不然會被覆蓋

4、 Post _update , 推薦使用這種更新方式!

POST  kuangshen/user/3/_update
{
  "doc":{
    "name":"狂神說java"
  }
}

簡單地搜尋!

GET kuangshen/user/1

_search

GET kuangshen/user/_search?q=name:狂神說java
GET kuangshen/user/_search
{
  "query":{
    "match":{
      "name":"狂神說"
    }
  }
}

結果:

"hits" : [
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "1",
        "_score" : 1.6594179,
        "_source" : {
          "name" : "狂神說",
          "age" : 23,
          "desc" : "一頓操作猛如虎,一看工資2500",
          "tags" : [
            "技術宅",
            "溫暖",
            "直男"
          ]
        }
      },

_source 進行對欄位進行過濾

GET kuangshen/user/_search
{
  "query":{
    "match":{
      "name":"狂神說"
    }
  },
   "_source":["name","age"]
}

sort 排序!

   "sort": [
     {
       "FIELD": {
         "order": "desc"或者asc
       }
     }
   ]

分頁查詢

"from":    起始值
"size":		大小
GET kuangshen/user/_search
{
  "query":{
    "match":{
      "name":"狂神說"
    }
  },
   "sort": [
     {
       "age": {
         "order": "desc"
       }
     }
   ],
   "from":0,
   "size": 1
}

must (and),所有的條件都要符合 where id = 1 and name = xxx

GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "狂神說"
          }
        },
        {
          "match": {
            "age": "30"
          }
        }
      ]
    }
  }
}

should(or),所有的條件都要符合 where id = 1 or name = xxx

must_not (not)

過濾器 filter

  • gt 大於
  • gte 大於等於
  • lt小於
  • lte 小於等於!
  "filter": [
        {"range": {
          "age": {
            "gte": 10,
            "lte": 30
          }
        }}
      ]

匹配多個條件!

  "query": {
    "match": {
      "tags": "男 暖"
    }
  }

term

查詢是直接通過倒排索引指定的詞條程式精確查詢的!

關於分詞:

  • term ,直接查詢精確的
  • match,會使用分詞器解析!(先分析文件,然後在通過分析的文件進行查詢!)

兩個型別 text keyword

高亮查詢!

GET kuangshen/user/_search
{
  "query": {
    "match": {
      "name":"java" 
    }
  },
  "highlight": {
    "fields": {
      "name":{}
    }
  }
}

自定義搜尋高亮

  "highlight": {
    "pre_tags": "<p class='key' style='color:red' >",
    "post_tags": "</p>", 
    "fields": {
      "name":{}
    }
  • 匹配
  • 按照條件匹配
  • 精確匹配
  • 區間範圍匹配
  • 匹配欄位過濾
  • 多條件查詢
  • 高亮查詢

整合SpringBoot

學習文件

API地址

1、找到原生的依賴

<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency

2、找物件

RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(
                new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")));


client.close();

3、分析這個類中的方法即可!

問題:一定要保證 我們的匯入的依賴和我們的es 版本一致

在這裡插入圖片描述

<elasticsearch.version>7.6.2</elasticsearch.version>

客戶端自動配置的類

ElasticsearchAutoConfiguration.class,
RestClientAutoConfiguration.class, 
ReactiveRestClientAutoConfiguration.class

配置的類

class RestClientConfigurations {
    @Configuration(proxyBeanMethods = false)
    static class RestClientBuilderConfiguration {
        // RestClientBuilder
        @Bean
        @ConditionalOnMissingBean
        RestClientBuilder elasticsearchRestClientBuilder(RestClientProperties
                                                                 properties,
                                                         ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
            HttpHost[] hosts =
                    properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new);
            RestClientBuilder builder = RestClient.builder(hosts);
            PropertyMapper map = PropertyMapper.get();
            map.from(properties::getUsername).whenHasText().to((username) -> {
                CredentialsProvider credentialsProvider = new
                        BasicCredentialsProvider();
                Credentials credentials = new
                        UsernamePasswordCredentials(properties.getUsername(),
                        properties.getPassword());
                credentialsProvider.setCredentials(AuthScope.ANY, credentials);
                builder.setHttpClientConfigCallback(
                        (httpClientBuilder) ->
                                httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
            });
            builder.setRequestConfigCallback((requestConfigBuilder) -> {
                map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMill
                        is)
                        .to(requestConfigBuilder::setConnectTimeout);
                map.from(properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
                        .to(requestConfigBuilder::setSocketTimeout);
                return requestConfigBuilder;
            });
            builderCustomizers.orderedStream().forEach((customizer) ->
                    customizer.customize(builder));
            return builder;
        }
    }
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RestHighLevelClient.class)
    static class RestHighLevelClientConfiguration {
        // RestHighLevelClient 高階客戶端,也是我們這裡要講,後面專案會用到的客戶端
        @Bean
        @ConditionalOnMissingBean
        RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder
                                                                     restClientBuilder) {
            return new RestHighLevelClient(restClientBuilder);
        }
        @Bean
        @ConditionalOnMissingBean
        RestClient elasticsearchRestClient(RestClientBuilder builder,
                                           ObjectProvider<RestHighLevelClient> restHighLevelClient) {
            RestHighLevelClient client = restHighLevelClient.getIfUnique();
            if (client != null) {
                return client.getLowLevelClient();
            }
            return builder.build();
        }
    }
    @Configuration(proxyBeanMethods = false)
    static class RestClientFallbackConfiguration {
        // RestClient 普通的客戶端!
        @Bean
        @ConditionalOnMissingBean
        RestClient elasticsearchRestClient(RestClientBuilder builder) {
            return builder.build();
        }
    }
}

3

具體的Api測試!

  • 1、建立索引
  • 2、判斷索引是否存在
  • 3、刪除索引
  • 4、建立文件
  • 5、crud文件!
package com.kuang;

import com.alibaba.fastjson.JSON;
import com.kuang.pojo.User;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

@SpringBootTest
class ElasticApplicationTests {

    @Autowired
    @Qualifier(value = "restHighLevelClient")
    private RestHighLevelClient client;


    //測試建立索引
    @Test
    void testCreateIndex() throws IOException {
        //1、建立索引請求
        CreateIndexRequest request = new CreateIndexRequest("kuang_index");
        //2、客戶端執行請求 IndicesClient,請求後獲得響應
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(response);
    }

    //測試獲得索引,判斷是不是存在
    @Test
    void testExistIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest("kuang_index");
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

    //測試刪除索引
    @Test
    void testRemoveIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest("kuang_index");
        AcknowledgedResponse exists = client.indices().delete(request, RequestOptions.DEFAULT);
        System.out.println(exists.isAcknowledged());
    }

    //測試新增文件
    @Test
    void testAddDocument() throws IOException {
        User user = new User("狂神說", 3);

        //獲取請求索引
        IndexRequest request = new IndexRequest("kuang_index");
        //設定請求規則
        request.id("1");
        request.timeout(TimeValue.timeValueSeconds(1));
        request.timeout("1s");

        //將資料放到請求中
        request.source(JSON.toJSONString(user), XContentType.JSON);


        IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);

        System.out.println(indexResponse.toString());
        System.out.println(indexResponse.status());

    }

    // 獲取文件,判斷是否存在 get /index/doc/1
    @Test
    void testIsExists() throws IOException {
        GetRequest getRequest = new GetRequest("kuang_index", "1");
        // 不獲取返回的 _source 的上下文了
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

    // 獲得文件的資訊
    @Test
    void testGetDocument() throws IOException {
        GetRequest getRequest = new GetRequest("kuang_index", "1");
        GetResponse getResponse = client.get(getRequest,
                RequestOptions.DEFAULT);
        System.out.println(getResponse.getSourceAsString()); // 列印文件的內容
        System.out.println(getResponse); // 返回的全部內容和命令式一樣的
    }

    // 更新文件的資訊
    @Test
    void testUpdateRequest() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("kuang_index", "1");
        updateRequest.timeout("1s");
        User user = new User("狂神說Java", 18);
        updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
        UpdateResponse updateResponse = client.update(updateRequest,
                RequestOptions.DEFAULT);
        System.out.println(updateResponse.status());
    }

    // 刪除文件記錄
    @Test
    void testDeleteRequest() throws IOException {
        DeleteRequest request = new DeleteRequest("kuang_index", "1");
        request.timeout("1s");
        DeleteResponse deleteResponse = client.delete(request,
                RequestOptions.DEFAULT);
        System.out.println(deleteResponse.status());
    }
    // 特殊的,真的專案一般都會批量插入資料!
    @Test
    void testBulkRequest() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        ArrayList<User> userList = new ArrayList<>();
        userList.add(new User("kuangshen1",3));
        userList.add(new User("kuangshen2",3));
        userList.add(new User("kuangshen3",3));
        userList.add(new User("qinjiang1",3));
        userList.add(new User("qinjiang1",3));
        userList.add(new User("qinjiang1",3));
// 批處理請求
        for (int i = 0; i < userList.size() ; i++) {
// 批量更新和批量刪除,就在這裡修改對應的請求就可以了
            bulkRequest.add(
                    new IndexRequest("kuang_index")
                            .id(""+(i+1))
                            .source(JSON.toJSONString(userList.get(i)),XContentType.JSON));
        }
        BulkResponse bulkResponse = client.bulk(bulkRequest,
                RequestOptions.DEFAULT);
        client.close();
        System.out.println(bulkResponse.hasFailures()); // 是否失敗,返回 false 代表成功!
    }
    // 查詢
// SearchRequest 搜尋請求
// SearchSourceBuilder 條件構造
// HighlightBuilder 構建高亮
// TermQueryBuilder 精確查詢
// MatchAllQueryBuilder
// xxx QueryBuilder 對應我們剛才看到的命令!
    @Test
    void testSearch() throws IOException {
        SearchRequest searchRequest = new SearchRequest("kuang_index");
// 構建搜尋條件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.highlighter();
// 查詢條件,我們可以使用 QueryBuilders 工具來實現
// QueryBuilders.termQuery 精確
// QueryBuilders.matchAllQuery() 匹配所有
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name",
                "qinjiang1");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(termQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);
        
        System.out.println(JSON.toJSONString(searchResponse.getHits()));
        System.out.println("=================================");
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            System.out.println(documentFields.getSourceAsMap());
        }
    }


}

相關文章