ElasticSearch極簡入門總結

唯愛程式設計發表於2021-01-23

一,目錄

  • 安裝es

  • 專案新增maven依賴

  • es客戶端元件注入到spring容器中

  • es與mysql表結構對比

  • 索引的刪除建立

  • 文件的crud

  • es能快速搜尋的核心-倒排索引

  • 基於倒排索引的精確搜尋、全文搜尋(重點)

  • es叢集

二,二,安裝ES環境(安裝完成後,開啟ES,預設埠9200能訪問到,說明成功了)

安裝文章

三,Java專案中新增ES的maven依賴

1.<dependency>  
2.    <groupId>org.elasticsearch.client</groupId>  
3.    <artifactId>elasticsearch-rest-high-level-client</artifactId>  
4.    <version>7.10.2</version>  
</dependency> 

四,四,將ES客戶端元件注入到spring容器中(客戶端元件RestHighLevelClient,其封裝了操作es的crud方法,底層原理就是模擬各種es需要的請求)

1.package com.shuang.config;  
2.  
3.import org.apache.http.HttpHost;  
4.import org.elasticsearch.client.RestClient;  
5.import org.elasticsearch.client.RestHighLevelClient;  
6.import org.springframework.context.annotation.Bean;  
7.import org.springframework.context.annotation.Configuration;  
8.  
9.@Configuration //xml檔案-bean  
10.public class ElasticSearchClientConfig {  
11.  
12.    @Bean  
13.    public RestHighLevelClient restHighLevelClient(){  
14.        RestHighLevelClient client = new RestHighLevelClient(  
15.                RestClient.builder(  
16.                        //多個叢集,就new多個  
17.                        new HttpHost("localhost", 9200, "http")));  
18.         return client;  
19.    }  
20.}  

五,到這裡,springboot整合ES就好了。相較於springboot操作mysql需要jdbc支援,ES直接一步封裝到位,後期可以直接使用。

六,在進行crud前,我們先對比一下es與mysql。看完後,我們正式開始crue

在這裡插入圖片描述

七,索引的操作(對應的mysql就是資料庫的建立、刪除)在kibana中可以傻瓜式的建立索引與刪除索引

1)java程式碼建立索引

package com.shuang;  
@SpringBootTest  
class DemoApplicationTests {  
    @Autowired  
    private RestHighLevelClient restHighLevelClient; 
    @Test  
    void contextLoads() throws IOException {  
       //1,建立索引表  
        CreateIndexRequest request=new CreateIndexRequest("shuangbao");  
       //2,客戶端執行請求 IndicesClient,請求後獲得響應  
       CreateIndexResponse createIndexResponse=  
               restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);  
       System.out.println(createIndexResponse);  
    }  

}

2)刪除索引

@Test  
    void testDeleteIndex() throws IOException {  
        DeleteIndexRequest request=new DeleteIndexRequest();  
        AcknowledgedResponse delete =restHighLevelClient.indices().delete(request,RequestOptions.DEFAULT);  
       System.out.println(delete);  
    }  

八,操作文件(es7中”型別”我們一般都是設定為預設的_doc,es8中將移除”型別”,相當於mysql的表沒有了),文件相當於mysql中的一條記錄。es中插入一條json資料,如果沒有指明文件id,就會隨機生成一個

先新建索引,預設型別為_doc,建立文件結構json格式的屬性有string型別的name,integer型別的age。

1.PUT /shuang_index1
2. {  
3.   "settings": {  
4.    "index": {  
5.       "number_of_replicas": "1",   
6.       "number_of_shards": "5"   }  
7.  },  
8. "mappings": {   
9.    "test_type": {   
10.      "properties": {  
11.       "name": {  
12.           "type": "string",  
13.         },  
14.         "age": {  
15.         "type": "integer"  
16.        }  
17.      }  
18.    }  
19.  }  
20. }  

插入一條文件id為1的資料

PUT shuang_index2/_doc/1
{  
  "name":"shuang1",  
  "age":"1"  
}  

1)獲取文件的資訊,這裡我們獲取文件id為1的資訊

//獲取文件的資訊  
@Test  
void testIsGetDocument() throws IOException {  
    GetRequest getRequest=new GetRequest("shuang_index1","1");  
    //不獲取返回的_source的上下文  
  
   GetResponse getResponse=restHighLevelClient.get(getRequest,RequestOptions.DEFAULT);  
    System.out.println(getResponse.getSourceAsString());//列印文件的資料_source 
   System.out.println(getResponse);  //列印文件id為1的完整返回資訊
}  

