ElasticSearch8 - SpringBoot整合ElasticSearch

来源:https://www.cnblogs.com/konghuanxi/p/18094055
-Advertisement-
Play Games

前言 springboot 整合 ES 有兩種方案,ES 官方提供的 Elasticsearch Java API Client 和 spring 提供的 [Spring Data Elasticsearch](Spring Data Elasticsearch) 兩種方案各有優劣 Spring:高 ...


前言

springboot 整合 ES 有兩種方案,ES 官方提供的 Elasticsearch Java API Client 和 spring 提供的 [Spring Data Elasticsearch](Spring Data Elasticsearch)

兩種方案各有優劣

Spring:高度封裝,用著舒服。缺點是更新不及時,有可能無法使用 ES 的新 API

ES 官方:更新及時,靈活,缺點是太靈活了,基本是一比一複製 REST APIs,項目中使用需要二次封裝。

Elasticsearch Java API Client

目前最新版本 ES8.12,要求 jdk8 以上,API 裡面使用了大量的 builder 和 lambda

官方也提供了 測試用例

相容

翻了不少博客,大部分都是使用 High Level Rest Client,這是舊版本的 api,新版本使用 Elasticsearch Java API Client,如何相容舊版本,官方也提供了解決方案)

下文描述的均是新版 API

添加 jar 包

官方文檔:[installation](安裝| Elasticsearch Java API 客戶端 [8.12] |鬆緊帶 --- Installation | Elasticsearch Java API Client [8.12] | Elastic)

使用的是 maven,在 pom.xml 中添加

<dependency>  
    <groupId>co.elastic.clients</groupId>  
    <artifactId>elasticsearch-java</artifactId>  
    <version>8.12.2</version>  
</dependency>

<!-- 如果有添加springmvc,此包可不引入 -->
<dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>  
    <artifactId>jackson-databind</artifactId>  
    <version>2.12.3</version>  
</dependency>

如果報錯 ClassNotFoundException: jakarta.json.spi.JsonProvider,則還需要添加

<dependency>
    <groupId>jakarta.json</groupId>
    <artifactId>jakarta.json-api</artifactId>
    <version>2.0.1</version>
</dependency>

列印請求

在 application. yml 中添加配置,列印 es 的 http 請求(建議在開發調試時使用)

logging:  
  level:  
    tracer: TRACE

連接 ES

配置文件如下,後續所有 ES 操作都通過 ElasticsearchClient 對象

更多配置請看 Common configuration

@Configuration  
public class ElasticSearchConfig {  
  
    @Bean  
    public ElasticsearchClient esClient() {  
        // ES伺服器URL  
        String serverUrl = "http://127.0.0.1:9200";  
        // ES用戶名和密碼  
        String userName = "xxx";  
        String password = "xxx";  
  
        BasicCredentialsProvider credsProv = new BasicCredentialsProvider();  
        credsProv.setCredentials(  
                AuthScope.ANY, new UsernamePasswordCredentials(userName, password)  
        );  
  
        RestClient restClient = RestClient  
                .builder(HttpHost.create(serverUrl))  
                .setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv))  
                .build();  
  
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());  
        return new ElasticsearchClient(transport);  
    }  
  
}

索引操作

代碼中的 esClient 就是 ElasticsearchClient,請自行註入 bean

// 索引名字  
String indexName = "student";  
  
// 索引是否存在  
BooleanResponse books = esClient.indices().exists(e -> e.index(indexName));  
System.out.println("索引是否存在:" + books.value());  
  
// 創建索引  
esClient.indices().create(c -> c  
        .index(indexName)  
        .mappings(mappings -> mappings  // 映射  
                .properties("name", p -> p  
                        .text(t -> t // text類型,index=false  
                                .index(false)  
                        )  
                )  
                .properties("age", p -> p  
                        .long_(t -> t) // long類型  
                )  
        )  
);  
  
// 刪除索引  
esClient.indices().delete(d -> d.index(indexName));

文檔操作 (CRUD)

下文以官方測試數據 account. json 為例

實體類

首先定義實體類,用於 ES 中的欄位

public class Account {
	private String id;
	// 解決ES中欄位與實體類欄位不一致的問題  
	@JsonProperty("account_number")
    private Long account_number;  
    private String address;  
    private Integer age;  
    private Long balance;  
    private String city;  
    private String email;  
    private String employer;  
    private String firstname;  
    private String lastname;  
    private String gender;  
    private String state;  
	... 省略get、set方法
}

新增

String indexName = "account";  // 索引名字
Account account = new Account();
account.setId("1");
account.setLastname("guyu");   

// 新增
CreateResponse createResponse = esClient.create(c -> c  
        .index(indexName) // 索引名字  
        .id(account.getId()) // id  
        .document(account) // 實體類  
);

修改

UpdateResponse<Account> updateResp = esClient.update(u -> u  
                .index(indexName)  
                .id(account.getId())  
                .doc(account),  
        Account.class  
);

