ElasticSearch 學習筆記(四)-----ES在SpringBoot中的集成以及項目應用開發(fā)指南
概述
接上一篇ElasticSearch 學習筆記(三)-----ES的設計原理以及分詞器說明。今天我們主要介紹ES 與SpringBoot 的集成以及項目應用開發(fā)指南。
ES與SpringBoot的集成
添加依賴
此處,添加的依賴一定要與你安裝的ES的版本對應,因為我安裝的ES版本是 6.4.3。查詢
ElasticSearch版本對應關系 發(fā)現(xiàn)對應的spring-data-elasticsearch 為 3.1.x
在SpringBoot 中對應依賴spring-boot-starter-data-elasticsearch 的版本為 2.1.1.RELEASE
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
創(chuàng)建文檔
@Data
@Document(indexName = “titleindex”,type = “_doc”)
public class TitleDocument implements Serializable {
/**
*
*/
@Id
private Long id;
/**
* 選中ik 分詞器
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String titlename;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String description;
public TitleDTO() {
}
public TitleDTO(Long id, String titlename, String description) {
this.id = id;
this.titlename = titlename;
this.description = description;
}
}
其中 indexName 與type 分別對應 索引名稱和索引類型,如果類比于關系型數據庫的話就相當于 庫和表。在Spring Data 中每個文檔必須要有id,不然數據會插入失敗。同時還需要有缺省構造器,不然分頁查詢會報錯。
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
@Field 注解作用到屬性之上,上面代碼表示的意思是,索引時分詞使用 ik_max_word, 搜索時分詞使用ik_smart
PS :
1、ik_max_word
會將文本做最細粒度的拆分,比如會將“中華人民共和國人民大會堂”拆分為“中華人民共和國、中華人民、中華、華人、人民共和國、人民、共和國、大會堂、大會、會堂等詞語。
2、ik_smart
會做最粗粒度的拆分,比如會將“中華人民共和國人民大會堂”拆分為中華人民共和國、人民大會堂。
Dao 層實現(xiàn)
@Component
public interface TitleRepository extends ElasticsearchRepository<TitleDTO,Long> {
}
此處我們直接繼承了ElasticsearchRepository接口,該接口實現(xiàn)了 save ,deleteById,search 等方法,其中save 表示新增或者更新索引,deleteById表示刪除索引,search表示查詢索引數據。
Service 調用實現(xiàn)
@Autowired
private TitleRepository titleRepository;
/**
* 創(chuàng)建索引
* @param titlename
* @param description
*/
public void save(Long id,String titlename,String description) throws NoSuchAlgorithmException {
TitleDocument titleDocument = new TitleDocument(id, titlename+id, description+id);
titleRepository.save(titleDocument);
}
/**
* 刪除索引數據
* @param id
* @return
*/
public boolean delete(Long id) {
titleRepository.deleteById(id);
return true;
}
/**
* 查詢所有的文章
* @param searchContent
* @return
*/
public List<TitleDocument> getTitle(String searchContent) {
QueryStringQueryBuilder builder = new QueryStringQueryBuilder(searchContent);
System.out.println("查詢的語句:"+builder);
Iterable<TitleDocument> searchResult = titleRepository.search(builder);
Iterator<TitleDocument> iterator = searchResult.iterator();
List<TitleDocument> list=new ArrayList<>();
while (iterator.hasNext()) {
list.add(iterator.next());
}
return list;
}
/**
* 分頁查詢
* @param pageNumber
* @param pageSize
* @param searchContent
* @return
*/
public List<TitleDocument> pageTitle(Integer pageNumber, Integer pageSize, String searchContent) {
// 分頁參數
PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);
QueryStringQueryBuilder queryBuilder = new QueryStringQueryBuilder(searchContent);
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageRequest).withQuery(queryBuilder).build();
System.out.println(“查詢的語句:” + searchQuery.getQuery().toString());
Page search = titleRepository.search(searchQuery);
return search.getContent();
}
controller 調用實現(xiàn)
@RestController
public class TitleController {
@Autowired
private TitleService titleService;
/**
* 給索引添加數據
* @return
*/
@PostMapping("save")
public String save(Long id,String titlename,String description) throws NoSuchAlgorithmException {
titleService.save(id,titlename,description);
return "success";
}
/**
* 刪除
* @return
*/
@PostMapping("delete")
public String delete(Long id){
titleService.delete(id);
return "success" ;
}
@PostMapping("deleteDoc")
public String deleteDoc(){
titleService.deleteDoc();
return "success" ;
}
@GetMapping("get")
public List<TitleDocument> getTitle(String param) {
List<TitleDocument> titleDocumentList = titleService.getTitle(param);
return titleDocumentList;
}
@GetMapping("page")
public List<TitleDocument> pageTitle(Integer pageNumber, Integer pageSize, String searchContent) {
List<TitleDocument> documentList = titleService.pageTitle(pageNumber, pageSize, searchContent);
return documentList;
}
測試示例
@Test
public void save() throws Exception {
for (int i = 0; i < 20; i++) {
titleService.save(Long.parseLong(String.valueOf(i)),“測試名稱”, “測試內容”);
System.out.println(“插入第”+i+“條”);
}
}
分頁查詢測試結果:
http://localhost:9898/page?pageNumber=0&pageSize=10&searchContent=測試
可能會遇到的幾個問題
問題:安裝好后Elasticsearch無法運行
解決:可能就是你版本安裝錯誤了,注意版本一定要對應
問題:按照首頁的方式調用Ik進行解析,但是無法設置mapping,出現(xiàn)如下錯誤
analyzer [ik_max_word] not found for field
解決:如果你的多個節(jié)點的集群,那么在集群的每個實例上都要安裝Ik
項目應用開發(fā)指南
在項目的實際開發(fā)中我們一般遵循如下幾個應用指南。
第一步,設計索引結構
首先我們需要考慮搜索的內容主體包含的信息,如ID,標題,描述,作者等,還需要考慮排序信息(如更新時間,創(chuàng)建時間)
第二步,為索引建立對應的模板(DTO)
如同上述demo 中設計的TitleDTO,字段與索引的字段一一對應,另外,因為key的名字都是定死的,為了統(tǒng)一管理,可以給每個key定義成final,方便統(tǒng)一管理和getset等。
第三步, 創(chuàng)建SearchService
該SearchService 主要有兩個方法,
1、構建索引(創(chuàng)建,更新);
2、 刪除索引
首先要一個ID作為唯一要構建的索引,通過ID從數據庫中查詢信息出來,包括需要關聯(lián)的表等信息,得到模板數據(DTO),然后去elasticsearch 查詢有沒有該索引,如果沒有則創(chuàng)建,如果有一條則更新,如果有多條則全部刪除后重新創(chuàng)建。
第四步,當業(yè)務數據發(fā)生變化時更新索引信息,
當業(yè)務數據發(fā)生變化時候更新對應索引信息,分為兩種形式調用
同步調用:直接在業(yè)務方法定點調用searchService對應的方法,
異步調用:基于消息中間件,如kafka,rabbitmq等。
編寫搜索業(yè)務
當用戶沒有輸入關鍵字的時候,默認直接從數據庫查詢信息,當用戶輸入關鍵詞的時候先從es條件查詢出數據的IDs,然后拿ids去數據庫中取。
總結
本篇博客,我們首先通過一個demo 來熟悉了ES如何與SpringBoot 進行整合,當然這只是一個小小的demo,具體在項目中還涉及到ES 數據更mysql 數據的同步問題,接著我們還簡單介紹了下ES在項目應用開發(fā)中的實踐指南。
作者:碼農飛哥
微信公眾號:碼農飛哥
