springboot整合elasticsearch(基於es7.2和官方high level client)

前言

最近寫的一個我的項目(傳送門:全終端雲書籤)中須要用到全文檢索功能,目前 mysql,es 均可以作全文檢索,mysql 勝在配置方便很快就能搞定上線(參考這裏),不考慮上手難度,es 在全文檢索方面是完勝 mysql 的。php

最後決定使用 es。使用最新的 7.2 版本。java 客戶端使用 es 官方的 high level client(官方文檔),爲何用這個有如下幾點緣由:html

  • jest 畢竟不是官方的,更新速度較慢
  • transportClient,速度太慢,連官方都嫌棄它了。在 7.x 中已經被棄用,8.x 中將徹底刪除
  • high level client 的官方文檔寫的很清楚明瞭,雖然目前相關的中文資料還不多,也可以上手用起來

本文主要內容以下:java

  • docker 部署 es(支持 ik 中文分詞)
  • 在 springboot 中進行增刪改查

docker 部署 es(基於 linux)

es 的中文分詞目前比較流行的分詞插件爲 ik(github 地址)。因爲手寫 docker 命令太繁雜,這裏用 docker-compose 來管理。假定當前在/root 目錄下node

  1. 下載 ik release 到/root/es/ik 目錄下,並解壓到當前文件夾。mysql

  2. 建立/root/es/data 目錄,並將讀寫權限給全部用戶.本目錄用於存放 es 數據。因爲 es 不能以 root 用戶執行,因此對於此目錄須要將讀寫權限給其餘用戶。linux

  3. 編寫 es 配置文件,7.2 的配置文件變化仍是較大的(以前用的是 2.x 版本),一個簡單的配置以下:git

    cluster.name: elasticsearch
    
     # 配置的集羣名稱,默認是 elasticsearch,es 服務會經過廣播方式自動鏈接在同一網段下的 es 服務,經過多播方式進行通訊,同一網段下能夠有多個集羣,經過集羣名稱這個屬性來區分不一樣的集羣。
    
     node.name: bookmark-world
    
     # 當前配置所在機器的節點名,你不設置就默認隨機指定一個 name 列表中名字,該 name 列表在 es 的 jar 包中 config 文件夾裏 name.txt 文件中,其中有不少做者添加的有趣名字。
    
     node.master: true
    
     # 指定該節點是否有資格被選舉成爲 node(注意這裏只是設置成有資格, 不表明該 node 必定就是 master),默認是 true,es 是默認集羣中的第一臺機器爲 master,若是這臺機掛了就會從新選舉 master。
    
     node.data: true
    
     # 指定該節點是否存儲索引數據,默認爲 true。
    
     bootstrap.memory_lock: false
    
     # 設置爲 true 來鎖住內存不進行 swapping。由於當 jvm 開始 swapping 時 es 的效率 會下降,因此要保證它不 swap,能夠把 ES_MIN_MEM 和 ES_MAX_MEM 兩個環境變量設置成同一個值,而且保證機器有足夠的內存分配給 es。 同時也要容許 elasticsearch 的進程能夠鎖住內存,linux 下啓動 es 以前能夠經過`ulimit -l unlimited`命令設置。
     # 設置爲 true,會致使報警告實際未鎖定內存,進而退出進程(es在生產模式下有警告就會退出)
    
     network.bind_host: 0.0.0.0
    
     # 設置綁定的 ip 地址,能夠是 ipv4 或 ipv6 的,默認爲 0.0.0.0,綁定這臺機器的任何一個 ip。
     # 集羣配置
     discovery.seed_hosts:
     - bookmark-es
     cluster.initial_master_nodes:
     - bookmark-world
    
    複製代碼
  4. 編寫/root/docker-compose.ymlgithub

    version: "2"
    services:
     bookmark-es:
     image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    container_name: bookmark-es
    volumes:
     - /etc/localtime:/etc/localtime
     - ./es/data:/usr/share/elasticsearch/data
     - ./es/ik:/usr/share/elasticsearch/plugins/ik
     - ./es/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
    ports:
     - 9200:9200
     - 9300:9300
    複製代碼
  5. 執行 docker-compose up -d啓動 esweb

詳細可參考這裏:雲書籤 docker 部署spring

springboot 整合

建立 springboot 項目