刪除

DeleteResponse deleteResp = esClient.delete(d -> d.index(indexName).id("1"));

批量新增

批量操作需要使用到 bulk

List<Account> accountList = ...
BulkRequest.Builder br = new BulkRequest.Builder();  
for (Account acc : accountList) {  
    br.operations(op -> op  
            .create(c -> c  
                    .index(indexName)  
                    .id(acc.getId())  
                    .document(acc)  
            )  
    );  
}  
BulkResponse bulkResp = esClient.bulk(br.build());

有沒有覺得批量新增的 .create () 裡面的參數很眼熟,批量刪除和更新請舉一反三

根據 id 查詢

// 定義實體類
GetResponse<Account> getResp = esClient.get(g -> g.index(indexName).id("1"), Account.class);  
if (getResp.found()) {  
    Account source = getResp.source();  // 這就是得到的實體類
    source.setId(getResp.id());  
}

// 不定義實體類
GetResponse<ObjectNode> getResp = esClient.get(g -> g  
                .index(indexName)  
                .id("1"),  
        ObjectNode.class  
);  
if (getResp.found()) {  
    ObjectNode json = getResp.source();  
    String firstname = json.get("firstname").asText();  
    System.out.println(firstname);  
}

搜索

搜索全部

SearchResponse<Account> searchResp = esClient.search(s -> s  
        .index(indexName)  
        .query(q -> q.matchAll(m -> m))  // 搜索全部
        , Account.class  
);

HitsMetadata<Account> hits = searchResp.hits();  
long totalValue = hits.total().value(); // 匹配到的數量  
hits.hits().forEach(h -> {  
    Account acc = h.source(); // 這就是得到的實體類  
    acc.setId(h.id());  
});

ES API 的對象定義,基本與返回的 json 一一對應的,所以 SearchResponse 就不過多贅述。

搜索 firstname = Amber

SearchResponse<Account> searchResp = esClient.search(s -> s  
        .index(indexName)  
        .query(q -> q  // 查詢  
                .match(t -> t  
                        .field("firstname")  
                        .query("Amber")  
                )  
        )  
        , Account.class  
);

// 也可以這樣寫
Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();
SearchResponse<Account> searchResp = esClient.search(s -> s  
        .index(indexName)  
        .query(firstNameQuery)  
        , Account.class  
);

嵌套查詢,比如搜索 firstname = Amber AND age = 32

Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();  
Query ageQuery = MatchQuery.of(m -> m.field("age").query(32))._toQuery();  
  
SearchResponse<Account> searchResp = esClient.search(s -> s  
        .index(indexName)  
        .query(q -> q  
                .bool(b -> b.must(firstNameQuery, ageQuery))  
        )  
        , Account.class  
);

淺分頁

from 和 size 參數類似於 mysql 的 limit,詳細說明見 Paginate search results

SearchResponse<Account> searchResp = esClient.search(s -> s  
        .index(indexName)   
        .from(0)  // 分頁參數
		.size(20)  // 分頁參數
        , Account.class  
);

排序

SearchResponse<Account> searchResp = esClient.search(s -> s  
        .index(indexName)   
        .sort(so -> so  // 排序欄位1
                .field(f -> f  
                        .field("age")  
                        .order(SortOrder.Asc)  
                )  
        )  
        .sort(so -> so  // 排序欄位2
                .field(f -> f  
                        .field("account_number")  
                        .order(SortOrder.Desc)  
                )  
        )  
        , Account.class  
);

Spring Data Elasticsearch

文檔: Spring Data Elasticsearch

添加 jar 和配置

pom.xml添加依賴

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

yml 配置

spring:  
  elasticsearch:  
    uris: http://xxx:9200  
    username: xxx  
    password: xxx
logging:  
  level:  
  # 輸出es的查詢參數(調試用)
    tracer: TRACE  

索引操作

實體類

@Data  
@Document(indexName = "account")  
public class Account {  
  
    @Id  
    private String id;  
    // 解決ES中欄位與實體類欄位不一致的問題  
    @Field(name = "account_number", type = FieldType.Long)  
    private Long accountNumber;  
    @Field(type = FieldType.Text)  
    private String address;  
    @Field(type = FieldType.Integer)  
    private Integer age;  
    @Field(type = FieldType.Long)  
    private Long balance;  
    @Field(type = FieldType.Text)  
    private String city;  
    @Field(type = FieldType.Text)  
    private String email;  
    @Field(type = FieldType.Text)  
    private String employer;  
    @Field(type = FieldType.Text)  
    private String firstname;  
    @Field(type = FieldType.Text)  
    private String lastname;  
    @Field(type = FieldType.Text)  
    private String gender;  
    @Field(type = FieldType.Text)  
    private String state;  
	... 省略get、set 方法
}
IndexOperations idxOpt = template.indexOps(Account.class);  
// 索引是否存在  
boolean idxExist = idxOpt.exists();  
  
