javascript
Spring Boot Elasticsearch 入门
1. 概述
如果胖友之前有用過 Elasticsearch 的話,可能有過被使用的 Elasticsearch 客戶端版本搞死搞活。如果有,那么一起握個抓。所以,我們在文章的開始,先一起理一理這塊。
Elasticsearch(ES)提供了兩種連接方式:
-
transport :通過 TCP 方式訪問 ES 。
對應的庫是?org.elasticsearch.client.transport?。
-
rest :通過 HTTP API 方式訪問 ES 。
對應的庫是:
-
elasticsearch-rest-client?+?org.elasticsearch.client.rest,提供 low-level rest API 。
-
elasticsearch-rest-high-level-client?,提供 high-level rest API 。從 Elasticsearch 6.0.0-beta1 開始提供。
-
如果想進一步了解上述的 3 個 ES 客戶端,可以看看 《Elasticsearch 客戶端 transport vs rest》 文章。
雖然說,ES 提供了 2 種方式,官方目前建議使用?rest?方式,而不是 transport 方式。并且,transport 在未來的計劃中,準備廢棄。并且,阿里云提供的 Elasticsearch 更加干脆,直接只提供?rest?方式,而不提供 transport 方式。
在社區中,有個 Jest 開源項目,也提供了的 Elasticsearch REST API 客戶端。
參考 《ES Java Client 的歷史》
Elasticsearch 5.0 才有了自己的 Rest 客戶端 ,6.0 才有了更好用的客戶端,所以 Jest 作為第三方客戶端,使用非常廣泛。
正如我們在項目中,編寫數據庫操作的邏輯,使用 MyBatis 或者 JPA 為主,而不使用原生的 JDBC 。那么,我們在編寫 Elasticsearch 操作的邏輯,也不直接使用上述的客戶端,而是:
-
spring-data-elasticsearch?,基于 Elasticsearch transport 客戶端封裝。
-
spring-data-jest?,基于 Jest 客戶端封裝。
雖然這兩者底層使用的不同客戶端,但是都基于 Spring Data 體系,所以項目在使用時,編寫的代碼是相同的。也因此,如果胖友想從?spring-data-elasticsearch?遷移到?spring-data-jest?時,基本透明無成本,美滋滋~
😈 這樣一個梳理,胖友是不是對 Elasticsearch 客戶端的選用,心里已經明明白白落。嘿嘿。
下面,艿艿先來講一個悲傷的故事。大體過程是這樣的:
-
1、在上線的時候,發現線上使用的是阿里云的 Elasticsearch 服務,不提供 transport 連接方式,而項目中使用的是?spring-data-elasticsearch?。所以,先臨時在 ECS 搭建一個 Elasticsearch 服務,過度下。
-
2、然后,了解到有 Jest 客戶端可以使用 rest 連接方式,于是將操作 Elasticsearch 的代碼,全部改成 Jest 的方式。
-
3、之后,發現竟然還有?spring-data-jest?,一臉悲傷,重新將操作 Elasticsearch 的代碼,全部改成?spring-data-jest?的方式。
淚崩。所以胖友可知,艿艿目前線上使用?spring-data-jest?訪問 Elasticsearch 。
下面,我們分別來入門:
-
Spring Data Jest
-
Spring Data Elasticsearch
艿艿:如果胖友還沒安裝 Elasticsearch ,并安裝 IK 插件,可以參考下 《Elasticsearch 安裝部署》 文章,先進行下安裝。
2. Spring Data Jest
示例代碼對應倉庫:lab-15-spring-data-jest 。
-
ES 版本號:6.5.0
-
spring-boot-starter-data-jest?:3.2.5.RELEASE
本小節,我們會使用?spring-boot-starter-data-jest?自動化配置 Spring Data Jest 主要配置。同時,編寫相應的 Elasticsearch 的 CRUD 操作。
2.1 引入依賴
在?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/><!-- lookup parent from repository --></parent><modelVersion>4.0.0</modelVersion><artifactId>lab-15-spring-data-jest</artifactId><dependencies><!-- 自動化配置 Spring Data Jest --><dependency><groupId>com.github.vanroy</groupId><artifactId>spring-boot-starter-data-jest</artifactId><version>3.2.5.RELEASE</version></dependency><!-- 方便等會寫單元測試 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></project>具體每個依賴的作用,胖友自己認真看下艿艿添加的所有注釋噢。
2.2 Application
創建?Application.java?類,配置?@SpringBootApplication?注解即可。代碼如下:
// Application.java@SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class}) publicclass Application { }-
需要排除 ElasticsearchAutoConfiguration 和 ElasticsearchDataAutoConfiguration 自動配置類,否則會自動配置 Spring Data Elasticsearch 。
2.3 配置文件
在?application.yml?中,添加 Jest 配置,如下:
spring:data:# Jest 配置項jest:uri:http://127.0.0.1:9200-
我們使用本地的 ES 服務。默認情況下,ES rest 連接方式暴露的端口是 9200 。
2.4 ESProductDO
在?cn.iocoder.springboot.lab15.springdatajest.dataobject?包路徑下,創建 ESProductDO 類。代碼如下:
// ESProductDO.java@Document(indexName = "product", // 索引名type = "product", // 類型。未來的版本即將廢棄shards = 1, // 默認索引分區數replicas = 0, // 每個分區的備份數refreshInterval = "-1"// 刷新間隔 ) public class ESProductDO {/*** ID 主鍵*/@Idprivate Integer id;/*** SPU 名字*/@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)private String name;/*** 賣點*/@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)private String sellPoint;/*** 描述*/@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)private String description;/*** 分類編號*/private Integer cid;/*** 分類名*/@Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)private String categoryName;// 省略 setting/getting 方法 }-
為了區別關系數據庫的實體對象,艿艿習慣以 ES 前綴開頭。
- 字段上的?@Field?注解的 FieldAnalyzer ,是定義的枚舉類,記得自己創建下噢。代碼如下: // FieldAnalyzer.javapublic class FieldAnalyzer {/*** IK 最大化分詞** 會將文本做最細粒度的拆分*/publicstaticfinal String IK_MAX_WORD = "ik_max_word";/*** IK 智能分詞** 會做最粗粒度的拆分*/publicstaticfinal String IK_SMART = "ik_smart";}
-
再友情提示下,一定要記得給 Elasticsearch 安裝 IK 插件,不然等會示例會報錯的喲。
-
2.5 ProductRepository
在?cn.iocoder.springboot.lab15.mybatis.repository?包路徑下,創建 ProductRepository 接口。代碼如下:
// ProductRepository.javapublic interface ProductRepository extends ElasticsearchRepository<ESProductDO, Integer> {}-
繼承?org.springframework.data.elasticsearch.repository.ProductRepository?接口,第一個泛型設置對應的實體是 ESProductDO ,第二個泛型設置對應的主鍵類型是 Integer 。
-
因為實現了 ElasticsearchRepository 接口,Spring Data Jest 會自動生成對應的 CRUD 等等的代碼。😈 是不是很方便。
-
ElasticsearchRepository 類圖如下:
-
每個接口定義的方法,胖友可以點擊下面每個鏈接,自己瞅瞅,簡單~
-
org.springframework.data.repository.CrudRepository
-
org.springframework.data.repository.PagingAndSortingRepository
-
org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository
-
org.springframework.data.elasticsearch.repository.ElasticsearchRepository
-
艿艿:如果胖友看過艿艿寫的 《芋道 Spring Boot JPA 入門》 文章,會發現和 Spring Data JPA 的使用方式,基本一致。這就是 Spring Data 帶給我們的好處,使用相同的 API ,統一訪問不同的數據源。o( ̄▽ ̄)d 點贊。
2.6 簡單測試
創建 ProductRepositoryTest 測試類,我們來測試一下簡單的 ProductRepositoryTest 的每個操作。代碼如下:
// ProductRepositoryTest.JAVA@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepositoryTest {@Autowiredprivate ProductRepository productRepository;@Test// 插入一條記錄public void testInsert() {ESProductDO product = new ESProductDO();product.setId(1); // 一般 ES 的 ID 編號,使用 DB 數據對應的編號。這里,先寫死product.setName("芋道源碼");product.setSellPoint("愿半生編碼,如一生老友");product.setDescription("我只是一個描述");product.setCid(1);product.setCategoryName("技術");productRepository.save(product);}// 這里要注意,如果使用 save 方法來更新的話,必須是全量字段,否則其它字段會被覆蓋。// 所以,這里僅僅是作為一個示例。@Test// 更新一條記錄public void testUpdate() {ESProductDO product = new ESProductDO();product.setId(1);product.setCid(2);product.setCategoryName("技術-Java");productRepository.save(product);}@Test// 根據 ID 編號,刪除一條記錄public void testDelete() {productRepository.deleteById(1);}@Test// 根據 ID 編號,查詢一條記錄public void testSelectById() {Optional<ESProductDO> userDO = productRepository.findById(1);System.out.println(userDO.isPresent());}@Test// 根據 ID 編號數組,查詢多條記錄public void testSelectByIds() {Iterable<ESProductDO> users = productRepository.findAllById(Arrays.asList(1, 4));users.forEach(System.out::println);}}-
每個測試單元方法,胖友自己看看方法上的注釋。
具體的,胖友可以自己跑跑,妥妥的。
3. Spring Data Elasticsearch
示例代碼對應倉庫:lab-15-spring-data-elasticsearch 。
-
ES 版本號:6.5.0
-
spring-boot-starter-data-elasticsearch?:2.1.3.RELEASE
本小節,我們會使用?spring-boot-starter-data-elasticsearch?自動化配置 Spring Data Elasticsearch 主要配置。同時,編寫相應的 Elasticsearch 的 CRUD 操作。
重點是,我們希望通過本小節,讓胖友感受到,Spring Data Jest 和 Spring Data Elasticsearch 是基本一致的。
3.1 引入依賴
在?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/><!-- lookup parent from repository --></parent><modelVersion>4.0.0</modelVersion><artifactId>lab-15-spring-data-elasticsearch</artifactId><dependencies><!-- 自動化配置 Spring Data Elasticsearch --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!-- 方便等會寫單元測試 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></project>-
差異點,就是將依賴?spring-boot-starter-data-jest?替換成?spring-boot-starter-data-elasticsearch?。
具體每個依賴的作用,胖友自己認真看下艿艿添加的所有注釋噢。
3.2 Application
創建?Application.java?類,配置?@SpringBootApplication?注解即可。代碼如下:
// Application.java@SpringBootApplication public class Application { }-
差異點,在于無需排除 ElasticsearchAutoConfiguration 和 ElasticsearchDataAutoConfiguration 自動配置類,因為需要它們自動化配置 Spring Data Elasticsearch 。
3.3 配置文件
在?application.yml?中,添加 Jest 配置,如下:
spring:data:# Elasticsearch 配置項elasticsearch:cluster-name:elasticsearch# 集群名cluster-nodes:127.0.0.1:9300# 集群節點-
差異點,將配置項?jest?替換成?elasticsearch?。
-
我們使用本地的 ES 服務。默認情況下,ES transport 連接方式暴露的端口是 9300 。
3.4 ESProductDO
和 「2.4 ESProductDO」 一致。
3.5 ProductRepository
和 「3.5 ProductRepository」 一致。
3.6 簡單測試
和 「3.6 簡單測試」 一致。
😈 有沒感受到,Spring Data Jest 和 Spring Data Elasticsearch 是基本一致的。
4. 基于方法名查詢
示例代碼對應倉庫:lab-15-spring-data-jest 。
-
ES 版本號:6.5.0
-
spring-boot-starter-data-jest?:3.2.5.RELEASE
在 《芋道 Spring Boot JPA 入門》 文章的「4. 基于方法名查詢」小節中,我們已經提到:
在 Spring Data 中,支持根據方法名作生成對應的查詢(WHERE)條件,進一步進化我們使用 JPA ,具體是方法名以?findBy、existsBy、countBy、deleteBy?開頭,后面跟具體的條件。具體的規則,在 《Spring Data JPA —— Query Creation》 文檔中,已經詳細提供。如下:
| And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
| Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
| Is,?Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
| Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
| LessThan | findByAgeLessThan | … where x.age < ?1 |
| LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
| GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
| GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
| After | findByStartDateAfter | … where x.startDate > ?1 |
| Before | findByStartDateBefore | … where x.startDate < ?1 |
| IsNull,?Null | findByAge(Is)Null | … where x.age is null |
| IsNotNull,?NotNull | findByAge(Is)NotNull | … where x.age not null |
| Like | findByFirstnameLike | … where x.firstname like ?1 |
| NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
| StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1?(parameter bound with appended?%) |
| EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1?(parameter bound with prepended?%) |
| Containing | findByFirstnameContaining | … where x.firstname like ?1?(parameter bound wrapped in?%) |
| OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
| Not | findByLastnameNot | … where x.lastname <> ?1 |
| In | findByAgeIn(Collection ages) | … where x.age in ?1 |
| NotIn | findByAgeNotIn(Collection ages) | … where x.age not in ?1 |
| True | findByActiveTrue() | … where x.active = true |
| False | findByActiveFalse() | … where x.active = false |
| IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
-
注意,如果我們有排序需求,可以使用?OrderBy?關鍵字。
下面,我們來編寫一個簡單的示例。
艿艿:IDEA 牛逼,提供的插件已經能夠自動提示上述關鍵字。太強了~
因為 Spring Data Elasticsearch 和 Spring Data Jest 也是 Spring Data 體系中的一員,所以也能享受到基于方法名查詢的福利。所以,我們在本小節中,我們也來嘗試下。
我們會在 「2. Spring Data Jest」 的示例代碼對應倉庫 lab-15-spring-data-jest 的基礎上,進行本小節的示例。
4.1 ProductRepository02
在?cn.iocoder.springboot.lab15.springdatajest.repository?包路徑下,創建 UserRepository02 接口。代碼如下:
// ProductRepository02.javapublic interface ProductRepository02 extends ElasticsearchRepository<ESProductDO, Integer> {ESProductDO findByName(String name);Page<ESProductDO> findByNameLike(String name, Pageable pageable);}-
對于分頁操作,需要使用到 Pageable 參數,需要作為方法的最后一個參數。
4.2 簡單測試
創建 UserRepository02Test 測試類,我們來測試一下簡單的 UserRepository02Test 的每個操作。代碼如下:
// UserRepository02Test.java@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository02Test {@Autowiredprivate ProductRepository02 productRepository;@Test// 根據名字獲得一條記錄public void testFindByName() {ESProductDO product = productRepository.findByName("芋道源碼");System.out.println(product);}@Test// 使用 name 模糊查詢,分頁返回結果public void testFindByNameLike() {// 根據情況,是否要制造測試數據if (true) {testInsert();}// 創建排序條件Sort sort = new Sort(Sort.Direction.DESC, "id"); // ID 倒序// 創建分頁條件。Pageable pageable = PageRequest.of(0, 10, sort);// 執行分頁操作Page<ESProductDO> page = productRepository.findByNameLike("芋道", pageable);// 打印System.out.println(page.getTotalElements());System.out.println(page.getTotalPages());}/*** 為了給分頁制造一點數據*/private void testInsert() {for (int i = 1; i <= 100; i++) {ESProductDO product = new ESProductDO();product.setId(i); // 一般 ES 的 ID 編號,使用 DB 數據對應的編號。這里,先寫死product.setName("芋道源碼:" + i);product.setSellPoint("愿半生編碼,如一生老友");product.setDescription("我只是一個描述");product.setCid(1);product.setCategoryName("技術");productRepository.save(product);}}}-
每個測試單元方法,胖友自己看看方法上的注釋。
具體的,胖友可以自己跑跑,妥妥的。
5. 復雜查詢
在一些業務場景下,我們需要編寫相對復雜的查詢,例如說類似京東 https://search.jd.com/Search?keyword=華為手機 搜索功能,需要支持關鍵字、分類、品牌等等,并且可以按照綜合、銷量等等升降序排序,那么我們就無法在上面看到的 Spring Data Repository 提供的簡單的查詢方法,而需要使用到 ElasticsearchRepository 的 search 方法,代碼如下:
// ElasticsearchRepository.java // 省略非 search 方法Page<T> search(QueryBuilder query, Pageable pageable);Page<T> search(SearchQuery searchQuery);Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);此時,我們就需要使用 QueryBuilder 和 SearchQuery 構建相對復雜的搜索和排序條件。所以,我們繼續在 「2. Spring Data Jest」 的示例代碼對應倉庫 lab-15-spring-data-jest 的基礎上,進行本小節的示例,實現一個簡單的商品搜索功能。
5.1 ProductRepository03
在?cn.iocoder.springboot.lab15.springdatajest.repository?包路徑下,創建 ProductRepository03 接口。代碼如下:
// ProductRepository03.javapublic interface ProductRepository03 extends ElasticsearchRepository<ESProductDO, Integer> {default Page<ESProductDO> search(Integer cid, String keyword, Pageable pageable) {// <1> 創建 NativeSearchQueryBuilder 對象NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();// <2.1> 篩選條件 cidif (cid != null) {nativeSearchQueryBuilder.withFilter(QueryBuilders.termQuery("cid", cid));}// <2.2> 篩選if (StringUtils.hasText(keyword)) {FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { // TODO 芋艿,分值隨便打的new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("name", keyword),ScoreFunctionBuilders.weightFactorFunction(10)),new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("sellPoint", keyword),ScoreFunctionBuilders.weightFactorFunction(2)),new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("categoryName", keyword),ScoreFunctionBuilders.weightFactorFunction(3)), // new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("description", keyword), // ScoreFunctionBuilders.weightFactorFunction(2)), // TODO 芋艿,目前這么做,如果商品描述很長,在按照價格降序,會命中超級多的關鍵字。};FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions).scoreMode(FunctionScoreQuery.ScoreMode.SUM) // 求和.setMinScore(2F); // TODO 芋艿,需要考慮下 scorenativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);}// 排序if (StringUtils.hasText(keyword)) { // <3.1> 關鍵字,使用打分nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));} elseif (pageable.getSort().isSorted()) { // <3.2> 有排序,則進行拼接pageable.getSort().get().forEach(sortField -> nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField.getProperty()).order(sortField.getDirection().isAscending() ? SortOrder.ASC : SortOrder.DESC)));} else { // <3.3> 無排序,則按照 ID 倒序nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));}// <4> 分頁nativeSearchQueryBuilder.withPageable(PageRequest.of(pageable.getPageNumber(), pageable.getPageSize())); // 避免// <5> 執行查詢return search(nativeSearchQueryBuilder.build());}}-
使用 QueryBuilder 和 SearchQuery 構建相對復雜的搜索和排序條件,我們可以放在 Service 層,也可以放在 Repository 層。艿艿個人的偏好放在 Repository 層。
-
主要原因是,盡量避免數據層的操作暴露在 Service 層。
-
缺點呢,就像我們這里看到的,有點業務邏輯就到了 Repository 層。
-
😈 有舍有得,看個人喜好。翻了一些開源項目,放在 Service 或 Repository 層的都有。
-
-
簡單來說下這個方法的整體邏輯,根據商品分類編號 + 關鍵字,檢索相應的商品,分頁返回結果。
-
<1>?處,創建 NativeSearchQueryBuilder 對象。
-
篩選條件
-
<2.1>?處,如果有分類編號?cid?,則進行篩選。
-
<2.2>?處,如果有關鍵字?keyword?,則按照?name?10 分、sellPoint?2 分、categoryName?3 分,計算求和,篩選至少滿足 2 分。
-
-
排序條件
-
<3.1>?處,如果有關鍵字,則按照打分結果降序。
-
<3.2>?處,如果有排序條件,則按照該排序即可。
-
<3.3>?處,如果無排序條件,則按照 ID 編號降序。
-
-
分頁條件
-
<4>?處,創建新的 PageRequest 對象,避免?pageable?里原有的排序條件。
-
-
執行搜索
-
<5>?處,調用?#search(SearchQuery searchQuery)?方法,執行 Elasticsearch 搜索。
-
5.2 ProductRepository03Test
創建 ProductRepository03Test 測試類,我們來測試一下簡單的 UserRepository03Test 的每個操作。代碼如下:
// ProductRepository03Test.java@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository03Test {@Autowiredprivate ProductRepository03 productRepository;@Testpublic void testSearch() {// 查找分類為 1 + 指定關鍵字,并且按照 id 升序Page<ESProductDO> page = productRepository.search(1, "技術",PageRequest.of(0, 5, Sort.Direction.ASC, "id"));System.out.println(page.getTotalPages());// 查找分類為 1 ,并且按照 id 升序page = productRepository.search(1, null,PageRequest.of(0, 5, Sort.Direction.ASC, "id"));System.out.println(page.getTotalPages());}}-
每個測試單元方法,胖友自己看看方法上的注釋。
具體的,胖友可以自己跑跑,妥妥的。
6. ElasticsearchTemplate
在 Spring Data Elasticsearch 中,有一個 ElasticsearchTemplate 類,提供了 Elasticsearch 操作模板,方便我們操作 Elasticsearch 。
😈 要注意,這是 Spring Data Elasticsearch 獨有,而 Spring Data Jest 沒有的一個類。
咳咳咳,當艿艿寫完這篇博客后,突然發現,Spring Data Jest 有一個 JestElasticsearchTemplate 類,和 ElasticsearchTemplate 是對等的。
也因此,我們繼續在 「3. Spring Data Elasticsearch」 的示例代碼對應倉庫 lab-15-spring-data-elasticsearch 的基礎上,進行本小節的示例,實現一個商品搜索條件返回的功能。
6.1 ProductConditionBO
在?cn.iocoder.springboot.lab15.springdataelasticsearch.bo?包路徑下,創建 ProductConditionBO 類,商品搜索條件 BO ,代碼如下:
// ProductConditionBO.javapublic class ProductConditionBO {/*** 商品分類數組*/private List<Category> categories;publicstaticclass Category {/*** 分類編號*/private Integer id;/*** 分類名稱*/private String name;// ... 省略 setting/getting 方法}// ... 省略 setting/getting 方法}6.2 簡單示例
創建 ProductRepository04Test 測試類,我們來測試一下簡單的 ProductRepository04Test 的每個操作。代碼如下:
// ProductRepository04Test.java@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class ProductRepository04Test {@Autowiredprivate ElasticsearchTemplate elasticsearchTemplate;@Testpublic void test() {// <1> 創建 ES 搜索條件NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withIndices("product");;// <2> 篩選nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery("芋道","name", "sellPoint", "categoryName"));// <3> 聚合nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("cids").field("cid")); // 商品分類// <4> 執行查詢ProductConditionBO condition = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), response -> {ProductConditionBO result = new ProductConditionBO();// categoryIds 聚合Aggregation categoryIdsAggregation = response.getAggregations().get("cids");if (categoryIdsAggregation != null) {result.setCategories(new ArrayList<>());for (LongTerms.Bucket bucket : (((LongTerms) categoryIdsAggregation).getBuckets())) {result.getCategories().add(new ProductConditionBO.Category().setId(bucket.getKeyAsNumber().intValue()));}}// 返回結果return result;});// <5> 后續遍歷 condition.categories 數組,查詢商品分類,設置商品分類名。System.out.println();}}-
簡單來說下這個方法的整體邏輯,根據關鍵字檢索?name、sellPoint、categoryName?字段,聚合?cid?返回。
-
<1>?處,創建 NativeSearchQueryBuilder 對象,并設置查詢的索引是?product?,即 ESProductDO 類的對應的索引。
-
篩選條件
-
<2>?處,根據關鍵字檢索?name、sellPoint、categoryName?字段。此處,我們使用的關鍵字是?"芋道"?。
-
-
聚合
-
<3>?處,將商品分類編號?cid?聚合成?cids?返回。
-
如果 ESProductDO 上有品牌編號,我們可以多在聚合一個品牌編號返回。
-
-
執行搜索
-
<4>?處,執行查詢,解析聚合結果,設置回 ProductConditionBO 中。
-
-
<5>?處,后續遍歷?condition.categories?數組,查詢商品分類,設置商品分類名。
6.3 小結
可能胖友會有疑惑?Spring Data Jest 沒有 ElasticsearchTemplate 類,豈不是不能實現當前示例么?答案是否定的,我們回過頭看 「5. 復雜查詢」 。對于 Spring Data Jest 來說,可以通過 ElasticsearchRepository 提供的 search 方法,實現聚合操作的功能。當然,Spring Data Elasticsearch 也可以。
所以呢,絕大多數情況下,我們并不會直接使用 ElasticsearchTemplate 類。
666. 彩蛋
通過寫這篇文章,艿艿自己也查了一些資料,終于把 Elasticsearch 客戶端的情況理順了。😈 當然,也推薦幾篇艿艿覺得不錯的 Elasticsearch 文章:
-
《圖解 Elasticsearch 原理》
-
《全文搜索引擎選 ElasticSearch 還是 Solr?》
-
《別再說你不會 ElasticSearch 調優了,都給你整理好了》
-
《Elasticsearch 如何做到億級數據查詢毫秒級返回?》
-
《日均 5 億查詢量的京東訂單中心,為什么舍 MySQL 用 ES ?》
另外,在推薦一個 Chrome ElasticSearch Head 插件,可用于監控 Elasticsearch 狀態的客戶端,提供數據可視化、執行增刪改查操作等等功能。
總結
以上是生活随笔為你收集整理的Spring Boot Elasticsearch 入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 绝了!一个妹子 rm -rf 把公司整个
- 下一篇: gradle idea java ssm