Spring項目 提供SpringData子模塊,爲各種數據訪問提供統一編程接口,包括關係數據庫(Mysql)、非關係數據庫(Redis)或者類似Elasticsearch這樣的分佈式索引數據庫。從而簡化代碼開發,提高開發效率。
Spring Data Elasticsearch 基於Spring Data API簡化elasticsearch操作,將elasticsearch原始客戶端API進行封裝。Spring Data Elasticsearch爲elasticsearch項目提供集成搜索引擎,提供了Spring Data風格的操縱數據方式,避免大量樣板代碼。
常用註解說明
@Document
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
//標示映射到Elasticsearch文檔上的領域對象
public @interface Document {
//索引庫名===>關係數據庫中數據庫概念
String indexName();
//類型=====》關係數據庫表的概念
String type() default "";
//創建索引是使用服務端設置-默認false
boolean useServerConfiguration() default false;
//分片數-默認5
short shards() default 5;
//副本數量-默認1
short replicas() default 1;
//刷新時間
String refreshInterval() default "1s";
//索引存儲類型
String indexStoreType() default "fs";
//配置是否在存儲庫上創建索引
boolean createIndex() default true;
//版本管理配置
VersionType versionType() default VersionType.EXTERNAL;
}
@Id
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
//Document中的id========>關係數據庫錶行的概念
public @interface Id {
}
@Field
@Field(type = FieldType.Keyword)
@Field(analyzer = "ik_max_word",type = FieldType.Text)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@Inherited
public @interface Field {
//文章中字段類型
FieldType type() default FieldType.Auto;
//是否建立倒排索引
boolean index() default true;
DateFormat format() default DateFormat.none;
String pattern() default "";
//是否存儲數據
boolean store() default false;
boolean fielddata() default false;
String searchAnalyzer() default "";
//分詞器名次
String analyzer() default "";
String normalizer() default "";
String[] ignoreFields() default {};
boolean includeInParent() default false;
String[] copyTo() default {};
}
@Field註解中爲文章自動指定元數據類型
public enum FieldType {
Text,//會進行分詞並建了索引的字符類型
Integer,
Long,
Date,
Float,
Double,
Boolean,
Object,
Auto,//自動判斷對象類型
Nested,//嵌套對象類型
Ip,
Attachment,
Keyword;//不會進行分詞建立索引的類型
private FieldType() {
}
}
Spring Data風格的操作數據
1. 通過繼承ElasticsearchRepository,來完成ES普通的CRUD和分頁查詢和普通JPA沒什麼區別
ElasticsearchRepository裏有幾個特殊的search方法,是ES獨有的,是區別與其他普通JPA的地方,用來構建ES查詢的。
<S extends T> S index(S var1);
Iterable<T> search(QueryBuilder var1);
Page<T> search(QueryBuilder var1, Pageable var2);
Page<T> search(SearchQuery var1);
Page<T> searchSimilar(T var1, String[] var2, Pageable var3);
void refresh();
Class<T> getEntityClass();
普通操作:
List<Order> findByNameAndPrice(String name, Integer price);
List<Order> findByNameOrPrice(String name, Integer price);
Page<Order> findByName(String name,Pageable page);
Page<Order> findByNameNot(String name,Pageable page);
Page<Order> findByPriceBetween(int price,Pageable page);
Page<Order> findByNameLike(String name,Pageable page);
//使用@query註解可以直接使用elasticsearch的DSL語句進行查詢
@Query("{\"bool\" : {\"must\" : {\"term\" : {\"message\" : \"?0\"}}}}")
Page<Order> findByMessage(String message, Pageable pageable);
//批量插入
List<order> orderList = orderDao.getAllOrderList(null);
Iterable<EsProduct> esProductIterable = orderRepository.saveAll(orderList);
從ElasticsearchRepository API中我們主要關注QueryBuilder 和SearchQuery 對象,要完成一些特殊查詢就主要看這兩個參數。
從接口關係圖中可以看到SearchQuery是個接口,其實現類是NativeSearchQuery。實際使用中,我們主要是通過構建NativeSearchQuery來完成一些複雜查詢。
從NativeSearchQuery源碼查看,NativeSearchQuery構造函數主要需要兩個參數:QueryBuilder 和 SortBuilder。
public NativeSearchQuery(QueryBuilder query) {
this.query = query;
}
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter) {
this.query = query;
this.filter = filter;
}
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
}
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts, Field[] highlightFields) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
this.highlightFields = highlightFields;
}
當然,實際使用過程基本不直接New NativeSearchQuery,而是通過NativeSearchQueryBuilder 的build方法來構建查詢。
QueryBuilder 主要用來構建查詢條件和過濾條件和 SortBuilder主要構建排序。我們可以通過NativeSearchQueryBuilder 來組合這些QueryBuilder和SortBuilder,在組合分頁參數,最後得到一個SearchQuery。
NativeSearchQuery實現:
Pageable pageable = PageRequest.of(pageNum, pageSize);
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//分頁
nativeSearchQueryBuilder.withPageable(pageable);
//過濾
if (brandId != null || productCategoryId != null) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (brandId != null) {
boolQueryBuilder.must(QueryBuilders.termQuery("brandId", brandId));
}
if (productCategoryId != null) {
boolQueryBuilder.must(QueryBuilders.termQuery("productCategoryId", productCategoryId));
}
nativeSearchQueryBuilder.withFilter(boolQueryBuilder);
}
//搜索
if (StringUtils.isEmpty(keyword)) {
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
} else {
List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", keyword),
ScoreFunctionBuilders.weightFactorFunction(10)));
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("subTitle", keyword),
ScoreFunctionBuilders.weightFactorFunction(5)));
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keywords", keyword),
ScoreFunctionBuilders.weightFactorFunction(2)));
FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()];
filterFunctionBuilders.toArray(builders);
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders)
.scoreMode(FunctionScoreQuery.ScoreMode.SUM)
.setMinScore(2);
nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);
}
//排序
if(sort==1){
//按新品從新到舊
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
}else if(sort==2){
//按銷量從高到低
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sale").order(SortOrder.DESC));
}else if(sort==3){
//按價格從低到高
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
}else if(sort==4){
//按價格從高到低
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
}else{
//按相關度
nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
}
nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
LOGGER.info("DSL:{}", searchQuery.getQuery().toString());
return productRepository.search(searchQuery);
ElasticsearchTemplate使用
ElasticsearchTemplate是ElasticsearchRepository的補充,提供更多底層操作方法。比如通過ElasticsearchRepository的saveAll方法能夠實現批量插入,但是隻支持小量數據查詢,如果是超大數據插入就需要使用ES原生的API bulk 了,可以實現百萬級數據迅速插入。
在ElasticsearchTemplate中提供了對應方法:
SpringBoot 整合 elasticsearch
1、在pom.xml中添加相關依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2、修改SpringBoot配置文件,添加elasticsearch相關配置
data:
elasticsearch:
repositories:
enabled: true
cluster-nodes: 127.0.0.1:9300
cluster-name: elasticsearch
3、構建Elasticsearch POJO,使用註解@Document創建ES 文章對象。
4、繼承ElasticsearchRepository接口,同時添加自定義衍生查詢,用於操作ES數據
5、創建Service實現ES操作
6、定義API
7、測試API