{"age":1,"name":"shuang1"}  

"_index":"shuang_index1","_type":"_doc","_id":"1","_version":1,"_seq_no":4,"_primary_term":1,"found":true,"_source":{"age":1,"name":"shuang1"}}  

在這裡插入圖片描述
2)插入資料(資料來源可以是其他地方)

1.@Test  
2.void testBulkRequest() throws IOException {  
3.    BulkRequest bulkRequest=new BulkRequest();  
4.    bulkRequest.timeout("10s");  
5.  
6.    ArrayList<User> userList=new ArrayList<>();  
7.    userList.add(new User("jiang1",1));  
8.    userList.add(new User("jiang2",2));  
9.    userList.add(new User("jiang3",3));  
10.    userList.add(new User("jiang4",4));  
11.    userList.add(new User("jiang5",5));  
12.    userList.add(new User("jiang6",6));  
13.    userList.add(new User("jiang7",7));  
14.    userList.add(new User("jiang8",8));  
15.  
16.    //批處理請求  
17.    for(int i=0;i<userList.size();i++){  
18.        //批量更新和刪除,就在這裡修改對應的請求就可以了  
19.        bulkRequest.add(  
20.                new IndexRequest("shuang_index1")  
21.                .id(""+(i+1))  
22.                .source(JSON.toJSONString(userList.get(i)),XContentType.JSON));  
23.  
24.    }  
25.    BulkResponse bulkResponse=restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT);  
26.  
27.    System.out.println(bulkResponse.hasFailures());//是否失敗,返回false代表成功  
28.}  

3)刪除文件資訊

1.@Test  
2.void testDeleteRequest() throws IOException {  
3.    DeleteRequest request=new DeleteRequest("shuang_index1","1");  
4.    request.timeout("1s");  
5.  
6.    DeleteResponse deleteResponse=restHighLevelClient.delete(request,RequestOptions.DEFAULT);  
7.    System.out.println(deleteResponse.status());  
8.  
9.}  

4)更新文件資訊

1.@Test  
2.    void testUpdateRequest() throws IOException {  
3.        UpdateRequest updateRequest=new UpdateRequest("shuang_index1","1");  
4.        updateRequest.timeout("1s");  
5.  
6.        User user=new User("爽爽爽",18);  
7.        updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);  
8.  
9.        UpdateResponse updateResponse=restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);  
10.  
11.        System.out.println(updateResponse.status());  
12.    }  

九,倒排索引

前面介紹了文件的查詢,是普通的查詢get方式的,需要指定_index、_type、_id。也就是根據id從正排索引中獲取內容,確定唯一文件。
但搜尋search方式需要一種更復雜的模型,因為不知道查詢會命中哪些文件。採用倒排索引可以滿足複雜的搜尋過程。

在這裡插入圖片描述
搜尋也分精確值搜尋和全文搜尋,對資料建立索引和執行搜尋的原理如下圖所示:
在這裡插入圖片描述

十,查詢和全文查詢

Term(精確,不會被分詞器解析,keyword型別的欄位也不會被分詞器解析)和match(全文,會被分詞器解析)是兩個常用的基於倒排索引的搜尋型別。他們對應的java類主要有 TermQueryBuilder和MatchQueryBuilder。
精確搜尋(在shuang_index1索引中,name欄位為shuang1的文件)

1.@Test  
2.    void testSearch() throws IOException {  
3.        SearchRequest  searchRequest=new SearchRequest("shuang_index1");  
4.        //構建搜尋條件  
5.        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();  
6.  
7.        TermQueryBuilder termQueryBuilder=QueryBuilders.termQuery("name","shuang1");  
8.  
9.        sourceBuilder.query(termQueryBuilder);  
10.        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));  
11.  
12.        searchRequest.source(sourceBuilder);  
13.  
14.        SearchResponse searchResponse=restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);  
15.        System.out.println(JSON.toJSONString(searchResponse.getHits()));  
16.  
17.        for(SearchHit documentFields :searchResponse.getHits().getHits()){  
18.            System.out.println(documentFields.getSourceAsMap());  
19.        }  
20.    }  

