SpringBoot ElasticSearch Starter API
1. pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.3.7.RELEASE</version>
</dependency>
2. Elasticsearch Repositories 实现
2.1 定义一个ES映射实体
@Document(indexName = "yourIndexName", shards = 20)
public class EsExampleEntity implements Serializable {
private static final long serialVersionUID = 2109037559727003184L;
/**
* 主键
*/
@Id
@Field(type = FieldType.Long)
private Long id;
/**
* 订单号
*/
@Field(type = FieldType.Keyword)
private Long orderNo;
/**
* 名称
*/
@Field(type = FieldType.Text)
private String name;
/**
* 创建时间
*/
@Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Date gmtCreated;
/**
* 销售店铺名称
*/
@MultiField(mainField = @Field(type = FieldType.Text), otherFields = { @InnerField(type = FieldType.Keyword, suffix = "keyword") })
private String shopName;
}
Text类型会被分词,KeyWord不会被分词,可以用来做聚合相关操作,如果一个字段又想被分词又想用来做聚合就需要使用 @MultiField 来进行指定
2.2 查询创建
public interface EsExampleRepository extends ElasticsearchRepository<EsExampleEntity, Long> {
}
注意: @Id指定的字段类型要与本处的泛型类型保持一致
① 按照某字段的升序或降序排序,并分页
写法一
FieldSortBuilder statusSort = SortBuilders.fieldSort("status").order(SortOrder.ASC);
FieldSortBuilder gmtModifiedSort = SortBuilders.fieldSort("gmtModified").order(SortOrder.DESC);
Integer current = request.getCurrent();
Integer size = request.getSize();
PageRequest pageRequest = PageRequest.of(current - 1, size);
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(mainQuery).withSort(statusSort).withSort(gmtModifiedSort).withSort(SortBuilders.scoreSort()).build();
searchQuery.setPageable(pageRequest);
//设置可以查询超过10000条数据
searchQuery.setTrackTotalHits(true);
Page<EsCooperationOrderEntity> orderEntities = esCooperationOrderRepository.search(searchQuery);
Long totalElements = orderEntities.getTotalElements();
List<EsCooperationOrderEntity> content = orderEntities.getContent();
写法二
Sort.Order gmtModified = new Sort.Order(Sort.Direction.DESC, "gmtModified");
Sort.Order topWeight = new Sort.Order(Sort.Direction.valueOf(param.getSortOrder().name()), param.getSortField().name());
Sort orders = Sort.by(topWeight, gmtModified);
PageRequest pageRequest = PageRequest.of(current - 1, size, orders);
// 2、es多条件查询
Page<TalentGoodsEsEntity> entities = talentGoodsEsRepository.search(getBoolQueryBuilder(param), pageRequest);
② 条件查询
public static void main(String[] args) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//模糊查询
WildcardQueryBuilder spuName = QueryBuilders.wildcardQuery("spuName", "*" + "your searchWord" + "*");
boolQueryBuilder.must(spuName);
//精确查询
boolQueryBuilder.must(QueryBuilders.termQuery("cateNamePath", "your searchWord"));
//范围查询 filter 必须匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档
boolQueryBuilder.filter(QueryBuilders.rangeQuery("salePrice").lte("your word"));
//满足任意一个条件 should
//当使用should查询时,如果包含了must或者filter查询,那么should的查询语句就不是或者的意思了,而是有或者没有都行的含义。
List<String> brandNames = new ArrayList<>();
for (String brandName : brandNames) {
boolQueryBuilder.should(QueryBuilders.termQuery("brandName", brandName));
}
// 参考 https://blog.csdn.net/java_chegnxuyuan/article/details/103025351
boolQueryBuilder.minimumShouldMatch(1);
}
③ 聚合查询
@Autowired
RestHighLevelClient client;
TermsAggregationBuilder liveStartTimeBuilder = AggregationBuilders.terms("liveStartTime").field("liveStartTime");
TermsAggregationBuilder talentNameBuilder = AggregationBuilders.terms("talentName").field("talentName");
TermsAggregationBuilder talentIdBuilder = AggregationBuilders.terms("talentId").field("talentId");
TermsAggregationBuilder liveIdBuilder = AggregationBuilders.terms("liveId").field("liveId");
TermsAggregationBuilder liveNameBuilder = AggregationBuilders.terms("liveName").field("liveName");
ValueCountAggregationBuilder spuCount = AggregationBuilders.count("spuCount").field("spuNo");
ValueCountAggregationBuilder doudianGoodsIdCount = AggregationBuilders.count("doudianGoodsIdCount").field("doudianGoodsId");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
liveStartTimeBuilder.subAggregation(talentIdBuilder);
talentIdBuilder.subAggregation(talentNameBuilder);
talentNameBuilder.subAggregation(liveIdBuilder);
liveIdBuilder.subAggregation(liveNameBuilder);
liveNameBuilder.subAggregation(spuCount).subAggregation(doudianGoodsIdCount);
searchSourceBuilder.query(mainQuery).aggregation(liveStartTimeBuilder).size(0);
SearchRequest searchRequest = new SearchRequest("es_coop_order_entity_index_104");
searchRequest.source(searchSourceBuilder);
client.search(searchRequest, RequestOptions.DEFAULT);
List<LiveCalendarVo> liveCalendarVoList = Lists.newArrayList();
Aggregations aggregations = searchResponse.getAggregations();
Terms liveStartTimeTerm = aggregations.get("liveStartTime");
for (Terms.Bucket liveStartTimeBucket : liveStartTimeTerm.getBuckets()) {
String liveStartTime = liveStartTimeBucket.getKeyAsString();
Terms talentIdTerm = liveStartTimeBucket.getAggregations().get("talentId");
for (Terms.Bucket talentIdBucket : talentIdTerm.getBuckets()) {
String talentId = talentIdBucket.getKeyAsString();
Terms talentNameTerm = talentIdBucket.getAggregations().get("talentName");
for (Terms.Bucket talentNameBucket : talentNameTerm.getBuckets()) {
String talentName = talentNameBucket.getKeyAsString();
Terms liveIdTerm = talentNameBucket.getAggregations().get("liveId");
for (Terms.Bucket liveIdBucket : liveIdTerm.getBuckets()) {
String liveId = liveIdBucket.getKeyAsString();
Terms liveNameTerm = liveIdBucket.getAggregations().get("liveName");
for (Terms.Bucket liveNameBucket : liveNameTerm.getBuckets()) {
String liveName = liveNameBucket.getKeyAsString();
ParsedValueCount spuCount = liveNameBucket.getAggregations().get("spuCount");
ParsedValueCount doudianGoodsIdCount = liveNameBucket.getAggregations().get("doudianGoodsIdCount");
LiveCalendarVo liveCalendarVo = new LiveCalendarVo();
liveCalendarVo.setLiveDate(liveStartTime);
liveCalendarVo.setTalentId(talentId);
liveCalendarVo.setTalentName(talentName);
liveCalendarVo.setLiveId(liveId);
liveCalendarVo.setLiveName(liveName);
liveCalendarVo.setSpuCount(spuCount.getValue());
liveCalendarVo.setDoudianGoodsIdCount(doudianGoodsIdCount.getValue());
liveCalendarVoList.add(liveCalendarVo);
}
}
}
}
}
//方式二
//聚合条件
TermsAggregationBuilder builder1 = AggregationBuilders.terms("taxonomy").field("taxonomy.keyword");
TermsAggregationBuilder builder2 = AggregationBuilders.terms("year").field("year.keyword");
TermsAggregationBuilder builder = builder1.subAggregation(builder2);
//构建查询
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(bqb)
.withSort(fsb)
.addAggregation(builder)
.withPageable(pageable)
.build();
ES工具类
package com.befriend.first.base.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import com.alibaba.fastjson.JSONObject;
import com.befriend.common.model.PageVO;
import com.befriend.first.base.constant.GlobalConstant;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.query.UpdateResponse.Result;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
/**
* @author by jlm
* @date 2021/9/2 10:52
*/
@Service
public class EsBaseUtils<T> {
public static final String CONTENT = "content";
public static final String NEXT_SCROLL_ID = "nextScrollId";
public static final String TOTAL_COUNT = "totalCount";
public static final String PAGE_VO = "pageVo";
@Resource
ElasticsearchRestTemplate template;
/**
* 该方法用于滚动游标分页 避免深度分页问题 不支持跳页查询
* @param currentPage 当前页码 从1开始
* @param size 每页显示数量
* @param query 查询条件 默认查全部
* @param scrollId 游标 首次查询为null
* @param indexName 索引名称
* @param clazz 查询的Entity class
* @return
*/
public Map<String, Object> scrollPage(Integer currentPage, Integer size, QueryBuilder query, String scrollId,
String indexName, Class<T> clazz) {
IndexCoordinates index = IndexCoordinates.of(indexName);
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(query == null ? matchAllQuery() : query)
.withPageable(PageRequest.of(currentPage - GlobalConstant.ONE, size))
.build();
Map<String, Object> resultMap = new HashMap<>(GlobalConstant.THREE);
List<T> sampleEntities = new ArrayList<>();
long count = template.count(searchQuery, clazz, index);
if (StringUtils.isEmpty(scrollId)) {
SearchScrollHits<T> scroll = template.searchScrollStart(30000, searchQuery, clazz, index);
if (scroll.hasSearchHits()) {
sampleEntities.addAll(scroll.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList()));
String nextScrollId = scroll.getScrollId();
resultMap.put(NEXT_SCROLL_ID, nextScrollId);
}
}else {
SearchScrollHits<T> scrollHits = template.searchScrollContinue(scrollId, 30000, clazz, index);
if (scrollHits.hasSearchHits()) {
sampleEntities.addAll(scrollHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList()));
String nextScrollId = scrollHits.getScrollId();
resultMap.put(NEXT_SCROLL_ID, nextScrollId);
}
}
resultMap.put(CONTENT, sampleEntities);
resultMap.put(TOTAL_COUNT, count);
return resultMap;
}
/**
* 获取分页VO 及 nextScrollId
* @param currentPage
* @param size
* @param query
* @param scrollId
* @param indexName
* @param clazz
* @return
*/
public Map<String, Object> buildPageVo(Integer currentPage, Integer size, QueryBuilder query, String scrollId, String indexName, Class<T> clazz) {
Map<String ,Object> finalResMap = new HashMap<>(GlobalConstant.TWO);
Map<String, Object> resPageMap = scrollPage(currentPage, size, query, scrollId, indexName, clazz);
Long count = (Long)resPageMap.get(TOTAL_COUNT);
PageVO<T> pageVO = PageResult.buildResult((List<T>) resPageMap.get(CONTENT), currentPage, size, count.intValue());
String nextScrollId = (String)resPageMap.get(NEXT_SCROLL_ID);
finalResMap.put(PAGE_VO, pageVO);
finalResMap.put(NEXT_SCROLL_ID, nextScrollId);
if (!pageVO.getHasNext()) {
searchScrollClear(nextScrollId);
}
return finalResMap;
}
/**
* 清除ScrollId
* @param scrollId
*/
public void searchScrollClear(String scrollId) {
template.searchScrollClear(Arrays.asList(scrollId));
}
/**
* 更新es部分字段数据
* @param esEntity
* @param id
*/
public boolean updateEsEntityById(T esEntity, String id) {
Class<?> aClass = esEntity.getClass();
Document annotation = aClass.getAnnotation(Document.class);
String indexName = annotation.indexName();
Map<String, Object> map = JSONObject.parseObject(JSONObject.toJSONString(esEntity), Map.class);
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document.create();
for (Entry<String, Object> stringObjectEntry : map.entrySet()) {
document.put(stringObjectEntry.getKey(), stringObjectEntry.getValue());
}
UpdateResponse response = getUpdateResponse(id, indexName, document);
return Result.UPDATED.equals(response.getResult());
}
/**
* 设置某字段为null
* @param indexName
* @param fieldName
* @param id
* @return
*/
public boolean setFieldNullById(String indexName, String fieldName, String id) {
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document.create();
document.put(fieldName, null);
UpdateResponse response = getUpdateResponse(id, indexName, document);
return Result.UPDATED.equals(response.getResult());
}
private UpdateResponse getUpdateResponse(String id, String indexName,
org.springframework.data.elasticsearch.core.document.Document document) {
UpdateQuery build = UpdateQuery.builder(id).withDocument(document).build();
return template.update(build, IndexCoordinates.of(indexName));
}
}