(Java High Level REST Client 官方已废弃,现在推行的是 Elasticsearch Java API Client )
依赖说明:在docker安装es文章中,我们使用的版本是7.12.1,所以本文依赖也引入相同版本
org.elasticsearch.client
elasticsearch-rest-high-level-client
7.12.1
@Component
public class RestHighLevelClientRequest {
@Bean
public RestHighLevelClient client() {
return new RestHighLevelClient(
RestClient.builder(
HttpHost.create("http://ip:9200"),
//多台服务器可继续添加URL
HttpHost.create("http://ip:9200")
)
);
}
}
将简单的增删改查进行封装
@Component
public class EsUtil {
private RestHighLevelClient client;
//处理返回结果
@Autowired
RestHighLevelClientResponse restHighLevelClientResponse;
/**
* 新增索引库
*
* @param indexName 索引库名 类似于数据库名
* @param mappingTempLate DSL语句
* @throws IOException
*/
public void createIndex(String indexName, String mappingTempLate) throws IOException {
if (isIndexExit(indexName)) {
throw new RuntimeException("索引库已存在");
}
//1、创建request,入参为索引库名称
CreateIndexRequest request = new CreateIndexRequest(indexName);
//2、请求参数:mappingTempLate为DSL语句,内容是json
request.source(mappingTempLate, XContentType.JSON);
// 3、发起请求,、indices()返回索引库操作的所有方法 RequestOptions.DEFAULT:控制请求头信息,一般为默认
client.indices().create(request, RequestOptions.DEFAULT);
}
/**
* 删除索引库
*
* @param indexName 索引库名 类似于数据库名
* @throws IOException
*/
public void delIndex(String indexName) throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
client.indices().delete(request, RequestOptions.DEFAULT);
}
/**
* 判断索引库是否存在
*
* @param indexName 索引库名 类似于数据库名
*/
public boolean isIndexExit(String indexName) throws IOException {
GetIndexRequest request = new GetIndexRequest(indexName);
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
return exists;
}
/**
* 判断文档是否存在
* @param indexName 索引库
* @param id 文档id
* @throws IOException
*/
public boolean documentIsExists(String indexName , String id) throws IOException {
GetRequest getRequest = new GetRequest(indexName, id);
// 不获取返回的 _source 的上下文了
// getRequest.fetchSourceContext(new FetchSourceContext(false));
// getRequest.storedFields("_none_");
return client.exists(getRequest, RequestOptions.DEFAULT);
}
/**
* (DELETE //索引名称/_doc/id)
* 删除文档
* @param indexName 索引库
* @param id 文档id
* @throws IOException
*/
public void documentDeleteRequest(String indexName,String id) throws IOException {
DeleteRequest request = new DeleteRequest(indexName, id);
DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);
System.out.println(deleteResponse.status()); // 对应命令返回的状态OK
}
/**
* 添加单条数据 如果id已经存在则会全量修改
*
* @param indexName 索引库名
* @param id 数据ID
* @param document 数据文本,json
* @throws IOException
*/
public void setIndexDocument(String indexName, String id, String document) throws IOException {
IndexRequest request = new IndexRequest(indexName).id(id);
request.source(document, XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
}
/**
* 批量写入
*
* @param list 数据集合
* @param indexName 索引名
* 考虑到可能会传入多个实体类类型,list指定类型Object,如果传入object就强转一下,也可以指定具体实体类类型就不用指定object
* @throws IOException
*/
public <T> void bulkDocument(List<Object> list, String indexName) throws IOException {
//1、创建bulkRequest对象
BulkRequest request = new BulkRequest();
for (T o : list) {
//转换数据并循环插入es
//假如转User类型
User user = (User) o;
request.add(new IndexRequest(indexName)
.id(user.getId())
.source(JSONObject.toJSONString(user), XContentType.JSON));
}
client.bulk(request, RequestOptions.DEFAULT);
}
/**
* 查询全部(全量查询)
* # query:查询 match_all:查询全部(全量查询)
* DSL语句
* GET /test/_search
* {
* "query": {
* "match_all": {
* }
* }
* }
* @param indexName
* @throws IOException
*/
public List<Object> matchAll(String indexName) throws IOException {
SearchRequest request = new SearchRequest(indexName);
//准备DSL语句
//source()为请求DSL,大DSL
//query()查询
//matchAllQuery全量查询
request.source().query(QueryBuilders.matchAllQuery());
//es将结果封装成了searchResponse对象返回,根据对象进行解析
List<Object> objects = handleResponse(request);
//将objects返回出去,处理成自己想要的格式
return objects;
}
/**
* multiMatchQuery 指定多字段搜索 类似 where (a = 'zs' or b = 'zs')
* multi_match:多字段全文检索查询,与match查询类似
* DSL语句
* GET /test/_search
* {
* "query": {
* "multi_match": {
* "query": "文本",
* "fields": ["字段1","字段2"...]
* }
* }
* }
* @param indexName 索引名
* @param keyword 搜索文本
* @param indexName 搜索字段
*/
public List<Object> multiMatchQuery(String indexName, String keyword, String... fieldNames) throws IOException {
SearchRequest request = new SearchRequest(indexName);
//multiMatchQuery 指定多字段搜索
request.source().query(QueryBuilders.multiMatchQuery(keyword, fieldNames));
return handleResponse(request);
}
/**
* termQuery 词条精确查询 termQuery("name",基本数据类型、object)
* 精确查询 类似 where name = name
* 精确查询(keyword)(term类型)语法 :FIELD(查询的具体字段) value(查询的具体值)
* GET /test/_search
* {
* "query": {
* "term": {
* "FIELD": {
* "value": "VALUE"
* }
* }
* }
* }
* @param indexName 索引库
* @param keyword 查询keyword
* @param condition 查询条件
* @throws IOException
*/
public List<Object> termQuery(String indexName, String keyword, Object condition) throws IOException {
SearchRequest request = new SearchRequest(indexName);
//termQuery 精确查询
request.source().query(QueryBuilders.termQuery(keyword, condition));
return handleResponse(request);
}
/**
* rangeQuery 范围查询
* # range(范围)类型查询语法: FIELD(字段) gt:大于 ,gte:大于等于 ,lt:小于,lte小于等于
* GET /test/_search
* {
* "query": {
* "range": {
* "FIELD": {
* "gte": 10,
* "lte": 20
* }
* }
* }
* }
* 范围查询 时间、价格 类似于 where price <= from and price <= to
*
* @param indexName 索引名
* @param field 字段名
* @param from 在前
* @param to 在后
* @return
* @throws IOException
*/
public List<Object> rangeQuery(String indexName, String field, Object from, Object to) throws IOException {
SearchRequest request = new SearchRequest(indexName);
//准备DSL语句
//source()为请求DSL,大DSL
//query()查询
//rangeQuery范围查询 get大于等于 lte小于等于 gt大于 lt小于
request.source().query(QueryBuilders.rangeQuery(field).gte(from).lte(to));
return handleResponse(request);
}
/**
* 处理es返回结果
*
* @param request
* @return
* @throws IOException
*/
public List<Object> handleResponse(SearchRequest request) throws IOException {
//es将结果封装成了searchResponse对象返回,根据对象进行解析
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析结果,取hits
SearchHits searchHits = response.getHits();
//取查询的总条数
long value = searchHits.getTotalHits().value;
//查询的结果数组
SearchHit[] hits = searchHits.getHits();
List<Object> objects = new ArrayList<>();
for (SearchHit hit : hits) {
//得到source
String sourceAsString = hit.getSourceAsString();
objects.add(sourceAsString);
}
return objects;
}
}
RequestParams.java 为入参的实体类,目的就是为了方便传参
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RequestParams {
//关键字
private String key;
//页码
private Integer page;
//大小
private Integer size;
//排序字段
private String sortBy;
//品牌
private String brand;
//城市
private String city;
//星级
private String starName;
//最小价格
private Integer minPrice;
//最大价格
private Integer maxPrice;
//坐标
private String location;
}
定义一个接口,入参为 RequestParams 实体类
public interface TestService {
/**
* 单条件查询
*
* @param params
* @return
*/
List<Object> search(RequestParams params);
/**
* 多条件、聚合查询
*
* @param params
* @return
*/
Map<String, List<String>> filters(RequestParams params);
}
@Service
public class TestServiceImpl implements TestService {
@Autowired
RestHighLevelClientResponse restHighLevelClientResponse;
@Override
public List<Object> search(RequestParams params) {
try {
//构建请求
//indexName为索引库名
SearchRequest request = new SearchRequest("indexName");
SearchRequest reqData = buildBasicQuery(params, request);
return restHighLevelClientResponse.handleResponse(reqData);
} catch (IOException e) {
throw new RuntimeException("查询失败");
}
}
/**
* 多聚合查询(对文档进行统计,类似于group by分组)+普通条件查询
*
* @param params
* @return
*/
@Override
public Map<String, List<String>> filters(RequestParams params) {
try {
//构建请求
//indexName为索引库名
SearchRequest request = new SearchRequest("indexName");
//普通条件查询
buildBasicQuery(params, request);
request.source().size(0);
//设置多聚合查询条件
buildAggregation(request);
Map<String, List<String>> stringListMap =restHighLevelClientResponse.handleBucketResponse(request);
return stringListMap;
} catch (IOException e) {
}
return null;
}
/**
* 多条件查询 and
*
* @param params
* @return
*/
private SearchRequest buildBasicQuery(RequestParams params, SearchRequest request) {
/**
* 查询条件由两部分组成,一是BoolQueryBuilder算分,二是 FunctionScoreQueryBuilder 操作分数,如果不需要对分数进行修改的话,第二部分可不要
* BoolQueryBuilder 是用来算分的原始查询,用来算分,不能操作分数
* 第一部分 BoolQueryBuilder 开始
*/
//-----------------------多条件拼接start-----------------------------
//根据关键字搜索
String key = params.getKey();
//构建DSL
//构建 booleanQuery(多条件查询)
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//must部分(must参与算分) 关键字搜索
if (StringUtils.isBlank(key)) {
// matchAllQuery : 全量查询,没有条件
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
// matchQuery 根据条件全量查询 多字段全文搜索(全量查询) all为指定多个字段的集合
// 也可以使用 multiMatchQuery 多字段查询,这里是将多个字段合并了一个集合为all
boolQuery.must(QueryBuilders.matchQuery("all", key));
}
// filter 过滤条件 类似and
//filter过滤条件 term()查询是过滤查询,不能放must,因为放must会影响得分,影响得分就会影响性能
if (StringUtils.isNotBlank(params.getCity())) {
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
if (StringUtils.isNotBlank(params.getBrand())) {
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
if (StringUtils.isNotBlank(params.getStarName())) {
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
//范围条件
if (null != params.getMinPrice() && null != params.getMaxPrice()) {
boolQuery.filter(
QueryBuilders.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice()));
}
//-----------------------多条件拼接end-----------------------------
//-----------------------排序 start-----------------------------
//分页条件
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
//价格排序
// request.source().trackScores(true);
// request.source().sort("price", SortOrder.DESC);
//-----------------------排序 end-----------------------------
/**
* 第一部分 BoolQueryBuilder算分结束
*/
/**
* 第二部分、算分控制
* 组合查询-function score
* POST /hotel/_search
* {
* "query": {
* "function_score": {
* "query": {
* "match": {
* "搜索的字段": "搜索的文本"
* }
* },
* "functions": [
* {
* "filter": {
* "term": {
* "字段": "文本"
* }
* },
* "weight": 5
* }
* ]
* }
* }
* }
*/
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
//原始查询(相关算分的查询)
boolQuery,
//functions score (functions)数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
// 其中一个functions score (filter)元素(对象)
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//过滤条件,满足条件才会参与算分
//isAd为参与算分的字段
QueryBuilders.termQuery("isAd", true),
//算分函数
ScoreFunctionBuilders.weightFactorFunction(10))
});
request.source().query(functionScoreQueryBuilder);
return request;
}
//设置多聚合查询条件 group by分组
private void buildAggregation(SearchRequest request) {
request.source().aggregation(AggregationBuilders
//设置名称
.terms("brandAgg")
//字段
.field("brand")
//大小
.size(200)
);
request.source().aggregation(AggregationBuilders
.terms("cityAgg")
.field("city")
.size(200)
);
request.source().aggregation(AggregationBuilders
.terms("starAgg")
.field("starName")
.size(200)
);
}
}
RestHighLevelClientResponse .java 解析es请求后的 SearchResponse 数据
@Component
public class RestHighLevelClientResponse {
@Autowired
RestHighLevelClient client;
/**
* 解析普通请求 response
*
* @param request
* @throws IOException
*/
public List<Object> handleResponse(SearchRequest request) throws IOException {
//es将结果封装成了searchResponse对象返回,根据对象进行解析
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析结果,取hits
SearchHits searchHits = response.getHits();
//取查询的总条数
long value = searchHits.getTotalHits().value;
//查询的结果数组
SearchHit[] hits = searchHits.getHits();
List<Object> lists = new ArrayList<>();
for (SearchHit hit : hits) {
//得到source
String sourceAsString = hit.getSourceAsString();
lists .add(sourceAsString );
}
return lists ;
}
/**
* 解析 bucket 桶聚合 请求 response
*
* @param request
* @throws IOException
*/
public Map<String, List<String>> handleBucketResponse(SearchRequest request) throws IOException {
//es将结果封装成了searchResponse对象返回,根据对象进行解析
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
Aggregations aggregations = response.getAggregations();
Map<String, List<String>> result = new HashMap<>();
result.put("brand", getAggResult(aggregations, "brandAgg"));
result.put("city", getAggResult(aggregations, "cityAgg"));
result.put("star", getAggResult(aggregations, "starAgg"));
return result;
}
private List<String> getAggResult(Aggregations aggregations, String aggName) {
Terms terms = aggregations.get(aggName);
List<? extends Terms.Bucket> buckets = terms.getBuckets();
List<String> b = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
b.add(bucket.getKeyAsString());
}
return b;
}
}
以上仅小弟个人记录,有帮助就看看,没帮助也别喷