首先建立一個 springboot 項目,而後引入high level client的依賴,pom 文件以下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.fanxb</groupId>
    <artifactId>es-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>es-demo</name>
    <description>Elasticsearch Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <!--注意:若是使用了parent那麼須要在此定義es版本號,由於spring-boot-start-parent中已經定義了es相關依賴的版本號 ,high-level-client中的部分依賴會被覆蓋成低版本的,導出出現莫名其妙的錯誤-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>elasticsearch-rest-high-level-client</artifactId>
                <version>7.2.0</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
                <version>7.2.0</version>
            </dependency>
            <!--&lt;!&ndash; https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-client &ndash;&gt;-->
            <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>elasticsearch-rest-client</artifactId>
                <version>7.2.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
複製代碼

注意:這裏有一個依賴的大坑,要注意!

若是定義了<parent>,就必須在<dependencyManagement>中指定部分依賴的版本,不然會由於依賴版本不對出現各類莫名其妙的錯誤,上面註釋中已經指出。

建立 util/EsUtil.java 工具類

主要功能函數以下:

預建立 index

雖然 es 在插入數據時會自動根據字段類型來建立字段定義,可是自動建立並不老是和須要相符的,好比想讓某個字段不分詞,或者使用其餘的分詞器。因此在代碼中先判斷 index(es7 中已經廢棄了 mapping,也就是一個 index 至關於一個表)是否存在,若是不存在就建立 index.

主要代碼以下:

//被@PostConstruct註釋的方法將會在對應類注入到Spring後調用,確保index的生成
@PostConstruct
public void init() {
    try {
        if (client != null) {
            client.close();
        }
        client = new RestHighLevelClient(RestClient.builder(new HttpHost(host, port, scheme)));
        if (this.indexExist(INDEX_NAME)) {
            return;
        }
        CreateIndexRequest request = new CreateIndexRequest(INDEX_NAME);
        request.settings(Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 2));
        request.mapping(CREATE_INDEX, XContentType.JSON);
        CreateIndexResponse res = client.indices().create(request, RequestOptions.DEFAULT);
        if (!res.isAcknowledged()) {
            throw new RuntimeException("初始化失敗");
        }
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(0);
    }
}
複製代碼

插入或者更新一個對象

經過指定 id,若是此 id 存在那麼就是更新,不然是插入。

public void insertOrUpdateOne(String index, EsEntity entity) {
    IndexRequest request = new IndexRequest(index);
    request.id(entity.getId());
    request.source(JSON.toJSONString(entity.getData()), XContentType.JSON);
    try {
        client.index(request, RequestOptions.DEFAULT);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
複製代碼

批量插入

high level client 提供了方便的批量操做接口,以下所示:

public void insertBatch(String index, List<EsEntity> list) {
    BulkRequest request = new BulkRequest();
    list.forEach(item -> request.add(new IndexRequest(index).id(item.getId())
            .source(JSON.toJSONString(item.getData()), XContentType.JSON)));
    try {
        client.bulk(request, RequestOptions.DEFAULT);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
複製代碼

批量刪除

和上面同樣一樣用到了BulkRequest

public <T> void deleteBatch(String index, Collection<T> idList) {
    BulkRequest request = new BulkRequest();
    idList.forEach(item -> request.add(new DeleteRequest(index, item.toString())));
    try {
        client.bulk(request, RequestOptions.DEFAULT);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
複製代碼

搜索

經過構建SearchSourceBuilder查詢參數

public <T> List<T> search(String index, SearchSourceBuilder builder, Class<T> c) {
    SearchRequest request = new SearchRequest(index);
    request.source(builder);
    try {
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHit[] hits = response.getHits().getHits();
        List<T> res = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            res.add(JSON.parseObject(hit.getSourceAsString(), c));
        }
        return res;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
複製代碼

delete by query

es 插入數據容易,刪除就比較麻煩了,特別是根據條件刪除。

public void deleteByQuery(String index, QueryBuilder builder) {
    DeleteByQueryRequest request = new DeleteByQueryRequest(index);
    request.setQuery(builder);
    //設置批量操做數量,最大爲10000
    request.setBatchSize(10000);
    request.setConflicts("proceed");
    try {
        client.deleteByQuery(request, RequestOptions.DEFAULT);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
複製代碼

結束

可經過測試類com.fanxb.esdemo.service.BookServiceTest查看運行結果。

源碼地址:github

本文原創發佈於:www.tapme.top/blog/detail…

掃碼關注微信公衆號:FleyX 學習筆記,獲取更多幹貨

相關文章
相關標籤/搜索