SpringBoot與ElasticSearch檢索筆記整理

介紹

(一)檢索

我們的應用經常需要添加檢索功能,開源的 ElasticSearch 是目前全文搜索引擎的首選。他可以快速的存儲、搜索和分析海量數據。Spring Boot通過整合Spring Data ElasticSearch爲我們提供了非常便捷的檢索功能支持;

Elasticsearch是一個分佈式搜索服務,提供Restful API,底層基於Lucene,採用多shard(分片)的方式保證數據安全,並且提供自動resharding的功能,github等大型的站點也是採用了ElasticSearch作爲其搜索服務

(二)主要概念融合

在Elasticsearch中有幾個重要的概念,這裏將該名詞與開發中的數據庫系統進行關係映射,以此來更好的認識理解這些概念
在這裏插入圖片描述
引用官方文檔:

第一個業務需求是存儲員工數據。 這將會以 員工文檔 的形式存儲:一個文檔代表一個員工。存儲數據到 Elasticsearch 的行爲叫做 索引 ,但在索引一個文檔之前,需要確定將文檔存儲在哪裏。

一個 Elasticsearch 集羣可以 包含多個 索引 ,相應的每個索引可以包含多個 類型 。 這些不同的類型存儲着多個 文檔 ,每個文檔又有 多個 屬性 。

索引(名詞):

如前所述,一個 索引 類似於傳統關係數據庫中的一個 數據庫 ,是一個存儲關係型文檔的地方。 索引 (index) 的複數詞爲 indices 或 indexes 。

索引(動詞):

索引一個文檔 就是存儲一個文檔到一個 索引 (名詞)中以便被檢索和查詢。這非常類似於 SQL 語句中的 INSERT 關鍵詞,除了文檔已存在時,新文檔會替換舊文檔情況之外。

倒排索引:

關係型數據庫通過增加一個 索引 比如一個 B樹(B-tree)索引 到指定的列上,以便提升數據檢索速度。Elasticsearch 和 Lucene 使用了一個叫做 倒排索引 的結構來達到相同的目的

安裝

Docker下載鏡像

$docker  pull  elasticsearch

啓動
由於ES啓動需要2G的內存,爲了保證測試正常運行,在這裏設置了內存大小爲256M,9200端口暴露給外部訪問,9300暴露給集羣各節點的數據通信

$docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -p 9200:9200 -p 9300:9300 --name myEs 5acf0e8da90b

測試:訪問如下地址返回JSON格式的數據即爲正常
http://host_ip:9200/

檢索方式學習

Elasticsearch 返回一個 HTTP 狀態碼(例如:200 OK)和(除HEAD請求)一個 JSON 格式的返回值

員工索實例(官方文檔實例):這裏使用PostMan工具來進行請求發送

https://www.elastic.co/guide/cn/elasticsearch/guide/cn/_indexing_employee_documents.html

實驗準備:

1:每個員工索引一個文檔,文檔包含該員工的所有信息。
2:每個文檔都將是 employee 類型 。
3:該類型位於 索引 megacorp 內。

(一)索引員工文檔
PUT /megacorp/employee/1
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}

PostMan使用:其中PUT框選擇請求方式,地址欄填入RestFul請求,如果請求方式帶有方法體,在Body部分選擇raw填入請求文檔數據,右側可以選擇數據文檔格式,建議JSON
在這裏插入圖片描述
注意,路徑 /megacorp/employee/1 包含了三部分的信息:

megacorp
索引名稱
employee
類型名稱
1
特定僱員的ID
請求體 —— JSON 文檔 —— 包含了這位員工的所有詳細信息,他的名字叫 John Smith ,今年 25 歲,喜歡攀巖。
很簡單!無需進行執行管理任務,如創建一個索引或指定每個屬性的數據類型之類的,可以直接只索引一個文檔。Elasticsearch 默認地完成其他一切,因此所有必需的管理任務都在後臺使用默認設置完成。

