Springboot整合elasticsearch

前言

本文整合基于Springboot2.0+,es版本6.5.4,使用spring-boot-starter-data-elasticsearch包。由于該包實(shí)際引用的是spring-data-elasticsearch,所以需要注意spring-data-elasticsearch和es版本的對應(yīng)關(guān)系,具體可在這里查看。

  • 注:雖然官網(wǎng)標(biāo)明es的6.5.0+版本需要對應(yīng)spring-data-elasticsearch的3.2.X,但由于項(xiàng)目中Springboot版本限制在2.0.3,因此spring-data-elasticsearch的版本也被限制在了3.0.8,經(jīng)過測試基本的插入查詢等功能均可以正常使用,但是否會(huì)有一些高版本的功能受到影響暫不可知。

pom依賴

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/>
    </parent>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>

配置參數(shù)

spring:
  data:
    elasticsearch:
      cluster-name: esCluster
      cluster-nodes: 127.0.0.1:9300 #配置es節(jié)點(diǎn)信息,逗號(hào)分隔,如果沒有指定,則啟動(dòng)ClientNode(9200端口是http查詢使用的。9300集群使用。這里使用9300.)

代碼實(shí)現(xiàn)

創(chuàng)建baen對象
@Data
@Document(indexName = "testgoods", type = "goods")
public class TestGoodsBo {

    @Id
    private long id;
    
    //@Field(type = FieldType.Text)
    private String name;

    private BigDecimal price;

    private long stock;

    @Version
    private Long version;
}

首先創(chuàng)建bean對象,其中幾個(gè)常用注解含義如下

@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Document {
    String indexName();//索引庫的名稱,個(gè)人建議以項(xiàng)目的名稱命名
    String type() default "";//類型,個(gè)人建議以實(shí)體的名稱命名
    short shards() default 5;//默認(rèn)分區(qū)數(shù)
    short replicas() default 1;//每個(gè)分區(qū)默認(rèn)的備份數(shù)
    String refreshInterval() default "1s";//刷新間隔
    String indexStoreType() default "fs";//索引文件存儲(chǔ)類型
}

@Document作用于類上,經(jīng)測試代碼初始化時(shí)若es中沒有對應(yīng)的索引,則會(huì)在es中創(chuàng)建一個(gè)。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field {

    FieldType type() default FieldType.Auto;#自動(dòng)檢測屬性的類型
    FieldIndex index() default FieldIndex.analyzed;#默認(rèn)情況下分詞
    DateFormat format() default DateFormat.none;
    String pattern() default "";
    boolean store() default false;#默認(rèn)情況下不存儲(chǔ)原文
    String searchAnalyzer() default "";#指定字段搜索時(shí)使用的分詞器
    String indexAnalyzer() default "";#指定字段建立索引時(shí)指定的分詞器
    String[] ignoreFields() default {};#如果某個(gè)字段需要被忽略
    boolean includeInParent() default false;
}

@Field作用于屬性上,經(jīng)測試該注解的屬性有時(shí)會(huì)與現(xiàn)有的屬性沖突,造成異常,錯(cuò)誤信息如下,所以建議es中映射已建立的情況下,不要使用該注解。

Caused by: java.lang.IllegalArgumentException: Mapper for [name] conflicts with existing mapping in other types:
[mapper [name] has different [analyzer]]

@Id@Version分別用來綁定es中的_id_version字段。

創(chuàng)建Repository對象
public interface GoodsRepository extends ElasticsearchRepository<TestGoodsBo,Long> , PagingAndSortingRepository<TestGoodsBo,Long> {

        List<TestGoodsBo> findByNameAndPrice(String name, Long price);

        List<TestGoodsBo> findByNameOrPrice(String name, Long price);
        
        Page<TestGoodsBo> findByName(String name,Pageable page);

        Page<TestGoodsBo> findByNameNot(String name,Pageable page);

        Page<TestGoodsBo> findByPriceBetween(long price,Pageable page);

        Page<TestGoodsBo> findByNameLike(String name,Pageable page);

