ElasticSearch 學(xué)習(xí)筆記(四)-----ES在SpringBoot中的集成以及項(xiàng)目應(yīng)用開發(fā)指南

概述

接上一篇ElasticSearch 學(xué)習(xí)筆記(三)-----ES的設(shè)計(jì)原理以及分詞器說(shuō)明。今天我們主要介紹ES 與SpringBoot 的集成以及項(xiàng)目應(yīng)用開發(fā)指南。
ES與SpringBoot的集成
添加依賴

此處,添加的依賴一定要與你安裝的ES的版本對(duì)應(yīng),因?yàn)槲野惭b的ES版本是 6.4.3。查詢
ElasticSearch版本對(duì)應(yīng)關(guān)系 發(fā)現(xiàn)對(duì)應(yīng)的spring-data-elasticsearch 為 3.1.x














在SpringBoot 中對(duì)應(yīng)依賴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 分別對(duì)應(yīng) 索引名稱和索引類型,如果類比于關(guān)系型數(shù)據(jù)庫(kù)的話就相當(dāng)于 庫(kù)和表。在Spring Data 中每個(gè)文檔必須要有id,不然數(shù)據(jù)會(huì)插入失敗。同時(shí)還需要有缺省構(gòu)造器,不然分頁(yè)查詢會(huì)報(bào)錯(cuò)。

@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")

@Field 注解作用到屬性之上,上面代碼表示的意思是,索引時(shí)分詞使用 ik_max_word, 搜索時(shí)分詞使用ik_smart
PS :
1、ik_max_word

會(huì)將文本做最細(xì)粒度的拆分,比如會(huì)將“中華人民共和國(guó)人民大會(huì)堂”拆分為“中華人民共和國(guó)、中華人民、中華、華人、人民共和國(guó)、人民、共和國(guó)、大會(huì)堂、大會(huì)、會(huì)堂等詞語(yǔ)。
2、ik_smart

會(huì)做最粗粒度的拆分,比如會(huì)將“中華人民共和國(guó)人民大會(huì)堂”拆分為中華人民共和國(guó)、人民大會(huì)堂。
Dao 層實(shí)現(xiàn)

@Component
public interface TitleRepository extends ElasticsearchRepository<TitleDTO,Long> {
}

此處我們直接繼承了ElasticsearchRepository接口,該接口實(shí)現(xiàn)了 save ,deleteById,search 等方法,其中save 表示新增或者更新索引,deleteById表示刪除索引,search表示查詢索引數(shù)據(jù)。
Service 調(diào)用實(shí)現(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);
}


/**
 * 刪除索引數(shù)據(jù)
 * @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("查詢的語(yǔ)句:"+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;
}

/**
 * 分頁(yè)查詢
 * @param pageNumber
 * @param pageSize
 * @param searchContent
 * @return
 */
public List<TitleDocument> pageTitle(Integer pageNumber, Integer pageSize, String searchContent) {

// 分頁(yè)參數(shù)
PageRequest pageRequest = PageRequest.of(pageNumber, pageSize);
QueryStringQueryBuilder queryBuilder = new QueryStringQueryBuilder(searchContent);
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageRequest).withQuery(queryBuilder).build();
System.out.println(“查詢的語(yǔ)句:” + searchQuery.getQuery().toString());
Page search = titleRepository.search(searchQuery);
return search.getContent();
}

controller 調(diào)用實(shí)現(xiàn)

@RestController
public class TitleController {
@Autowired
private TitleService titleService;

/**
 * 給索引添加數(shù)據(jù)
 * @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;
}

測(cè)試示例

@Test
public void save() throws Exception {
for (int i = 0; i < 20; i++) {
titleService.save(Long.parseLong(String.valueOf(i)),“測(cè)試名稱”, “測(cè)試內(nèi)容”);
System.out.println(“插入第”+i+“條”);
}

}

在這里插入圖片描述

分頁(yè)查詢測(cè)試結(jié)果:
http://localhost:9898/page?pageNumber=0&pageSize=10&searchContent=測(cè)試

可能會(huì)遇到的幾個(gè)問(wèn)題

問(wèn)題:安裝好后Elasticsearch無(wú)法運(yùn)行
解決:可能就是你版本安裝錯(cuò)誤了,注意版本一定要對(duì)應(yīng)
問(wèn)題:按照首頁(yè)的方式調(diào)用Ik進(jìn)行解析,但是無(wú)法設(shè)置mapping,出現(xiàn)如下錯(cuò)誤

analyzer [ik_max_word] not found for field 

解決:如果你的多個(gè)節(jié)點(diǎn)的集群,那么在集群的每個(gè)實(shí)例上都要安裝Ik
項(xiàng)目應(yīng)用開發(fā)指南

在項(xiàng)目的實(shí)際開發(fā)中我們一般遵循如下幾個(gè)應(yīng)用指南。

第一步,設(shè)計(jì)索引結(jié)構(gòu)
首先我們需要考慮搜索的內(nèi)容主體包含的信息,如ID,標(biāo)題,描述,作者等,還需要考慮排序信息(如更新時(shí)間,創(chuàng)建時(shí)間)
第二步,為索引建立對(duì)應(yīng)的模板(DTO)
如同上述demo 中設(shè)計(jì)的TitleDTO,字段與索引的字段一一對(duì)應(yīng),另外,因?yàn)閗ey的名字都是定死的,為了統(tǒng)一管理,可以給每個(gè)key定義成final,方便統(tǒng)一管理和getset等。
第三步, 創(chuàng)建SearchService
該SearchService 主要有兩個(gè)方法,
1、構(gòu)建索引(創(chuàng)建,更新);
2、 刪除索引
首先要一個(gè)ID作為唯一要構(gòu)建的索引,通過(guò)ID從數(shù)據(jù)庫(kù)中查詢信息出來(lái),包括需要關(guān)聯(lián)的表等信息,得到模板數(shù)據(jù)(DTO),然后去elasticsearch 查詢有沒(méi)有該索引,如果沒(méi)有則創(chuàng)建,如果有一條則更新,如果有多條則全部刪除后重新創(chuàng)建。
第四步,當(dāng)業(yè)務(wù)數(shù)據(jù)發(fā)生變化時(shí)更新索引信息,
當(dāng)業(yè)務(wù)數(shù)據(jù)發(fā)生變化時(shí)候更新對(duì)應(yīng)索引信息,分為兩種形式調(diào)用

同步調(diào)用:直接在業(yè)務(wù)方法定點(diǎn)調(diào)用searchService對(duì)應(yīng)的方法,
異步調(diào)用:基于消息中間件,如kafka,rabbitmq等。

編寫搜索業(yè)務(wù)
當(dāng)用戶沒(méi)有輸入關(guān)鍵字的時(shí)候,默認(rèn)直接從數(shù)據(jù)庫(kù)查詢信息,當(dāng)用戶輸入關(guān)鍵詞的時(shí)候先從es條件查詢出數(shù)據(jù)的IDs,然后拿ids去數(shù)據(jù)庫(kù)中取。

總結(jié)

本篇博客,我們首先通過(guò)一個(gè)demo 來(lái)熟悉了ES如何與SpringBoot 進(jìn)行整合,當(dāng)然這只是一個(gè)小小的demo,具體在項(xiàng)目中還涉及到ES 數(shù)據(jù)更mysql 數(shù)據(jù)的同步問(wèn)題,接著我們還簡(jiǎn)單介紹了下ES在項(xiàng)目應(yīng)用開發(fā)中的實(shí)踐指南。






作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