完整代碼示例,請參考我的GitHub倉庫:(github.com/KimZing), 包含controller/repository以及測試代碼。java
歡迎star,若有錯誤,歡迎指正_node
開發過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
//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')
複製代碼
配置文件是能夠配置也能夠不配置的,data-elasticsearch的依賴結構中已經包含了Lucene和ES的jar,SpringBoot會自動在本地給咱們生成一個ES的倉庫,項目下會自動產生一個data文件夾存儲ES的數據。若是咱們不配置ES實例, 那麼SpringBoot就會自動生成這個ES實例,固然性能確定是不行的,因此咱們仍是使用本身搭建的ES實例。web
data-elasticsearch的依賴結構 spring
spring:
data:
#ElasticSearch的鏈接地址
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: localhost:9300
複製代碼
關於ES的安裝能夠參考個人另外一篇博文CentOS6.5安裝ES教程sql
編寫實體類主要會用到以下三個註解數據庫
類型 | 屬性名 | 默認值 | 說明 |
---|---|---|---|
String | indexName | 無 | 索引庫的名稱,建議以項目的名稱命名 |
String | type | "" | 類型,建議以實體的名稱命名 |
short | shards | 5 | 默認分區數 |
short | replica | 1 | 每一個分區默認的備份數 |
String | refreshInterval | "1s" | 刷新間隔 |
String | indexStoreType | "fs" | 索引文件存儲類型 |
只是一個標識,並無屬性。json
@Field默認是能夠不加的,默認全部屬性都會添加到ES中。
類型 | 屬性名 | 默認值 | 說明 |
---|---|---|---|
FileType | type | FieldType.Auto | 自動檢測屬性的類型 |
FileType | index | FieldIndex.analyzed | 默認狀況下分詞 |
boolean | store | false | 默認狀況下不存儲原文 |
String | searchAnalyzer | "" | 指定字段搜索時使用的分詞器 |
String | indexAnalyzer | "" | 指定字段創建索引時指定的分詞器 |
String[] | ignoreFields | {} | 若是某個字段須要被忽略 |
@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;
}
複製代碼
寫一個類繼承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會自動根據方法名爲咱們生成對應的代理類去實現這些方法。
先來看看ElasticsearchRepository已經實現的一些基礎方法,這些方法的名稱已經具備很好的說明解釋了,那麼你們本身看看,很容易就能理解
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);
}
複製代碼
咱們可使用@Query註解進行查詢,這樣要求咱們須要本身寫ES的查詢語句,須要會ES查詢才能夠,其實也很簡單,不會寫查就是了。 看看官方給的例子
public interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
Page<Book> findByName(String name,Pageable pageable);
}
複製代碼
這時候咱們須要本身寫查詢條件,在類中注入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…