ElasticSearch與SpringBoot的集成與JPA方法的使用

完整代碼示例,請參考我的GitHub倉庫:(github.com/KimZing), 包含controller/repository以及測試代碼。java

歡迎star,若有錯誤,歡迎指正_node

1、環境簡介

  • idea 2016.3
  • jdk 1.8
  • ElasticSearch 2.4(之因此不用最新的,是由於SpringBoot和ES的版本是須要相匹配的,而SpringBoot Starter目前不支持最新版)

2、ES簡介

開發過Java搜索的同窗必定都知道lucene搜索引擎,可是lucene只是一個搜索引擎,就比如一個汽車的發動機,重要可是卻沒法直接使用。 後來就有了爲你們所知的solr搜索,提供了對應的web操做界面和java api操做,可是solr的數據併發量和大數據量下的表現相比後來者ES 都仍是有必定差距的,並且ES是天生支持分佈式集羣的。mysql

ES是什麼?咱們能夠把ES比做一個Mysql數據庫,一樣用來存儲數據,不過比Mysql提供了更多的搜索功能,例如分詞搜索,關聯度搜索等,並且搜索速度也不是同一級別的, ES可以實現百萬數據/秒的查詢速度。接下來將ES中用到的概念和Mysql進行類比git

字段 解釋
index 索引,至關於Mysql中的一個庫,例若有一個叫『jd』的庫,那麼裏面能夠創建不少表,存儲不一樣類型的數據,而表在ES中就是type。
type 類型,至關於Mysql中的一張表,存儲json類型的數據
document 文檔,一個文檔至關於Mysql一行的數據
shards 分片,通俗理解,就是數據分紅幾塊區域來存儲,能夠理解爲mysql中的分庫分表(不太恰當)
replicas 備份,就是分片的備份數,至關於mysql的備份庫

ES使用json數據進行數據傳遞,例如{username:king,age:12},那麼這一整條json數據就是一個document,而username,age就是field。github

3、SpringBoot集成ES的依賴

//ES的核心依賴Starter
compile('org.springframework.boot:spring-boot-starter-data-elasticsearch')
//jna依賴,不然項目啓動時,會報classNotFound: native method disable的錯誤
compile("com.sun.jna:jna:3.0.9")
//添加web支持,方便測試
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
複製代碼

4、配置文件

配置文件是能夠配置也能夠不配置的,data-elasticsearch的依賴結構中已經包含了Lucene和ES的jar,SpringBoot會自動在本地給咱們生成一個ES的倉庫,項目下會自動產生一個data文件夾存儲ES的數據。若是咱們不配置ES實例, 那麼SpringBoot就會自動生成這個ES實例,固然性能確定是不行的,因此咱們仍是使用本身搭建的ES實例。web

data-elasticsearch的依賴結構 spring

data-elasticsearch的依賴結構
鏈接獨立的ES實例的配置以下

spring:
  data:
    #ElasticSearch的鏈接地址
    elasticsearch:
      cluster-name: elasticsearch
      cluster-nodes: localhost:9300
複製代碼

關於ES的安裝能夠參考個人另外一篇博文CentOS6.5安裝ES教程sql

5、編寫存儲實體類

編寫實體類主要會用到以下三個註解數據庫

1.類上註解:@Document (至關於Hibernate實體的@Entity/@Table) (必寫)

類型 屬性名 默認值 說明
String indexName 索引庫的名稱,建議以項目的名稱命名
String type "" 類型,建議以實體的名稱命名
short shards 5 默認分區數
short replica 1 每一個分區默認的備份數
String refreshInterval "1s" 刷新間隔
String indexStoreType "fs" 索引文件存儲類型

2.主鍵註解:@Id (至關於Hibernate實體的主鍵@Id註解) (必寫)

只是一個標識,並無屬性。json

3.屬性註解 @Field (至關於Hibernate實體的@Column註解)

@Field默認是能夠不加的,默認全部屬性都會添加到ES中。

類型 屬性名 默認值 說明
FileType type FieldType.Auto 自動檢測屬性的類型
FileType index FieldIndex.analyzed 默認狀況下分詞
boolean store false 默認狀況下不存儲原文
String searchAnalyzer "" 指定字段搜索時使用的分詞器
String indexAnalyzer "" 指定字段創建索引時指定的分詞器
String[] ignoreFields {} 若是某個字段須要被忽略

4.實體類示例

@Data  //lombok註解,會自動生成setter/getter,須要引入lombok的包才能使用。
@Document(indexName = "shop", type = "user", refreshInterval = "0s")
public class User {

    @Id
    private Long id;

    private String username;

    private String realname;

    private String password;

    private Integer age;

    //這三個註解是爲了前臺序列化java8 LocalDateTime使用的,須要引入jsr310的包纔可使用
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    private LocalDateTime birth;

}
複製代碼

