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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...