通過PostMan剛纔的請求,現在查看響應體可以發現ES已經爲我們添加了索引,並且索引了一個文檔
在這裏插入圖片描述
接着依次通過PostMan工具將下面倆條數據索引到文檔中

PUT /megacorp/employee/2
{
    "first_name" :  "Jane",
    "last_name" :   "Smith",
    "age" :         32,
    "about" :       "I like to collect rock albums",
    "interests":  [ "music" ]
}

PUT /megacorp/employee/3
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}
(二)檢索文檔

目前我們已經在 Elasticsearch 中存儲了一些數據, 接下來就能專注於實現應用的業務需求了。第一個需求是可以檢索到單個僱員的數據。

這在 Elasticsearch 中很簡單。簡單地執行 一個 HTTP GET 請求並指定文檔的地址——索引庫、類型和ID。 使用這三個信息可以返回原始的 JSON 文檔:

發送GET請求

http://192.168.0.104:9200/megacorp/employee/1

返回結果包含了文檔的一些元數據,以及 _source 屬性,內容是 John Smith 僱員的原始 JSON 文檔:

{
    "_index": "megacorp",
    "_type": "employee",
    "_id": "1",
    "_version": 1,
    "found": true,
    "_source": {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing",
        "interests": [
            "sports",
            "music"
        ]
    }
}

注意:將 HTTP 命令由 PUT 改爲 GET 可以用來檢索文檔,同樣的,可以使用 DELETE 命令來刪除文檔,以及使用 HEAD 指令來檢查文檔是否存在。如果想更新已存在的文檔,只需再次 PUT 。HEAD 指令 如果檢索到數據返回200,如果未檢索到數據返回404,沒有響應體

(三)輕量搜索
GET /megacorp/employee/_search

可以看到,我們仍然使用索引庫 megacorp 以及類型 employee,但與指定一個文檔 ID 不同,這次使用 _search 。返回結果包括了所有三個文檔,放在數組 hits 中。一個搜索默認返回十條結果。

注意:返回結果不僅告知匹配了哪些文檔,還包含了整個文檔本身:顯示搜索結果給最終用戶所需的全部信息。

接下來,嘗試下搜索姓氏爲 Smith 的僱員。爲此,我們將使用一個 高亮 搜索,很容易通過命令行完成。這個方法一般涉及到一個 查詢字符串 (query-string) 搜索,因爲我們通過一個URL參數來傳遞查詢信息給搜索接口:

GET /megacorp/employee/_search?q=last_name:Smith

我們仍然在請求路徑中使用 _search 端點,並將查詢本身賦值給參數 q= 。返回結果給出了所有的 Smith:

(四)使用查詢表達式搜索

Query-string 搜索通過命令非常方便地進行臨時性的即席搜索 ,但它有自身的侷限性(參見 輕量 搜索 )。Elasticsearch 提供一個豐富靈活的查詢語言叫做 查詢表達式 , 它支持構建更加複雜和健壯的查詢。

領域特定語言 (DSL), 使用 JSON 構造了一個請求。我們可以像這樣重寫之前的查詢所有名爲 Smith 的搜索 :

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}

返回結果與之前的查詢一樣,但還是可以看到有一些變化。其中之一是,不再使用 query-string 參數,而是一個請求體替代。這個請求使用 JSON 構造,並使用了一個 match 查詢

(五)更復雜的查詢

現在嘗試下更復雜的搜索。 同樣搜索姓氏爲 Smith 的員工,但這次我們只需要年齡大於 30 的。查詢需要稍作調整,使用過濾器 filter ,它支持高效地執行一個結構化查詢。

GET /megacorp/employee/_search
{
    "query" : {
        "bool": {
            "must": {
                "match" : {
                    "last_name" : "smith" 
                }
            },
            "filter": {
                "range" : {
                    "age" : { "gt" : 30 } 
                }
            }
        }
    }
}

這部分與我們之前使用的 match 查詢 一樣。
這部分是一個 range 過濾器 , 它能找到年齡大於 30 的文檔,其中 gt 表示_大於_(great than)

(六)全文搜索