        @Query("{\"bool\" : {\"must\" : {\"term\" : {\"message\" : \"?0\"}}}}")
        Page<TestGoodsBo> findByMessage(String message, Pageable pageable);
}

es的操作主要通過自定義的Repository對象完成,該對象可以通過繼承模板接口ElasticsearchRepository<T, ID extends Serializable>實(shí)現(xiàn),該模板提供了savefindById、findAllsearch等通用方法的實(shí)現(xiàn),同時(shí)還支持通過規(guī)定的名稱格式自定義操作方法,自定義的規(guī)則見上述方法名。(通過規(guī)定格式的名稱注入方法實(shí)現(xiàn)的方式非常有趣,暫時(shí)還不了解原理,后面可以研究一下源碼)

調(diào)用過程
@Slf4j
@RestController
@RequestMapping(value = "/search")
public class SearchController {

    @Autowired
    private GoodsRepository repository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;


    @RequestMapping(value = "/insert")
    public TestGoodsBo insert(@RequestBody TestGoodsBo bo) {
        repository.save(bo);
        return bo;
    }

    @RequestMapping(value = "/get")
    public TestGoodsBo get() {
        Optional<TestGoodsBo> result = repository.findById(1L);
        return result.get();
    }

    @RequestMapping(value = "/find")
    public List<TestGoodsBo> find(String name,Pageable page){
        return repository.findByName(name, page);
    }

    @GetMapping(value = "/search")
    public Page<TestGoodsBo> search(String name, @PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC)Pageable pageable) {
//        //通過ElasticsearchTemplate實(shí)現(xiàn)
//        QueryBuilder queryBuilder = QueryBuilders.matchQuery("name", name);
//        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).withHighlightFields().build();
//        Page<TestGoodsBo> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, TestGoodsBo.class);

//        //Pageable對象的手動(dòng)實(shí)現(xiàn)
//        Sort sort = new Sort(Sort.Direction.ASC,"name");
//        Pageable page = PageRequest.of(0,10,sort);

        Page<TestGoodsBo> sampleEntities = repository.search(QueryBuilders.matchQuery("name", name),pageable);
        return sampleEntities;
    }

}

Controller的實(shí)現(xiàn)比較簡單,以從es搜索文檔為例,可以通過注入之前的Repository對象或者是注入ElasticsearchTemplate對象實(shí)現(xiàn),查詢規(guī)則不復(fù)雜的情況下前者更為簡單一些,具體實(shí)現(xiàn)看上述代碼片段便能理解。

說明:這里要特別說明的一點(diǎn)是經(jīng)過我的測試findByName相較于search方法有一定的局限性,比如我的es設(shè)置了ik和pinyin混合的分詞器時(shí),中文搜索兩者都沒問題,但使用拼音首字母搜索只有后者能搜索到結(jié)果,所以選擇使用哪個(gè)方法需要慎重。

Pageable對象

然后要重點(diǎn)講解一下Pageable類型的對象,該對象可以幫助我們完成分頁和排序操作,有手動(dòng)和自動(dòng)兩種方式實(shí)現(xiàn)。

  • 手動(dòng)方式
        Sort sort = new Sort(Sort.Direction.ASC,"name");
        Pageable page = PageRequest.of(0,10,sort);
  • 自動(dòng)方式
    @GetMapping(value = "/search")
    public Page<TestGoodsBo> search(String name, @PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC)Pageable pageable)

自動(dòng)方式可以在request傳參的同時(shí)就根據(jù)傳入的參數(shù)來組裝Pageable對象,同時(shí)還能使用@PageableDefault注解設(shè)定默認(rèn)值,因此更推薦使用。Spring支持的request參數(shù)如下:

  • page,第幾頁,從0開始,默認(rèn)為第0頁
  • size,每一頁的大小,默認(rèn)為20
  • sort,排序相關(guān)的信息,以property,property(,ASC|DESC)的方式組織,例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基礎(chǔ)上按lastname倒序排列
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容