6、編寫倉庫

1.代碼編寫

寫一個類繼承ElasticsearchRepository<T, ID>,須要寫兩個泛型,第一個表明要存儲的實體類型,第二個表明主鍵類型,例如寫一個User類的倉儲以下:

/** * @author kingboy--KingBoyWorld@163.com * @date 2017/11/27 下午10:10 * @desc 用戶倉庫. */
public interface UserRepository extends ElasticsearchRepository<User, Long>{

}
複製代碼

咱們來看一下ElasticsearchRepository的繼承結構(以下),其實就能夠發現仍然是JPA的一套Reposiroty,那咱們其實就能夠用JPA的一套接口操做進行數據的增刪改查, spring會自動根據方法名爲咱們生成對應的代理類去實現這些方法。

2.CRUD基礎操做

先來看看ElasticsearchRepository已經實現的一些基礎方法,這些方法的名稱已經具備很好的說明解釋了,那麼你們本身看看,很容易就能理解

3.稍微複雜操做

jpa自帶的這些方法確定是不能知足咱們的業務需求的,那麼咱們如何自定義方法呢?咱們只要使用特定的單詞對方法名進行定義,那麼Spring就會對咱們寫的方法名進行解析, 生成對應的實例進行數據處理,有木有很簡單?那麼接下來就使用Spring官方文檔中的實例進行演示。

先來看下關鍵字的說明


關鍵字 使用示例 等同於的ES查詢
And findByNameAndPrice {「bool」 : {「must」 : [ {「field」 : {「name」 : 「?」}}, {「field」 : {「price」 : 「?」}} ]}}
Or findByNameOrPrice {「bool」 : {「should」 : [ {「field」 : {「name」 : 「?」}}, {「field」 : {「price」 : 「?」}} ]}}
Is findByName {「bool」 : {「must」 : {「field」 : {「name」 : 「?」}}}}
Not findByNameNot {「bool」 : {「must_not」 : {「field」 : {「name」 : 「?」}}}}
Between findByPriceBetween {「bool」 : {「must」 : {「range」 : {「price」 : {「from」 : ?,「to」 : ?,「include_lower」 : true,「include_upper」 : true}}}}}
LessThanEqual findByPriceLessThan {「bool」 : {「must」 : {「range」 : {「price」 : {「from」 : null,「to」 : ?,「include_lower」 : true,「include_upper」 : true}}}}}
GreaterThanEqual findByPriceGreaterThan {「bool」 : {「must」 : {「range」 : {「price」 : {「from」 : ?,「to」 : null,「include_lower」 : true,「include_upper」 : true}}}}}
Before findByPriceBefore {「bool」 : {「must」 : {「range」 : {「price」 : {「from」 : null,「to」 : ?,「include_lower」 : true,「include_upper」 : true}}}}}
After findByPriceAfter {「bool」 : {「must」 : {「range」 : {「price」 : {「from」 : ?,「to」 : null,「include_lower」 : true,「include_upper」 : true}}}}}
Like findByNameLike {「bool」 : {「must」 : {「field」 : {「name」 : {「query」 : 「? *」,「analyze_wildcard」 : true}}}}}
StartingWith findByNameStartingWith {「bool」 : {「must」 : {「field」 : {「name」 : {「query」 : 「? *」,「analyze_wildcard」 : true}}}}}
EndingWith findByNameEndingWith {「bool」 : {「must」 : {「field」 : {「name」 : {「query」 : 「*?」,「analyze_wildcard」 : true}}}}}
Contains/Containing findByNameContaining {「bool」 : {「must」 : {「field」 : {「name」 : {「query」 : 「?」,「analyze_wildcard」 : true}}}}}
In findByNameIn(Collectionnames) {「bool」 : {「must」 : {「bool」 : {「should」 : [ {「field」 : {「name」 : 「?」}}, {「field」 : {「name」 : 「?」}} ]}}}}
NotIn findByNameNotIn(Collectionnames) {「bool」 : {「must_not」 : {「bool」 : {「should」 : {「field」 : {「name」 : 「?」}}}}}}
True findByAvailableTrue {「bool」 : {「must」 : {「field」 : {「available」 : true}}}}
False findByAvailableFalse {「bool」 : {「must」 : {「field」 : {「available」 : false}}}}
OrderBy findByAvailableTrueOrderByNameDesc {「sort」 : [{ 「name」 : {「order」 : 「desc」} }],「bool」 : {「must」 : {「field」 : {「available」 : true}}}}

下面寫幾個示例進行演示,只把倉儲層的列出來了,總體運行是測試過的,沒問題,若是須要總體代碼請到本文頂部的github倉庫查看。

