SpringBoot 集成ElasticSearch 之分页查询
SpringBoot 集成ElasticSearch 的代码,我在上一篇博客中已经写过,这边就不重复了。主要记录下service层实现类中分页查询的方法
实体类、配置信息以及新增、更新的代码在上一篇博客中,地址:
我在网上找了几种方法,我这里暂时只记录其中两种查询方式,第一种是几乎原生的API,第二种是经过spring二次封装的;两种方式都能查询成功
注 :两种方式的 page 都是从 0 开始,我这里是做了减一处理的
第一种
导包
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
代码
@Qualifier("client")
@Autowired
private RestHighLevelClient restHighLevelClient;
@Override
public Map<String,Object> query(Map<String,Object> datasMap) throws IOException {
// 参数定义
String datas = String.valueOf(datasMap.get("datas"));
String deviceMac = String.valueOf(datasMap.get("deviceMac"));
String deviceSn = String.valueOf(datasMap.get("deviceSn"));
String deviceTypes = String.valueOf(datasMap.get("deviceTypes"));
String createDateStart = String.valueOf(datasMap.get("createDateStart"));
String createDateEnd = String.valueOf(datasMap.get("createDateEnd"));
// 分页参数
String order = String.valueOf(datasMap.get("order"));
String orderColumn = String.valueOf(datasMap.get("orderColumn"));
Integer page = datasMap.get("pageNum") != null ? Integer.valueOf(String.valueOf(datasMap.get("pageNum"))) : 1;
Integer size = datasMap.get("pageSize") != null ? Integer.valueOf(String.valueOf(datasMap.get("pageSize"))) : 10;
List<String> deviceTypeList = null;
if(StringUtils.isNotBlank(deviceTypes) && !(deviceTypes.equalsIgnoreCase("null"))) {
deviceTypeList = Arrays.asList(deviceTypes.split(","));
}
// 封装查询条件
BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
// 多字段查询
BoolQueryBuilder multiBuilder = new BoolQueryBuilder();
// 多值查询
BoolQueryBuilder termsBuilder = new BoolQueryBuilder();
// 模糊查询
BoolQueryBuilder matchBuilder = new BoolQueryBuilder();
// 精确查询
BoolQueryBuilder termBuilder = new BoolQueryBuilder();
if(StringUtils.isNotBlank(datas) && !(datas.equalsIgnoreCase("null"))) {
// 在discusspost索引的datas和deviceMac字段中都查询 datas
multiBuilder.should(QueryBuilders.multiMatchQuery(datas, "datas", "deviceMac"));
queryBuilder.must(multiBuilder);
}
if(deviceTypeList != null && deviceTypeList.size() > 0) {
// 在discusspost索引的deviceType字段中都查询多个值
termsBuilder.should(QueryBuilders.termsQuery("deviceType",deviceTypeList));
queryBuilder.must(termsBuilder);
}
if(StringUtils.isNotBlank(deviceMac) && !(deviceMac.equalsIgnoreCase("null"))) {
// 模糊查询
matchBuilder.should(QueryBuilders.matchQuery("deviceMac",deviceMac));
queryBuilder.must(matchBuilder);
}
if(StringUtils.isNotBlank(deviceSn) && !(deviceSn.equalsIgnoreCase("null"))) {
// 精准查询
termBuilder.should(QueryBuilders.termQuery("deviceSn",deviceSn));
queryBuilder.must(termBuilder);
}
// 时间范围筛选
if (StringUtils.isNotBlank(createDateStart) && StringUtils.isNotBlank(createDateEnd)
&& !(createDateStart.equalsIgnoreCase("null")) && !(createDateEnd.equalsIgnoreCase("null"))){
queryBuilder
.filter(QueryBuilders.rangeQuery("createDate")
.gte(DateUtil.parseStrToDate(createDateStart,DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS))
.lte(DateUtil.parseStrToDate(createDateEnd,DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS)));
}
// 构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
.sort(SortBuilders.fieldSort(orderColumn).order(SortOrder.fromString(order)))
// 一个可选项,用于控制允许搜索的时间:searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 查询条件
.query(queryBuilder)
.from((page - 1) * size) // 指定从哪条开始查询 从0开始计算 第一页是 0
.size(size); // 需要查出的总记录条数
// discusspost是索引名
SearchRequest searchRequest = new SearchRequest("discusspost");
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
List<DatasEntity> list = new LinkedList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
DatasEntity discussPost = JSONObject.parseObject(hit.getSourceAsString(), DatasEntity.class);
list.add(discussPost);
}
// 返回参数
Map<String,Object> map = new HashMap<>();
map.put("data", list);
map.put("page", page);
map.put("size", size);
map.put("total", searchResponse.getHits().getTotalHits().value);
return map;
}
postman测试
第二种
service层
导包
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
代码
@Autowired
private DatasMapper datasMapper;
@Override
public Map<String,Object> query(Map<String,Object> datasMap) throws IOException {
// 参数定义
String datas = String.valueOf(datasMap.get("datas"));
String deviceTypes = String.valueOf(datasMap.get("deviceTypes"));
String createDateStart = String.valueOf(datasMap.get("createDateStart"));
String createDateEnd = String.valueOf(datasMap.get("createDateEnd"));
// 分页参数
String order = String.valueOf(datasMap.get("order"));
String orderColumn = String.valueOf(datasMap.get("orderColumn"));
Integer page = datasMap.get("page") != null ? Integer.valueOf(String.valueOf(datasMap.get("page"))) : 1;
Integer size = datasMap.get("size") != null ? Integer.valueOf(String.valueOf(datasMap.get("size"))) : 10;
List<String> deviceTypeList = null;
if(StringUtils.isNotBlank(deviceTypes) && !(deviceTypes.equalsIgnoreCase("null"))) {
deviceTypeList = Arrays.asList(deviceTypes.split(","));
}
// 查询对象
BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
// 模糊搜索对象
BoolQueryBuilder keyBuilder = new BoolQueryBuilder();
// 分类查询对象
BoolQueryBuilder orBuilder = new BoolQueryBuilder();
// 拼接模糊搜索条件
if (StringUtils.isNotBlank(datas) && !(datas.equalsIgnoreCase("null"))){
// 这边主要用的是should,也就是相当于mysql的or datas like concat('%datas%') or deviceMac like concat('%datas%')
// wildcardQuery可以用于带分词的模糊搜索,如果要分词的话,那么字段的type应该是text,假如在用wildcardQuery而不想分词的话,可以查.keyword
// 例如title.keyword,不过我这边title的type已经定了是keyword类型,所以我就直接做不分词的模糊查询,精确查询的话就用matchQuery
keyBuilder.should(QueryBuilders.wildcardQuery("datas", "*" + datas + "*"));
keyBuilder.should(QueryBuilders.wildcardQuery("deviceMac", "*" + datas + "*"));
queryBuilder.must(keyBuilder);
}
// 拼接分类筛选条件
if (deviceTypeList != null && deviceTypeList.size() > 0){
// 这里主要是实现了多条件筛选的需求,前端有复选框的条件筛选,后端以集合方式接收,然后做or的条件拼接 deviceType = '1' or deviceType = '2'...
deviceTypeList.forEach(s -> orBuilder.should(QueryBuilders.matchQuery("deviceType",s)));
queryBuilder.must(orBuilder);
}
// 时间范围筛选
if (StringUtils.isNotBlank(createDateStart) && StringUtils.isNotBlank(createDateEnd)
&& !(createDateStart.equalsIgnoreCase("null")) && !(createDateEnd.equalsIgnoreCase("null"))){
// 范围筛选就用rangeQuery 相当于 >= 'xx' and <= 'xx' 还有gt方法和lt方法就是不带 '='
QueryBuilder queryRange = QueryBuilders.rangeQuery("createDate")
.gte(DateUtil.parseStrToDate(createDateStart,DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS))
.lte(DateUtil.parseStrToDate(createDateEnd,DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS));
queryBuilder.filter(queryRange);
}
// 分页查询并按发布时间排序
// 这边的分页只能算是form-size的浅层分页,如果数据量大的话,建议改造成scroll深度分页
// 这里 page 要减一
Pageable pageable = PageRequest.of(page - 1, size, Direction.fromString(order),orderColumn);
Page<DatasEntity> search = datasMapper.search(queryBuilder,pageable);
Map<String ,Object> map = new HashMap<>();
map.put("data", search);
return map;
}
Dao层
导包
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
代码
@Component
public interface DatasMapper extends ElasticsearchRepository<DatasEntity,Long> {
}
postman测试
参考博客:
https://blog.csdn.net/wpw2000/article/details/115704320
https://www.jb51.net/article/211278.htm
https://blog.csdn.net/weixin_45566249/article/details/111297868
https://blog.csdn.net/BiandanLoveyou/article/details/115874372
https://www.jianshu.com/p/86afb2d3bd27