截止目前的搜索相對都很簡單:單個姓名,通過年齡過濾。現在嘗試下稍微高級點兒的全文搜索——一項 傳統數據庫確實很難搞定的任務。

搜索下所有喜歡攀巖(rock climbing)的員工:

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}

Elasticsearch 默認按照相關性得分排序,即每個文檔跟查詢的匹配程度。第一個最高得分的結果很明顯:John Smith 的 about 屬性清楚地寫着 “rock climbing” 。

但爲什麼 Jane Smith 也作爲結果返回了呢?原因是她的 about 屬性裏提到了 “rock” 。因爲只有 “rock” 而沒有 “climbing” ,所以她的相關性得分低於 John 的。

這是一個很好的案例,闡明瞭 Elasticsearch 如何 在 全文屬性上搜索並返回相關性最強的結果。Elasticsearch中的 相關性 概念非常重要,也是完全區別於傳統關係型數據庫的一個概念,數據庫中的一條記錄要麼匹配要麼不匹配。
短語搜索
找出一個屬性中的獨立單詞是沒有問題的,但有時候想要精確匹配一系列單詞或者_短語_ 。 比如, 我們想執行這樣一個查詢,僅匹配同時包含 “rock” 和 “climbing” ,並且 二者以短語 “rock climbing” 的形式緊挨着的僱員記錄。

爲此對 match 查詢稍作調整,使用一個叫做 match_phrase 的查詢:

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}
(七)高亮搜索

許多應用都傾向於在每個搜索結果中 高亮 部分文本片段,以便讓用戶知道爲何該文檔符合查詢條件。在 Elasticsearch 中檢索出高亮片段也很容易。

再次執行前面的查詢,並增加一個新的 highlight 參數:

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    },
    "highlight": {
        "fields" : {
            "about" : {}
        }
    }
}

(八)檢索總結

在這裏插入圖片描述

SpringBoot整合ES

(一)簡單介紹和自動配置分析

下面通過SpringBoot來整合ES,啓動選擇時選擇ElasticSearch即可,本次測試版本爲SpringBoot 1.X
SpringBoot默認使用Spring Data來操作ElasticSearch的.
查看SpringBoot的自動配置包可以發現它有倆種方式交互ElasticSearch,
第一種是JEST,第二種是SpringData ElasticSearch
在這裏插入圖片描述
JEST默認是不生效的,SpringData ElasticSearch默認引入

如果需要使用JEST則需要添加對應的依賴並且去掉SpringData ElasticSearch
的依賴,根據ElasticSearch的版本選擇JEST依賴,二者依賴不要同時引入

(二)Jest客戶端整合使用
<!-- https://mvnrepository.com/artifact/io.searchbox/jest -->
<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
    <version>6.3.1</version>
</dependency>

使用Jest來操作ES,該自動配置包下有倆個主要的類,一個是自動配置類,一個是屬性映射類:
JestAutoConfiguration類中提供了@Bean JestClient該對象可以爲我們提供具體的ES操作功能
JestProperties:提供了相關的ES配置映射
uris:表示的連接的主機地址,可以有多個,默認爲http://localhost:9200

配置文件application.properties添加配置

spring.elasticsearch.jest.uris=http://192.168.0.104:9200
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootElasticsearchApplicationTests {

    @Autowired
    JestClient jestClient;
    //索引文檔到索引中
    @Test
    public void createIndex() throws IOException {
        Employee employee = new Employee();
        employee.setId(4);
        employee.setFirst_name("lihua");
        employee.setLast_name("bob");
        employee.setAge(18);
        employee.setAbout("hello world");
        List<String> list = new ArrayList<>();
        list.add("ball");
        list.add("sing");
        employee.setInterests(list);
        //構建一個索引,將employee文檔索引到megacorp上
        Index index = new Index.Builder(employee).index("megacorp").type("employee").build();
        jestClient.execute(index);

    }

    //檢索數據
    @Test
    public void search() throws IOException {
        //創建一個搜索表達式
        String json = "{\n" +
                "    \"query\" : {\n" +
                "        \"match\" : {\n" +
                "            \"about\" : \"rock climbing\"\n" +
                "        }\n" +
                "    }\n" +
                "}";
        //構建搜索
        Search build = new Search.Builder(json).addIndex("megacorp").addType("employee").build();
        SearchResult result = jestClient.execute(build);
       //返回的result 包含了查詢的頭部信息和內部JSON數據
        System.out.println(result);

    }
}

