SpringBoot 整合 Spring Data Elasticsearch
啟動(dòng)命令行窗口,執(zhí)行:elasticsearch 命令即可啟動(dòng) Elasticsearch 服務(wù)器
三種查詢方式解釋:
方法名關(guān)鍵字查詢: 就是全自動(dòng)查詢,只要按照規(guī)則來定義查詢方法 ,Spring Data Elasticsearch 就會(huì)幫我們生成對(duì)應(yīng)的查詢語句,并且生成方法體。
@Query 查詢 : 就是半自動(dòng)查詢, 按照 Spring Data Elasticsearch 所要求的規(guī)則來定義查詢語句,那么 Spring Data Elasticsearch 就會(huì)為其生成查詢方法。
自定義查詢: 就是全手動(dòng)查詢,就是自己寫完整的查詢方法。
ElasticsearchRestTemplate(ReactiveElasticsearchTemplate) 介紹
若類加載路徑下有 Spring Data Elasticsearch 依賴,
Spring Boot 就會(huì)在容器中自動(dòng)配置一個(gè) ElasticsearchRestTemplate(傳統(tǒng)的、非反應(yīng)式的同步的API)
若類加載路徑下有 Spring Data Elasticsearch 和 Spring WebFlux 的依賴,
Spring Boot會(huì)在容器中自動(dòng)配置一個(gè) ReactiveElasticsearchTemplate(基于 Reactive 反應(yīng)式的異步的API)
ElasticsearchRestTemplate 底層依賴于容器中自動(dòng)配置的 RestHighLevelClient
( RestHighLevelClient 是 Elasticsearch 官方提供的),
ReactiveElasticsearchTemplate 底層依賴于容器中自動(dòng)配置的 ReactiveElasticsearchClient。
( ReactiveElasticsearchClient 是 Spring Data Elastic提供的)
【備注】
Elasticsearch 官方并未提供反應(yīng)式的 RestClient :
因此 Spring Data Elasticsearch 額外補(bǔ)充了一個(gè) ReactiveElasticsearchClient,用于提供反應(yīng)式API支持,
ReactiveElasticsearchClient 相當(dāng)于 RestHighLevelClient 的反應(yīng)式版本,因此它們二者的功能基本相似。
與 RestHighLevelClient、ReactiveElasticsearchClient相比,
ElasticsearchRestTemplate、 ReactiveElasticsearchTemplate 能以更面向?qū)ο蟮姆椒▉聿僮?Elasticsearch 索引庫,這些 XxxTemplate 方法操作的是數(shù)據(jù)類的對(duì)象。
而 RestHighLevelClient、ReactiveElasticsearchClient 依然是面向 Elasticsearch 的索引庫、文檔編程。
Spring Data Elasticsearch 的功能
Spring Data Elasticsearch 大致包括如下幾方面功能:
1、DAO 接口只需繼承 CrudRepository 或 ReactiveCrudRepository ,Spring Data Elasticsearch 能為 DAO 組件提供實(shí)現(xiàn)類。
2、Spring Data Elasticsearch 支持方法名關(guān)鍵字查詢,只不過 Elasticsearch 查詢都是全文檢索查詢。
3、Spring Data Elasticsearch 同樣支持使用 @Query 指定查詢語句,只不過此處指定的查詢語句都是 JSON 格式的查詢語句。
——這是因?yàn)?Elasticsearch 它需要的查詢語句就是 JSON 格式。
4、Spring Data Elasticsearch 同樣支持 DAO 組件添加自定義的查詢方法
——通過添加額外的父接口,并為額外的該接口提供實(shí)現(xiàn)類,Spring Data Elasticsearch 就能該實(shí)現(xiàn)類中的方法“移植”到DAO組件中。
與 NoSQL 技術(shù)不同的是,Elasticsearch 屬于全文檢索引擎,因此無論它的方法名關(guān)鍵字查詢也是基于全文檢索的。
例如:
對(duì)于 findByName(String name) 方法,假如傳入?yún)?shù)為 “鳴人”,
這意味著查詢 name 字段中 包含“ 鳴人” 關(guān)鍵字的文檔,
而不是查詢 name 字段值 等于 “鳴人” 的文檔
——此處與 Solr 是完全一樣的操作。
注解(@Document 和 @Field )
Repository 操作的數(shù)據(jù)類使用 @Document 和 @Field 注解修飾。
其中 @Document 修飾的數(shù)據(jù)類映射到索引庫,使用該注解時(shí)可指定如下兩個(gè)常用屬性:
indexName: 指定該實(shí)體映射到哪個(gè) Index(Index在這里就是索引庫)
createIndex: 指定是否根據(jù)實(shí)體類創(chuàng)建 Index。
@Field 修飾的屬性則映射到索引文檔的 Field,使用該注解時(shí)可指定如下常用屬性:
name: 指定該屬性映射到索引文檔的哪個(gè)Field,如果不指定該屬性,默認(rèn)基于同名映射。
analyzer: 用這個(gè)字段指定在創(chuàng)建索引庫時(shí),為索引庫添加哪種分詞器。
searchAnalyzer: 指定對(duì)該字段執(zhí)行搜索時(shí)所使用的分詞器。
代碼演示:
1、創(chuàng)建項(xiàng)目
創(chuàng)建一個(gè)springboot項(xiàng)目
2、添加依賴
3、配置文件
4、添加實(shí)體類
5、基于 ReactiveElasticsearchTemplate 的反應(yīng)式的異步版本API
就是 Dao 組件是繼承 【ReactiveCrudRepository 】這個(gè) API 接口來實(shí)現(xiàn) 【方法名關(guān)鍵字查詢】 和 【@Query查詢】的。
然后【自定義查詢方法】是基于 【ReactiveElasticsearchTemplate 】這個(gè)反應(yīng)式的 API 實(shí)現(xiàn)的。
ReactiveCrudRepository 是 Spring Data 提供的接口之一,用于支持基于響應(yīng)式編程模型的 CRUD(增刪改查)操作,同時(shí)支持異步、非阻塞的操作。
反應(yīng)式異步版本,就是演示的查詢方法的返回值類型是 Flux 或者 Mono 。
一、測(cè)試類演示–方法名關(guān)鍵字查詢 (全自動(dòng)查詢)
演示 Spring Data Elasticsearch 所提供的方法名關(guān)鍵字查詢
方法名關(guān)鍵字查詢: 就是全自動(dòng)查詢,只要按照規(guī)則來定義查詢方法 ,Spring Data Elasticsearch 就會(huì)幫我們生成對(duì)應(yīng)的查詢語句,并且生成方法體。
添加 DAO 組件(繼承 ReactiveCrudRepository 接口 )
1、添加文檔
代碼
測(cè)試結(jié)果
運(yùn)行成功
查看指定索引庫的所有文檔信息:http://localhost:9200/books/_search
2、根據(jù)文檔id刪除文檔
代碼
測(cè)試結(jié)果
運(yùn)行成功
查看指定索引庫的所有文檔信息:http://localhost:9200/books/_search
可以看到 id =13 和 14 的文檔已經(jīng)被刪除了
3、普通關(guān)鍵字查詢
對(duì) name 在這個(gè)字段進(jìn)行關(guān)鍵字查詢
代碼
測(cè)試結(jié)果
4、對(duì)關(guān)鍵字做正則表達(dá)式查詢
對(duì) description 這個(gè)字段的關(guān)鍵詞做正則表達(dá)式查詢,
如圖:通過這個(gè)方法名關(guān)鍵字查詢【findByDescriptionMatches】 可以看出來是對(duì) description 這個(gè)字段的關(guān)鍵詞進(jìn)行查詢。
代碼
測(cè)試結(jié)果
全自動(dòng)的方法名關(guān)鍵字查詢:內(nèi)部執(zhí)行的查詢語句如圖:
5、對(duì)關(guān)鍵字做范圍查詢
對(duì) price 這個(gè)字段,做價(jià)格的范圍查詢
代碼
測(cè)試結(jié)果
全自動(dòng)的方法名關(guān)鍵字查詢:內(nèi)部執(zhí)行的查詢語句如圖:
6、根據(jù) in 運(yùn)算符查詢
使用 in 運(yùn)算符,根據(jù)集合內(nèi)的id,查詢出對(duì)應(yīng)的文檔
代碼
測(cè)試結(jié)果
二、 測(cè)試類演示–@Query查詢(半自動(dòng)查詢)
@Query 查詢 : 就是半自動(dòng)查詢, 按照 Spring Data Elasticsearch 所要求的規(guī)則來定義查詢語句,那么 Spring Data Elasticsearch 就會(huì)為其生成查詢方法。
添加 DAO 組件
//指定自定義的查詢語法:-------------------------------------------------------
//根據(jù)字段和關(guān)鍵字進(jìn)行全文檢索 -- ?0就是要查詢的字段;?1就是要查詢的關(guān)鍵字 ------ 不支持通配符查詢
@Query("{ \"match\": { \"?0\": \"?1\" } } ")
//通過 query_string 來指定原生的 Lucene 查詢語法--------支持通配符查詢
//@Query("{ \"query_string\": { \"query\": \"?0:?1\" } } ")
Flux<Book> findByQuery(String field, String term);
match – 不支持通配符查詢
如圖,在dao組件寫的查詢語句中,用的是簡單 match ,是不支持通配符查詢的
測(cè)試類代碼
測(cè)試結(jié)果
因?yàn)樵谟?@Query("{ “match”: { “?0”: “?1” } } ")查詢時(shí),
用的是簡單 match ,是不支持通配符查詢的
query_string – 支持通配符查詢
dao注解這里的查詢方法,把 match 改成 query_string,
通過 query_string 來指定原生的 Lucene 查詢語法--------支持通配符查詢
測(cè)試類代碼
測(cè)試類代碼還是這個(gè)
測(cè)試結(jié)果
如圖: @Query("{ “query_string”: { “query”: “?0:?1” } } ") 中,
使用 query_string 就支持通配符查詢了
三、測(cè)試類演示–自定義查詢方法(全手動(dòng)查詢)
自定義查詢: 就是全手動(dòng)查詢,就是自己寫完整的查詢方法。
讓 DAO 接口繼承自定義 DAO 接口、并為自定義 DAO 接口提供實(shí)現(xiàn)類,可以為 DAO 組件添加自定義查詢方法。
自定義查詢方法通常推薦使用 ElasticsearchRestTemplate 或 ReactiveElasticsearchTemplate 執(zhí)行查詢;
當(dāng)然也可通過 RestHighLevelClient 或 ReactiveElasticsearchClient 來執(zhí)行查詢。
代碼演示(使用 ReactiveElasticsearchTemplate 反應(yīng)式)
自定義DAO接口
自定義DAO組件,用來實(shí)現(xiàn)全手動(dòng)的自定義查詢方法
自定義查詢的方法的作用:查詢一個(gè)文檔內(nèi), name 字段要包含 nameTerm 這個(gè)關(guān)鍵字 且 description 字段要包含 descTerm 這個(gè)關(guān)鍵字
自定義方法的實(shí)現(xiàn)邏輯
BookDao 繼承CustomBookDao 這個(gè)自定義的Dao接口,主要用來通過 BookDao 調(diào)用 CustomBookDao 里面的自定義查詢方法
測(cè)試類方法
測(cè)試結(jié)果
name 字段里面有【忍者】這個(gè)關(guān)鍵字,且 description 字段里面有 【成長】 這個(gè)關(guān)鍵字。
符合的文檔有一條
name 字段里面有【教師】這個(gè)關(guān)鍵字,且 description 字段里面有 【成長】 這個(gè)關(guān)鍵字。
符合的文檔有一條
name 字段里面有【教師】這個(gè)關(guān)鍵字,而 description 字段里面沒有 【嘿嘿嘿】 這個(gè)關(guān)鍵字。
符合的文檔沒有
有符合的條件卻沒有查詢出文檔,
如圖,表示 is 這個(gè)查詢語句不支持通配符查詢
沒查出文檔,因?yàn)槭且笠粋€(gè)文檔的 name 字段要有【忍者】關(guān)鍵字,且 description 字段要有【首領(lǐng)】關(guān)鍵字才行
6、基于 ElasticsearchRestTemplate 的傳統(tǒng)的、非反應(yīng)式的同步版本API
就是 Dao 組件是繼承 【CrudRepository】這個(gè) API 接口來實(shí)現(xiàn) 【方法名關(guān)鍵字查詢】 和 【@Query查詢】的。
然后【自定義查詢方法】是基于 【ElasticsearchRestTemplate 】這個(gè)反應(yīng)式的 API 實(shí)現(xiàn)的。
上面的代碼,都是演示基于Reactive 反應(yīng)式的異步編程去操作 Elasticsearch 服務(wù)器,返回的都是 Flux 和 Mono 這種數(shù)據(jù)類型。
接下來演示的就是傳統(tǒng)的、非反應(yīng)式的同步編程,返回的數(shù)據(jù)就是我們熟悉的 List < Book > 集合
@EnableElasticsearchRepositories 注解介紹
@EnableElasticsearchRepositories 注解用于手動(dòng)啟用 Elasticsearch Repository 支持。
一旦程序顯式使用該注解,那 Spring Data Elasticsearch 的 Repository 自動(dòng)配置就會(huì)失效。
應(yīng)用場(chǎng)景: 當(dāng)需要連接多個(gè) Elasticsearch 索引庫時(shí)或進(jìn)行更多定制時(shí),可手動(dòng)使用該注解。
使用 @EnableElasticsearchRepositories 或 @EnableReactiveElasticsearchRepositories 時(shí)也要指定如下屬性:
basePackages: 指定掃描哪個(gè)包下的DAO組件(Repository組件)。
elasticsearchTemplateRef 或 reactiveElasticsearchTemplateRef:
指定基于哪個(gè) ElasticsearchRestTemplate 或 ReactiveElasticsearchTemplate 來實(shí)現(xiàn)Repository組件。
由此可見,Spring Data Elasticsearch 的 Respository 組件要依賴 RestTemplate,
而不是依賴 restClient ( restClient 就是指 RestHighLevelClient 或者 ReactiveElasticsearchClient)。
異步DAO組件 修改成 同步DAO組件(非反應(yīng)式)步驟
上面的反應(yīng)式是異步的,現(xiàn)在使用傳統(tǒng)的、非反應(yīng)式的同步組件來操作 Elasticsearch 服務(wù)器。
1、改為繼承 CrudRepository
2、方法返回值不再是 Flux 或 Mono,而是應(yīng)該改為 List 或 數(shù)據(jù)對(duì)象。
3、實(shí)現(xiàn)自定義方法時(shí),應(yīng)該依賴 ElasticsearchRestTemplate,而不是 ReactiveElasticsearchTemplate
4、同步方式連接 Elasticsearch 所使用的配置信息與反應(yīng)式 API 的連接方式是不同的。
代碼演示
就是拷貝上面的代碼,把反應(yīng)式異步的改成同步的操作。
application.properties 配置文件
反應(yīng)式的 API 連接 Elasticsearch 的方式和 同步的 API 連接 Elasticsearch 的方式是不一樣的
SyncBookDao 方法名關(guān)鍵字查詢和@Query查詢的DAO組件
其他沒變,就是返回值的數(shù)據(jù)類型改成用List集合來接收
SyncCustomBookDao 自定義查詢方法的DAO組件
SyncCustomBookDaoImpl 自定義查詢方法的實(shí)現(xiàn)類
SyncBookDaoTest 同步的測(cè)試類
同步方式測(cè)試結(jié)果
可以看出全部都測(cè)試通過文章來源:http://www.zghlxwxcb.cn/news/detail-838836.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-838836.html
完整代碼
Book 實(shí)體類
package cn.ljh.elasticsearch_boot.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
//實(shí)體類-和數(shù)據(jù)庫表進(jìn)行映射的類就叫實(shí)體類
@Document(indexName = "books")
@Getter
@Setter
@ToString
public class Book
{
//@Id:標(biāo)識(shí)這個(gè)字段為這個(gè)實(shí)體類的主鍵
@Id
//@Field:用于指定實(shí)體類中屬性與文檔字段的映射關(guān)系
@Field
private Integer id;
//使用 @Field 為字段指定 ik_max_word 和 ik_smart,是為了能通過該字段的內(nèi)容來進(jìn)行全文檢索
//"ik_max_word" 表示索引時(shí)使用 IK 分詞器的最細(xì)粒度切分; "ik_smart" 則表示搜索時(shí)使用 IK 分詞器的智能分詞模式
@Field(analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String name; //書名
@Field(analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String description; //內(nèi)容
@Field
private Double price; //價(jià)格
//無參構(gòu)造器
public Book()
{
}
//有參構(gòu)造器
public Book(Integer id, String name, String description, Double price)
{
this.id = id;
this.name = name;
this.description = description;
this.price = price;
}
}
application.properties 配置文件
# --------------------基于 Reactive 反應(yīng)式的異步方式連接Elasticsearch-----------------------
# 配置Elasticsearch服務(wù)器的地址
spring.data.elasticsearch.client.reactive.endpoints=127.0.0.1:9200
# 配置不使用SSL
spring.data.elasticsearch.client.reactive.use-ssl=false
# 連接超時(shí)時(shí)間
spring.data.elasticsearch.client.reactive.socket-timeout=10s
# 配置連接Elasticsearch服務(wù)器的用戶名、密碼
spring.data.elasticsearch.client.reactive.username=elastic
spring.data.elasticsearch.client.reactive.password=123456
# 查看 spring data elasticsearch 執(zhí)行查詢時(shí)的語句
logging.level.org.springframework.data.elasticsearch=debug
# --------------------傳統(tǒng)同步方式連接Elasticsearch-----------------------
# 配置Elasticsearch服務(wù)器的地址
spring.elasticsearch.rest.uris=http://127.0.0.1:9200
# 連接超時(shí)時(shí)間
spring.elasticsearch.rest.read-timeout=10s
# 配置連接Elasticsearch服務(wù)器的用戶名、密碼
spring.elasticsearch.rest.username=elastic
spring.elasticsearch.rest.password=123456
反應(yīng)式的異步方式代碼
BookDao 異步DAO組件
package cn.ljh.elasticsearch_boot.dao;
import cn.ljh.elasticsearch_boot.domain.Book;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;
import java.util.List;
/**
* Book 表示操作這個(gè)實(shí)體類; Integer:表示實(shí)體類Book的主鍵類型是Integer
* <Book,Integer>:表示告訴 ReactiveCrudRepository 接口,對(duì)Book這個(gè)實(shí)體類進(jìn)行數(shù)據(jù)庫操作。
* 在 BookDao 接口中就可以直接調(diào)用 ReactiveCrudRepository 接口提供的方法來對(duì) Book 實(shí)體進(jìn)行增刪改查等操作
*
* BookDao 繼承CustomBookDao 這個(gè)自定義的Dao接口,主要用來通過 BookDao 調(diào)用 CustomBookDao 里面的自定義查詢方法
*
* 繼承 ReactiveCrudRepository 接口的 Reactive 反應(yīng)式的異步編程的Dao組件
*
*/
public interface BookDao extends ReactiveCrudRepository<Book, Integer>,CustomBookDao
{
//這里使用的是響應(yīng)式的API,索引返回的類型是Flux
//Spring Data Elasticsearch 所提供的方法名關(guān)鍵字查詢---------------------------------------
//對(duì)name在這個(gè)字段進(jìn)行關(guān)鍵字查詢
Flux<Book> findByName(String term);
//對(duì)Description這個(gè)字段的關(guān)鍵詞做正則表達(dá)式查詢
Flux<Book> findByDescriptionMatches(String termPattern);
//對(duì) price 這個(gè)字段,做價(jià)格的范圍查詢
Flux<Book> findByPriceBetween(double start, double end);
//使用 in 運(yùn)算符,根據(jù)集合內(nèi)的id,查詢出對(duì)應(yīng)的文檔
Flux<Book> findByIdIn(List<Integer> ids);
//通過 @Query 來指定自定義的查詢語法:-------------------------------------------------------
//根據(jù)字段和關(guān)鍵字進(jìn)行全文檢索 -- ?0就是要查詢的字段;?1就是要查詢的關(guān)鍵字 ------ 不支持通配符查詢
// @Query("{ \"match\": { \"?0\": \"?1\" } } ")
//通過 query_string 來指定原生的 Lucene 查詢語法--------支持通配符查詢
@Query("{ \"query_string\": { \"query\": \"?0:?1\" } } ")
Flux<Book> findByQuery(String field, String term);
}
CustomBookDao 異步自定義DAO組件
package cn.ljh.elasticsearch_boot.dao;
import cn.ljh.elasticsearch_boot.domain.Book;
import reactor.core.publisher.Flux;
/**
* author JH 2024-02
*/
//自定義DAO接口,用來實(shí)現(xiàn)自定義查詢方法
public interface CustomBookDao
{
//方法作用:查詢一個(gè)文檔內(nèi), name 字段要包含 nameTerm 這個(gè)關(guān)鍵字 且 description 字段要包含 descTerm 這個(gè)關(guān)鍵字
Flux<Book> customQuery(String nameTerm, String descTerm);
}
CustomBookDaoImpl 異步自定義DAO組件實(shí)現(xiàn)類
package cn.ljh.elasticsearch_boot.dao.impl;
import cn.ljh.elasticsearch_boot.dao.CustomBookDao;
import cn.ljh.elasticsearch_boot.domain.Book;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import reactor.core.publisher.Flux;
/**
* author JH 2024-02
*/
public class CustomBookDaoImpl implements CustomBookDao
{
//ReactiveElasticsearchTemplate 能以更面向?qū)ο蟮姆椒▉聿僮?Elasticsearch 索引庫
private final ReactiveElasticsearchTemplate reactiveEsTemplate;
//通過構(gòu)造器完成依賴注入
public CustomBookDaoImpl(ReactiveElasticsearchTemplate reactiveEsTemplate)
{
this.reactiveEsTemplate = reactiveEsTemplate;
}
@Override
public Flux<Book> customQuery(String nameTerm, String descTerm)
{
//構(gòu)建了查詢條件 Criteria 對(duì)象;要求查詢的 "name" 字段值為 nameTerm,且 "description" 字段值為 descTerm
Criteria criteria = new Criteria("name").is(nameTerm)
.and("description").is(descTerm);
//通過 Criteria 對(duì)象構(gòu)建了 CriteriaQuery 對(duì)象,用于包裝查詢條件
CriteriaQuery criteriaQuery = new CriteriaQuery(criteria);
//執(zhí)行全文檢索查詢:參數(shù)1:要執(zhí)行的查詢語句; 參數(shù)2:要執(zhí)行的實(shí)體類型
//查詢結(jié)果以 Flux<SearchHit<Book>> 的形式返回,其中 SearchHit 包含了 Elasticsearch 中查詢到的相關(guān)數(shù)據(jù)
Flux<SearchHit<Book>> searchHitFlux = reactiveEsTemplate.search(criteriaQuery, Book.class);
//通過 map 操作將 SearchHit 轉(zhuǎn)換為 Book 對(duì)象,最終返回一個(gè) Flux<Book>,包含了符合查詢條件的 Book 對(duì)象流
Flux<Book> bookFlux = searchHitFlux.map(bookSearchHit -> bookSearchHit.getContent());
return bookFlux;
}
}
BookDaoTest 異步DAO組件方法的測(cè)試類
package cn.ljh.elasticsearch_boot.dao;
import cn.ljh.elasticsearch_boot.domain.Book;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.annotations.Query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* author JH 2024-02
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class BookDaoTest
{
@Autowired
private BookDao bookDao;
//---------------------------方法名關(guān)鍵字查詢方法------全自動(dòng)---------------------------------------------------
//添加文檔
@ParameterizedTest //參數(shù)測(cè)試
//多次測(cè)試中,每一次進(jìn)行測(cè)試時(shí)需要多個(gè)參數(shù),用這個(gè)注解
@CsvSource({
"11,火影忍者,旋渦鳴人成長為第七代火影的故事,150",
"12,家庭教師,廢材綱成長為十代首領(lǐng)的熱血事跡,200",
"13,七龍珠,孫悟空來到地球后的熱鬧景象,300",
"14,七龍珠Z,超級(jí)賽亞人貝吉塔來到地球后的熱鬧景象,400"
})
public void testSave(Integer id, String name, String description, double price)
{
Book book = new Book(id, name, description, price);
//反應(yīng)式API返回的值是 Mono
Mono<Book> result = bookDao.save(book);
//反應(yīng)式API,以同步阻塞的方式保證它執(zhí)行完成
result.blockOptional().ifPresent(System.err::println);
}
/**
* .blockOptional(): 這是 Mono 類的方法,在 Reactor 中用于將 Mono 對(duì)象轉(zhuǎn)換為一個(gè) Optional 對(duì)象,并以阻塞的方式等待 Mono 的完成。
* 如果 Mono 成功完成并且有值,那么會(huì)返回包含該值的 Optional 對(duì)象;
* 如果 Mono 為空或者出現(xiàn)錯(cuò)誤,則返回一個(gè)空的 Optional。
* 這個(gè)方法適用于需要立即獲取 Mono 結(jié)果并進(jìn)行后續(xù)處理的場(chǎng)景
*
* .blockOptional() 主要用于處理單個(gè)值的 Mono 對(duì)象,并以阻塞的方式獲取結(jié)果
*/
//根據(jù)文檔id刪除文檔
@ParameterizedTest
//多次測(cè)試中,每一次進(jìn)行測(cè)試時(shí)只需要1個(gè)參數(shù),用這個(gè)注解
@ValueSource(ints = {
13,
14
})
public void testDeleteById(Integer id)
{
//根據(jù)id刪除文檔的方法
bookDao.deleteById(id)
//反應(yīng)式API,以同步阻塞的方式保證它執(zhí)行完成
.blockOptional();
}
//對(duì) name 在這個(gè)字段進(jìn)行關(guān)鍵字查詢
@ParameterizedTest
@ValueSource(strings = {
"忍者",
"教師"
})
public void testFindByName(String term){
Flux<Book> bookFlux = bookDao.findByName(term);
//toIterable()方法是同步的,將一個(gè) Reactive Flux 轉(zhuǎn)換為普通的 Iterable 對(duì)象;
bookFlux.toIterable()
//遍歷打印
.forEach(System.err::println);
}
/**
* .toIterable(): 這是 Flux 類的方法,在 Reactor 中用于將 Flux 對(duì)象轉(zhuǎn)換為一個(gè) Iterable 對(duì)象,從而可以在同步代碼中對(duì) Flux 中的元素進(jìn)行迭代遍歷
* 使用 .toIterable() 方法會(huì)阻塞當(dāng)前線程直到 Flux 中的所有元素都被訂閱完畢,然后將這些元素包裝成 Iterable 對(duì)象返回
*
* .toIterable() 主要用于處理包含多個(gè)元素的 Flux 對(duì)象,并以阻塞的方式將所有元素轉(zhuǎn)換為 Iterable 對(duì)象
*/
//對(duì)description這個(gè)字段的關(guān)鍵詞做正則表達(dá)式查詢
@ParameterizedTest
@ValueSource(strings = {
// . 代表任何字符; + 代表匹配前面 . 這個(gè)字符可以出現(xiàn)一次到多次
//正則表達(dá)式要放在//斜杠中
"/熱.+/",
"/成.+/",
"/忍.+/"
})
public void testFindByDescriptionMatches(String termPattern)
{
Flux<Book> bookFlux = bookDao.findByDescriptionMatches(termPattern);
//toIterable()方法是同步的,將一個(gè) Reactive Flux 轉(zhuǎn)換為普通的 Iterable 對(duì)象;
bookFlux.toIterable()
//遍歷打印
.forEach(System.err::println);
}
//對(duì) price 這個(gè)字段,做價(jià)格的范圍查詢
@ParameterizedTest
@CsvSource({
"150,200",
"200,300"
})
public void testFindByPriceBetween(double start, double end)
{
Flux<Book> bookFlux = bookDao.findByPriceBetween(start, end);
//toIterable()方法是同步的,將一個(gè) Reactive Flux 轉(zhuǎn)換為普通的 Iterable 對(duì)象;
bookFlux.toIterable()
//遍歷打印
.forEach(System.err::println);
}
//使用 in 運(yùn)算符,根據(jù)集合內(nèi)的id,查詢出對(duì)應(yīng)的文檔
@ParameterizedTest
@CsvSource({
"11,14",
"11,12"
})
public void testFindByIdIn(Integer id1, Integer id2)
{
Flux<Book> bookFlux = bookDao.findByIdIn(List.of(id1,id2));
//toIterable()方法是同步的,將一個(gè) Reactive Flux 轉(zhuǎn)換為普通的 Iterable 對(duì)象;
bookFlux.toIterable()
//遍歷打印
.forEach(System.err::println);
}
//---------------------------@Query()查詢方法------半自動(dòng)---------------------------------------------------
//根據(jù)字段和關(guān)鍵字進(jìn)行全文檢索
@ParameterizedTest
@CsvSource({
"description,成長",
"description,熱*"
})
public void testFindByQuery(String field, String term)
{
Flux<Book> bookFlux = bookDao.findByQuery(field, term);
//toIterable()方法是同步的,將一個(gè) Reactive Flux 轉(zhuǎn)換為普通的 Iterable 對(duì)象;
bookFlux.toIterable()
//遍歷打印
.forEach(System.err::println);
}
//---------------------------自定義查詢方法----全手動(dòng)-----------------------------------------------------
//方法作用:查詢 name 字段要包含 nameTerm 這個(gè)關(guān)鍵字 和 description 字段要包含 descTerm 這個(gè)關(guān)鍵字
@ParameterizedTest
@CsvSource({
//查詢要求 name 字段里面有【忍者】這個(gè)關(guān)鍵字,且 description 字段里面有 【成長】 這個(gè)關(guān)鍵字
"忍者,成長",
"教師,成長",
"教師,嘿嘿嘿",
"忍者,成*",
"忍者,首領(lǐng)"
})
public void testCustomQuery(String nameTerm, String descTerm)
{
Flux<Book> bookFlux = bookDao.customQuery(nameTerm, descTerm);
bookFlux.toIterable().forEach(System.err::println);
}
}
傳統(tǒng)的同步方式代碼
SyncBookDao 同步DAO組件
package cn.ljh.elasticsearch_boot.dao;
import cn.ljh.elasticsearch_boot.domain.Book;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.repository.CrudRepository;
import reactor.core.publisher.Flux;
import java.util.List;
/**
* Book 表示操作這個(gè)實(shí)體類; Integer:表示實(shí)體類Book的主鍵類型是Integer
* <Book,Integer>:表示告訴 eCrudRepository 接口,對(duì)Book這個(gè)實(shí)體類進(jìn)行數(shù)據(jù)庫操作。
* 在 BookDao 接口中就可以直接調(diào)用 CrudRepository 接口提供的方法來對(duì) Book 實(shí)體進(jìn)行增刪改查等操作
*
* BookDao 繼承CustomBookDao 這個(gè)自定義的Dao接口,主要用來通過 BookDao 調(diào)用 CustomBookDao 里面的自定義查詢方法
*
*
* 繼承 CrudRepository 接口的傳統(tǒng)的、非反應(yīng)式的同步的Dao組件,
*
*/
public interface SyncBookDao extends CrudRepository<Book, Integer>, SyncCustomBookDao
{
//Spring Data Elasticsearch 所提供的方法名關(guān)鍵字查詢---------------------------------------
//對(duì)name在這個(gè)字段進(jìn)行關(guān)鍵字查詢
List<Book> findByName(String term);
//對(duì)Description這個(gè)字段的關(guān)鍵詞做正則表達(dá)式查詢
List<Book> findByDescriptionMatches(String termPattern);
//對(duì) price 這個(gè)字段,做價(jià)格的范圍查詢
List<Book> findByPriceBetween(double start, double end);
//使用 in 運(yùn)算符,根據(jù)集合內(nèi)的id,查詢出對(duì)應(yīng)的文檔
List<Book> findByIdIn(List<Integer> ids);
//通過 @Query 來指定自定義的查詢語法:-------------------------------------------------------
//根據(jù)字段和關(guān)鍵字進(jìn)行全文檢索 -- ?0就是要查詢的字段;?1就是要查詢的關(guān)鍵字 ------ 不支持通配符查詢
// @Query("{ \"match\": { \"?0\": \"?1\" } } ")
//通過 query_string 來指定原生的 Lucene 查詢語法--------支持通配符查詢
@Query("{ \"query_string\": { \"query\": \"?0:?1\" } } ")
List<Book> findByQuery(String field, String term);
}
SyncCustomBookDao 同步自定義DAO組件
package cn.ljh.elasticsearch_boot.dao;
import cn.ljh.elasticsearch_boot.domain.Book;
import reactor.core.publisher.Flux;
import java.util.List;
/**
* author JH 2024-02
*/
//自定義DAO接口,用來實(shí)現(xiàn)自定義查詢方法
public interface SyncCustomBookDao
{
//方法作用:查詢一個(gè)文檔內(nèi), name 字段要包含 nameTerm 這個(gè)關(guān)鍵字 且 description 字段要包含 descTerm 這個(gè)關(guān)鍵字
List<Book> customQuery(String nameTerm, String descTerm);
}
SyncCustomBookDaoImpl 同步自定義DAO組件實(shí)現(xiàn)類
package cn.ljh.elasticsearch_boot.dao.impl;
import cn.ljh.elasticsearch_boot.dao.CustomBookDao;
import cn.ljh.elasticsearch_boot.dao.SyncCustomBookDao;
import cn.ljh.elasticsearch_boot.domain.Book;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import java.util.ArrayList;
import java.util.List;
/**
* author JH 2024-02
*/
public class SyncCustomBookDaoImpl implements SyncCustomBookDao
{
//ElasticsearchRestTemplate 能以更面向?qū)ο蟮姆椒▉聿僮?Elasticsearch 索引庫
private final ElasticsearchRestTemplate esRestTemplate;
//通過構(gòu)造器完成依賴注入
public SyncCustomBookDaoImpl(ElasticsearchRestTemplate esRestTemplate)
{
this.esRestTemplate = esRestTemplate;
}
@Override
public List<Book> customQuery(String nameTerm, String descTerm)
{
//構(gòu)建了查詢條件 Criteria 對(duì)象;要求查詢的 "name" 字段值為 nameTerm,且 "description" 字段值為 descTerm
Criteria criteria = new Criteria("name").is(nameTerm)
.and("description").is(descTerm);
//通過 Criteria 對(duì)象構(gòu)建了 CriteriaQuery 對(duì)象,用于包裝查詢條件
CriteriaQuery criteriaQuery = new CriteriaQuery(criteria);
//執(zhí)行全文檢索查詢:參數(shù)1:要執(zhí)行的查詢語句; 參數(shù)2:要執(zhí)行的實(shí)體類型
//search方法的返回值是 SearchHit<Book> ,該 API 帶了s,相當(dāng)于是一個(gè)集合
SearchHits<Book> searchHits = esRestTemplate.search(criteriaQuery, Book.class);
List<Book> bookList = new ArrayList<>();
//bookSearchHit 的類型是 SearchHit<Book>
searchHits.forEach(bookSearchHit -> bookList.add(bookSearchHit.getContent()));
return bookList;
}
}
SyncBookDaoTest 同步DAO組件方法的測(cè)試類
package cn.ljh.elasticsearch_boot.dao;
import cn.ljh.elasticsearch_boot.domain.Book;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* author JH 2024-02
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SyncBookDaoTest
{
@Autowired
private SyncBookDao syncBookDao;
//---------------------------方法名關(guān)鍵字查詢方法------全自動(dòng)---------------------------------------------------
//添加文檔
@ParameterizedTest //參數(shù)測(cè)試
//多次測(cè)試中,每一次進(jìn)行測(cè)試時(shí)需要多個(gè)參數(shù),用這個(gè)注解
@CsvSource({
"21,火影忍者,旋渦鳴人成長為第七代火影的故事,150",
"22,家庭教師,廢材綱成長為十代首領(lǐng)的熱血事跡,200",
"23,七龍珠,孫悟空來到地球后的熱鬧景象,300",
"24,七龍珠Z,超級(jí)賽亞人貝吉塔來到地球后的熱鬧景象,400"
})
public void testSave(Integer id, String name, String description, double price)
{
Book book = new Book(id, name, description, price);
Book b = syncBookDao.save(book);
System.err.println(b);
}
//根據(jù)文檔id刪除文檔
@ParameterizedTest
//多次測(cè)試中,每一次進(jìn)行測(cè)試時(shí)只需要1個(gè)參數(shù),用這個(gè)注解
@ValueSource(ints = {
13,
14
})
public void testDeleteById(Integer id)
{
//根據(jù)id刪除文檔的方法
syncBookDao.deleteById(id);
}
//對(duì) name 在這個(gè)字段進(jìn)行關(guān)鍵字查詢
@ParameterizedTest
@ValueSource(strings = {
"忍者",
"教師"
})
public void testFindByName(String term)
{
List<Book> books = syncBookDao.findByName(term);
books.forEach(System.err::println);
}
//對(duì)description這個(gè)字段的關(guān)鍵詞做正則表達(dá)式查詢
@ParameterizedTest
@ValueSource(strings = {
// . 代表任何字符; + 代表匹配前面 . 這個(gè)字符可以出現(xiàn)一次到多次
//正則表達(dá)式要放在//斜杠中
"/熱.+/",
"/成.+/",
"/忍.+/"
})
public void testFindByDescriptionMatches(String termPattern)
{
List<Book> books = syncBookDao.findByDescriptionMatches(termPattern);
books.forEach(System.err::println);
}
//對(duì) price 這個(gè)字段,做價(jià)格的范圍查詢
@ParameterizedTest
@CsvSource({
"150,200",
"200,300"
})
public void testFindByPriceBetween(double start, double end)
{
List<Book> books = syncBookDao.findByPriceBetween(start, end);
books.forEach(System.err::println);
}
//使用 in 運(yùn)算符,根據(jù)集合內(nèi)的id,查詢出對(duì)應(yīng)的文檔
@ParameterizedTest
@CsvSource({
"11,14",
"11,12"
})
public void testFindByIdIn(Integer id1, Integer id2)
{
List<Book> books = syncBookDao.findByIdIn(List.of(id1, id2));
books.forEach(System.err::println);
}
//---------------------------@Query()查詢方法------半自動(dòng)---------------------------------------------------
//根據(jù)字段和關(guān)鍵字進(jìn)行全文檢索
@ParameterizedTest
@CsvSource({
"description,成長",
"description,熱*"
})
public void testFindByQuery(String field, String term)
{
List<Book> books = syncBookDao.findByQuery(field, term);
books.forEach(System.err::println);
}
//---------------------------自定義查詢方法----全手動(dòng)-----------------------------------------------------
//方法作用:查詢 name 字段要包含 nameTerm 這個(gè)關(guān)鍵字
// 且 description 字段要包含 descTerm 這個(gè)關(guān)鍵字
@ParameterizedTest
@CsvSource({
//查詢要求 name 字段里面有【忍者】這個(gè)關(guān)鍵字,
// 且 description 字段里面有 【成長】 這個(gè)關(guān)鍵字
"忍者,成長",
"教師,成長",
"教師,嘿嘿嘿",
"忍者,成*",
"忍者,首領(lǐng)"
})
public void testCustomQuery(String nameTerm, String descTerm)
{
List<Book> books = syncBookDao.customQuery(nameTerm, descTerm);
books.forEach(System.err::println);
}
}
pom.xml 依賴
<?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 https://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.5.3</version>
</parent>
<groupId>cn.ljh</groupId>
<artifactId>elasticsearch_boot</artifactId>
<version>1.0.0</version>
<name>elasticsearch_boot</name>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 這個(gè)依賴會(huì)傳遞依賴 elasticsearch Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
到了這里,關(guān)于18、全文檢索--Elasticsearch-- SpringBoot 整合 Spring Data Elasticsearch(異步方式(Reactive)和 傳統(tǒng)同步方式 分別操作ES的代碼演示)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!