第二個輸出是對搜尋返回資訊進行了提取關鍵欄位。
在這裡插入圖片描述
全文搜尋
1)在kibana上新建shuang_index2索引(相當於在navicat上去新建mysql資料庫)
一般我們不指定型別(系統會預設為_doc,es7不推薦使用了,es8將會移除型別,相當於mysql沒有了表這個結構)。
設定裡面的文件欄位為name和age,它們型別為string與interger。

1.PUT /shuang_index2  
2. {  
3.   "settings": {  
4.    "index": {  
5.       "number_of_replicas": "1",   //設定副本數為1
6.       "number_of_shards": "5"   }  //設定分片數為5
7.  },  
8. "mappings": {   
9.    "test_type": {   
10.      "properties": {  
11.       "name": {  
12.           "type": "string",  
13.         },  
14.         "age": {  
15.         "type": "integer"  
16.        }  
17.      }  
18.    }  
19.  }  
20. }  

2)插入資料(一般我們是從資料倉儲批量匯入,java程式碼見上面文件的插入)

Kibana控制檯也可以自己手動插入

1.PUT shuang_index2/_doc/5  
2.{  
3.  "name":"shuang e",  
4.  "age":"5"  
5.}  

這裡shuang_index2為索引、_doc為型別、5代表文件id(大資料環境下,我們一般不指定文件id,系統會隨機生成一個文件id,唯一標識該文件)。它們分別對應mysql的資料庫、表、行。name和age代表mysql欄位中的列屬性。文件結構必須為json格式。

3)搭建好索引結構後,我們去用java程式碼實現一下

(內建分詞器先把shuang bao分詞為shuang 與 bao ,再去shuang_index2索引中去尋找,文件1、2、3、4、5分別有name=shuang a、b、c、d、e。他們事先也被分詞器拆解了,只要有一個與上面對應,就搜尋成功,並且會攜帶一個相似程度score返回)

1.@Test  
2.void testSearch() throws IOException {  
3.    SearchRequest  searchRequest=new SearchRequest("shuang_index2");  
4.    //構建搜尋條件  
5.    SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();  
6.   
7.       MatchQueryBuilder matchQueryBuilder=QueryBuilders.matchQuery("name","shuang bao");  
8.    sourceBuilder.query(matchQueryBuilder);  
9.    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));  
10.  
11.    searchRequest.source(sourceBuilder);  
12.  
13.    SearchResponse searchResponse=restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);  
14.    System.out.println(searchResponse);  
15.   
16.    for(SearchHit documentFields :searchResponse.getHits().getHits()){  
17.        System.out.println(documentFields.getSourceAsMap());  
18.    }  
19.}  

返回結果:

{"took":770,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":5,"relation":"eq"},"max_score":0.08701137,"hits":[{"_index":"shuang_index2","_type":"_doc","_id":"1","_score":0.08701137,"_source":{"name":"shuang a","age":"1"}},{"_index":"shuang_index2","_type":"_doc","_id":"2","_score":0.08701137,"_source":{"name":"shuang b","age":"2"}},{"_index":"shuang_index2","_type":"_doc","_id":"3","_score":0.08701137,"_source":{"name":"shuang c","age":"3"}},{"_index":"shuang_index2","_type":"_doc","_id":"4","_score":0.08701137,"_source":{"name":"shuang d","age":"4"}},{"_index":"shuang_index2","_type":"_doc","_id":"5","_score":0.08701137,"_source":{"name":"shuang e","age":"5"}}]}}

{name=shuang a, age=1}
{name=shuang b, age=2}
{name=shuang c, age=3}
{name=shuang d, age=4}
{name=shuang e, age=5}

十一,es叢集

在這裡插入圖片描述
如圖3個es服務構成的叢集,索引test分成了5片,一個副本(粗框的),而book一個分片一個副本。保證瞭如果有一個服務壞了,其他服務也能執行所有工作。

一個搜尋請求必須詢問請求的索引中所有分片的某個副本來進行匹配。假設一個索引有5個主分片,每個主分片有1個副分片,共10個分片,一次搜尋請求會由5個分片來共同完成,它們可能是主分片,也可能是副分片。也就是說,一次搜尋請求只會命中所有分片副本中的一個。

ElasticSearch極簡入門總結就結束了感謝您的閱讀

相關文章