索引文檔結果可以通過PostMan工具查詢是否已經索引文檔到庫中

(三)SpringData ES整合使用
(1)官方文檔參考

https://docs.spring.io/spring-data/elasticsearch/docs/3.2.5.RELEASE/reference/html/#new-features

(2)自動配置類介紹

自動配置類:ElasticsearchAutoConfiguration
主要信息:
1:Client類,集羣節點信息clusterNodes、clusterName
2:ElasticsearchProperties配置映射
前綴:spring.data.elasticsearch
開發時需要配置倆個屬性:
clusterName
clusterNodes

3:ElasticsearchTemplate 操作elasticsearch
4: ElasticsearchRepository,可繼承該類,通過繼承該類使用該類的各種操作方法,類似於JPA的Repository模式

使用注意:elasticsearch和SpringData Elasticsearch版本存在很多不一致,在使用過程中啓動可能存在問題,所以需要將版本進行匹配
在這裏插入圖片描述
備註:本次實驗使用版本情況如下:
SpringBoot:1.5.12.RELEASE,SpringData Elasticsearch依賴於SpringBoot
elasticsearch:2.4.6

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

設置配置文件

#集羣名(默認值: elasticsearch)
spring.data.elasticsearch.cluster-name=elasticsearch
#默認 9300 是 Java 客戶端的端口。9200 是支持 Restful HTTP 的接口
#集羣節點地址列表,用逗號分隔。如果沒有指定,就啓動一個客戶端節點
spring.data.elasticsearch.cluster-nodes=192.168.0.104:9300
(3)ElasticsearchRepository方式操作

接下來使用ElasticsearchRepository操作ES,首先創建一個實體類作爲傳輸的文檔數據,
Employee,字段名稱同官方文檔中的員工實例屬性

然後創建一個接口EmployeeRepository繼承ElasticsearchRepository,設置對應的數據類型,
泛型參數中:Employee爲數據格式,Integer爲ID主鍵類型

public interface EmployeeRepository  extends ElasticsearchRepository<Employee,Integer> {

}

通過下面註解來聲明索引和類型
@Document(indexName = “megacorp”,type = “employee”)
其中indexName 表示索引名稱,type 表示類型

@Document(indexName = "megacorp",type = "employee")
public class Employee {

    private Integer id;
    private String first_name;
    private String last_name;
    private Integer age;
    private String about;
private List<String> interests;
//省略setter和getter等
}

測試



@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootElasticsearchApplicationTests {


    @Autowired
    EmployeeRepository employeeRepository;

    @Test
    public void employeeRepositoryInsert() {
        Employee employee = new Employee();
        employee.setId(5);
        employee.setFirst_name("lihua");
        employee.setLast_name("bob");
        employee.setAge(43);
        employee.setAbout("hello world");
        List<String> list = new ArrayList<>();
        list.add("game");
        list.add("football");
		employee.setInterests(list);
        employeeRepository.index(employee);
    }

}  

生成結果

在這裏插入圖片描述

除了使用父類提供的方法外,還可以自定義方法,可以根據提示來創建方法名,格式類似於MyBatis的Example的用法,詳細的介紹參考官方文檔

public interface EmployeeRepository extends ElasticsearchRepository<Employee, Integer> {
public  List<Employee>  findEmployeeByAboutIsLike(String about);
}
@Test
    public void TestFindEmployeesByFirst_nameIsLike() {

        List<Employee> employees = employeeRepository.findEmployeeByAboutIsLike("hello");
        System.out.println("-----------------------");
        for (Employee employee : employees) {
            System.out.println(employee);
        }

    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章