// 創建索引  
boolean createSuccess = idxOpt.createWithMapping();  
System.out.println(createSuccess);  
  
// 刪除索引  
boolean deleted = idxOpt.delete();

文檔操作(CRUD)

Account account = new Account();  
account.setId("1");  
account.setLastname("guyu");  
  
// 這是插入或覆蓋,如果id存在了就是覆蓋  
template.save(account);  
  
// 修改,用的是es的_update  
template.update(account);

// 刪除  
template.delete(account)

// 批量新增(用的是es的_bulk)
List<Account> accountList = ...
template.save(accountList);

// 根據id查詢
Account account = template.get("1", Account.class);

搜索 + 排序 + 分頁

// 搜索 firstname = Amber AND age = 32
Criteria criteria = new Criteria();  
criteria.and(new Criteria("firstname").is("Amber"));  
criteria.and(new Criteria("age").is(32));  
  
// 分頁  
int pageNum = 1; // 頁碼  
int pageSize = 20; // 每頁數量  
Query query = new CriteriaQueryBuilder(criteria)  
        .withSort(Sort.by(new Order(Sort.Direction.ASC, "age"))) // 排序欄位1  
        .withSort(Sort.by(new Order(Sort.Direction.DESC, "balance"))) // 排序欄位1  
        .withPageable(PageRequest.of(pageNum - 1, pageSize)) // 淺分頁  
        // 不需要查詢的欄位
        .withSourceFilter(new FetchSourceFilterBuilder().withExcludes("email", "address").build())
        .build();  
  
SearchHits<Account> searchHits = template.search(query, Account.class);  
long totalValue = searchHits.getTotalHits(); // 匹配到的數量  
for (SearchHit<Account> searchHit : searchHits.getSearchHits()) {  
    Account account = searchHit.getContent(); // 這就是得到的實體類  
}

總結

本文介紹了 SpringBoot 整合 ElasticSearch 的兩種方案,但均只是簡單提及,更詳細的用法需要自行查看官方文檔。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 多任務進程與線程 一、多任務介紹 ​ 我們生活中有很多事情是同時進行的,比如開車的時候 手和腳共同來駕駛汽車,再比如唱歌跳舞也是同時進行的;用程式來模擬: from time import sleep def sing(): for i in range(3): print("正在唱歌...%d"% ...
  • 目錄一、Docker Compose 簡介二、Docker Compose 安裝2.1 Mac、Windows 平臺預設支持2.2 Linux 安裝(通過包管理)2.2.1 安裝2.2.2 測試2.2.3 卸載2.3 使用PIP 安裝與卸載2.3.1 PIP安裝2.3.2 PIP 卸載三、基本使用3 ...
  • C++ 變數 變數是用於存儲數據值的容器。 在 C++ 中,有不同類型的變數(使用不同的關鍵字定義),例如: int - 存儲整數(沒有小數點),例如 123 或 -123 double - 存儲浮點數,帶有小數點,例如 19.99 或 -19.99 char - 存儲單個字元,例如 'a' 或 ' ...
  • 在Java EE(Java Enterprise Edition)開發環境中,編程意義上的“介面”(interface)和API介面雖然都涉及介面的概念,但它們屬於不同層面的術語。 1. 編程意義上的“介面”(interface) 在Java編程語言中,介面(interface)是一種引用類型,它是 ...
  • Spring 為開發 Java 應用程式提供了全面的基礎架構支持,它將 Java 對象交由容器統一管理,從而實現控制反轉(IOC)和依賴註入(DI),並提供了一些常用模塊如 SpringAOP、SpringJDBC、SpringMVC 等等 SpringBoot 繼承了 Spring 的核心思想,並 ...
  • 在處理PDF文檔時,有時需要為文檔中的每一頁添加頁眉和頁腳,以包含一些有用的信息,如文檔標題、章節名稱、日期、頁碼等。對於需要自動化處理的場景,或者需要在大量文檔中添加一致的頁眉和頁腳,可以通過編程的方式來實現。本文將介紹如何使用Java為PDF文件添加頁眉、頁腳。 所需工具:Free Spire. ...
  • 今天講一個常見的gc compiler(也就是官方版本的go編譯器和runtime)在垃圾回收的掃描標記階段做的優化。 我對這個優化的描述印象最深的是在bigcache的註釋里,大致內容是如果map的鍵值都不包含指針,那麼gc掃描的時候不管這個map多大都不會深入掃描map內部存儲的數據,只檢查ma ...
  • 目錄簡介架構Application 和組件簡單入門示例先決條件創建項目編譯庫文件引用庫文件運行項目界面交互示例創建項目編譯庫文件實現應用函數引用庫文件運行項目參考資料 簡介 Sauron 是一個多功能的 Web 框架和庫,用於構建客戶端和/或伺服器端 Web 應用程式,重點關註人體工程學、簡單性和優 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...