/** * @author kingboy--KingBoyWorld@163.com * @date 2017/11/27 下午10:10 * @desc 用戶倉庫. */
public interface UserRepository extends ElasticsearchRepository<User, Long>{

    /** * 查詢用戶名爲username的用戶 * @param username * @return */
    List<User> findByUsername(String username);

    /** * 查詢用戶名爲username而且真實姓名爲realname的用戶 * @param username * @param realname */
    List<User> findByUsernameAndRealname(String username, String realname);

    /** * 查詢用戶名爲username或者姓名爲realname的用戶 */
    List<User> findByUsernameOrRealname(String username, String realname);

    /** * 查詢用戶名不是username的全部用戶 * @param username * @return */
    List<User> findByUsernameNot(String username);


    /** * 查詢年齡段爲ageFrom到ageTo的用戶 * @param ageFrom * @param ageTo * @return */
    List<User> findByAgeBetween(Integer ageFrom, Integer ageTo);

    /** * 查詢生日小於birthTo的用戶 */
    List<User> findByBirthLessThan(LocalDateTime birthTo);


    /** * 查詢生日段大於birthFrom的用戶 * @param birthFrom * @return */
    List<User> findByBirthGreaterThan(LocalDateTime birthFrom);

    /** * 查詢年齡小於或等於ageTo的用戶 */
    List<User> findByAgeBefore(Integer ageTo);

    /** * 查詢年齡大於或等於ageFrom的用戶 * @param ageFrom * @return */
    List<User> findByAgeAfter(Integer ageFrom);

    /** * 用戶名模糊查詢 * @param username * @return */
    List<User> findByUsernameLike(String username);


    /** * 查詢以start開頭的用戶 * @param start * @return */
    List<User> findByUsernameStartingWith(String start);

    /** * 查詢以end結尾的用戶 * @return */
    List<User> findByUsernameEndingWith(String end);

    /** * 查詢用戶名包含word的用戶 * @param word * @return */
    List<User> findByUsernameContaining(String word);

    /** * 查詢名字屬於usernames中的用戶 * @param usernames * @return */
    List<User> findByUsernameIn(Collection<String> usernames);

    /** * 查詢名字不屬於usernames中的用戶 * @param usernames * @return */
    List<User> findByUsernameNotIn(Collection<String> usernames);

    /** *最後來個複雜點的:查詢年齡小於ageTo,姓名以start開頭,id大於idTo的用戶,而且按照年齡倒序 * @return */
    List<User> findByAgeBeforeAndUsernameStartingWithAndIdGreaterThanOrderByAgeDesc(Integer ageTo, String start, Long idTo);

}
複製代碼

4.更復雜一點的操做

咱們可使用@Query註解進行查詢,這樣要求咱們須要本身寫ES的查詢語句,須要會ES查詢才能夠,其實也很簡單,不會寫查就是了。 看看官方給的例子

public interface BookRepository extends ElasticsearchRepository<Book, String> {
        @Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
        Page<Book> findByName(String name,Pageable pageable);
}
複製代碼

5.咱們還可使用相似Hibernate中criteria的方式進行查詢,

這時候咱們須要本身寫查詢條件,在類中注入UserRepository,使用search方法傳入查詢參數,而後獲取查詢結果 示例以下:

/** * @author kingboy--KingBoyWorld@163.com * @date 2017/11/28 下午12:53 * @desc 用戶服務. */
@Service
public class UserService {

    @Resource
    UserRepository userRepository;

    public Page<User> getUsers() {
        //建立builder
        BoolQueryBuilder builder = QueryBuilders.boolQuery();
        //builder下有must、should以及mustNot 至關於sql中的and、or以及not
        //設置模糊搜索,真實姓名中包含金的用戶
        builder.must(QueryBuilders.fuzzyQuery("realname", "金"));
        //設置用戶名爲king
        builder.must(new QueryStringQueryBuilder("king").field("username"));

        //排序
        FieldSortBuilder sort = SortBuilders.fieldSort("age").order(SortOrder.DESC);

        //設置分頁
        //====注意!es的分頁和Hibernate同樣api是從第0頁開始的=========
        PageRequest page = new PageRequest(0, 2);

        //構建查詢
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //將搜索條件設置到構建中
        nativeSearchQueryBuilder.withQuery(builder);
        //將分頁設置到構建中
        nativeSearchQueryBuilder.withPageable(page);
        //將排序設置到構建中
        nativeSearchQueryBuilder.withSort(sort);
        //生產NativeSearchQuery
        NativeSearchQuery query = nativeSearchQueryBuilder.build();

        //執行,返回包裝結果的分頁
        Page<User> resutlList = userRepository.search(query);

        return resutlList;
    }
}
複製代碼

做者:KimZing 來源:CSDN 原文:blog.csdn.net/KingBoyWorl…

相關文章
相關標籤/搜索