我们在 springBoot集成elasticsearch 中完成了集成,但是我们需要知道如何使用。其实es和mysql很相识。
es->索引->字段
mysql->表->字段
所以索引和表是一个层级的。
接下来,该说下如何使用。
一:查询索引
/**
* 查询索引是否存在
* @param myIndex
* @return
*/
public boolean queryIndex(String myIndex) {
try {
GetIndexRequest request = new GetIndexRequest(myIndex);
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
return exists;
} catch (Exception e) {
return false;
}
}
二:创建索引
/**
* 创建索引
*
* @return
*/
public Result addIndex(String myIndex) {
try {
//查询索引是否存在
boolean exists =this.queryIndex(myIndex);
if (exists) {
return Result.sendSuccess("索引:" + myIndex + "已经存在");
}
//创建索引
CreateIndexRequest indexRequest = new CreateIndexRequest(myIndex);
CreateIndexResponse response = client.indices()
.create(indexRequest, RequestOptions.DEFAULT);
boolean flag = response.isAcknowledged();
if (flag) {
return Result.sendSuccess("索引:" + myIndex + "创建索引成功!");
} else {
return Result.sendSuccess("索引:" + myIndex + "创建索引失败!");
}
} catch (Exception e) {
e.printStackTrace();
return Result.sendFailure("新增异常");
}
}
三:添加文档(相当于向数据库添加数据)
/**
* 添加文档
* @param myIndex 索引
* @param id es 中id
* @param user 数据
* @return
*/
public Result addDoc(String myIndex, String id, Object user) {
try {
IndexRequest request = new IndexRequest(myIndex);
String source = JSONObject.toJSONString(user);
// 手动设置id
request.id(id);
request.source(source, XContentType.JSON);
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
System.out.println(response.getResult());
return Result.sendSuccess("索引:" + myIndex + "数据成功!", response.getResult());
} catch (Exception e) {
e.printStackTrace();
return Result.sendFailure("添加数据失败");
}
}
四:修改文档
其实和三一样,id一定要有。因为es会检查es中是否存在id已经存在,如果id存在。则更新,如果id不存在,则新增。
五:删除文档
/**
* 删除文档
* @param myIndex
* @param id
* @return
*/
public Result deleteDoc(String myIndex, String id) {
try {
DeleteRequest deleteRequest = new DeleteRequest(myIndex, id);
DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
return Result.sendSuccess("删除文档成功", response.getResult());
} catch (Exception e) {
e.printStackTrace();
return Result.sendFailure("删除成功!");
}
}
六:查询文档
其实查询才是最麻烦的地方,因为这个查询比较多。我们这介绍下分页和查询。查询条件的EsUserDto
@ApiModelProperty(value = "查询条件")
private String name;
@ApiModelProperty(value = "索引")
private String myIndex;
@ApiModelProperty(value = "当前页")
private Integer pageNum;
@ApiModelProperty(value = "当前页大小")
private Integer pageSize;
查询组装
/**
* 查询文档
* @param esUser
* @return
*/
public Result queryDoc(EsUserDto esUser) {
try {
//1:查询条件设置
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//2:设置分页相关
sourceBuilder.from((esUser.getPageNum() - 1) * esUser.getPageSize());
//3:每页查询数据
sourceBuilder.size(esUser.getPageSize());
//4:设置条件查询
BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
if (esUser.getName() != null) {
boolBuilder.must(QueryBuilders.matchQuery("name", esUser.getName()));
}
boolBuilder.should();
sourceBuilder.query(boolBuilder);
//5:按时间排序 降序
sourceBuilder.sort("createTime", SortOrder.DESC);
//6:设置索引
SearchRequest searchRequest = new SearchRequest(esUser.getMyIndex());
searchRequest.source(sourceBuilder);
System.out.println("搜索语句是:" + sourceBuilder.toString());
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("查询条件:" + search);
SearchHits hits = search.getHits();
SearchHit[] hitsArr = hits.getHits();
TotalHits totalHits = hits.getTotalHits();
long total = totalHits.value;
List<EsUser> userList = new ArrayList<>();
for (SearchHit documentFields : hitsArr) {
EsUser esUsers = JSONObject.toJavaObject(JSON.parseObject(documentFields.getSourceAsString()), EsUser.class);
userList.add(esUsers);
}
Map<String, Object> map = new HashMap<>(8);
map.put("total", total);
map.put("data", userList);
return Result.sendSuccess("查询成功", map);
} catch (Exception e) {
e.printStackTrace();
return Result.sendFailure("查询异常!");
}
}
上面则查询成功了。
七:实现高亮
public Result searchHigh(EsUserDto esUserDto){
//1、构造请求对象
SearchRequest searchRequest = new SearchRequest(esUserDto.getMyIndex());
//2:search builder
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("name", esUserDto.getName()));
sourceBuilder.from((esUserDto.getPageNum() - 1) * esUserDto.getPageSize());
sourceBuilder.size(esUserDto.getPageSize());
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//3:sort
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
//4:highlight
HighlightBuilder highlightBuilder = new HighlightBuilder();
HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("name");
highlightTitle.preTags("<span class=\"highlight\">");
highlightTitle.postTags("</span>");
highlightBuilder.field(highlightTitle);
sourceBuilder.highlighter(highlightBuilder);
// add builder into request
searchRequest.indices(esUserDto.getMyIndex());
searchRequest.source(sourceBuilder);
//response
SearchResponse searchResponse = null;
try {
searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
TimeValue took = searchResponse.getTook();
//search hits
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits().value;
SearchHit[] searchHits = hits.getHits();
List<EsUser> postList = new ArrayList<>();
for (SearchHit hit : searchHits) {
String str = hit.getSourceAsString();
EsUser esPost = JSONObject.parseObject(str, EsUser.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField highlight = highlightFields.get("name");
if (highlight != null) {
Text[] fragments = highlight.fragments();
String fragmentString = fragments[0].string();
esPost.setName(fragmentString);
}
postList.add(esPost);
}
return Result.sendSuccess("查询成功",postList);
}
}
八:es查询模板
对于es的相关查询,我们可以有一个比较好理解的格式:
1:创建请求对象
SearchRequest searchRequest = new SearchRequest(this.indexName);
2:构建查询体对象。
//2、构造查询体对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 是否查询所有的数据true 是全部数据 false是10000条数据。
searchSourceBuilder.trackTotalHits(true);
3:条件
//查询都需要在这个下面boolQueryBuilder.must(xxx);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
bool 可以用来合并多个过滤条件查询结果的布尔逻辑,它包含这如下几个操作符:
- must : 多个查询条件的完全匹配,相当于 and。
- must_not ::多个查询条件的相反匹配,相当于 not。
- should : 至少有一个查询条件匹配, 相当于 or。
(1):查询条件(eq等方法 )
term主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型)
boolQueryBuilder.must(QueryBuilders.termQuery(fieldName, fieldValue));
当然,如果想在一个字段匹配多个值的话,可以使用terms,相当于SQL的in语法。
注意: terms是匹配多个
boolQueryBuilder.must(QueryBuilders.termsQuery(key, value));
其中注意对于value,切记不能是List.因为terms内部方法有一个方法:
public TermsQueryBuilder(String fieldName, Object... values) {
this(fieldName, (Iterable)(values != null ? Arrays.asList(values) : (Iterable)null));
}
他是把请求转成List,如果你是list这样查询的时候就变成
{
"terms":{
"key":[[list]]
}
}
所以要判断是否是list,如果是list转成数组,然后执行,如下面这种。
if(value instanceof List){
List valuess=(List)value;
Object[] array = valuess.toArray();
TermsQueryBuilder termsQueryBuilder1 = QueryBuilders.termsQuery(key, array);
boolQueryBuilder.must(termsQueryBuilder1);
}else{
TermsQueryBuilder termsQueryBuilder1 = QueryBuilders.termsQuery(key, value);
boolQueryBuilder.must(QueryBuilders.termsQuery(key, value));
}
(2):范围条件
range可以理解为SQL中的><符号,其中gt是大于,lt是小于,gte是大于等于,lte是小于等于。
//构造单个rangeBuilder
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(fieldName);
//大于等于
rangeQueryBuilder.gte(fieldvalue);
//大于
rangeQueryBuilder.gt(fieldvalue);
//小于等于
rangeQueryBuilder.lte(fieldvalue);
//小于
rangeQueryBuilder.lt(fieldvalue);
boolQueryBuilder.must(rangeQuery);
4:模糊查询
boolQueryBuilder.must(QueryBuilders.wildcardQuery(fieldName,fieldValue));
5:正则表达式
//正则表达式
String regStr="xu[0-9]";
boolQueryBuilder.must(QueryBuilders.regexpQuery(fieldName, regStr));
上面几个个都需要用
searchSourceBuilder.query(boolQueryBuilder);
还有一个排序
searchSourceBuilder.sort("字段", SortOrder.DESC);
6:聚合
(1)sum(统计某个字段的总和) (字段类型必须是数值类型)
String name="sum_name"
searchSourceBuilder.aggregation(AggregationBuilders.sum(name).field(fieldName));
(2):max(某个字段中最大值)
String name="max_name"
searchSourceBuilder.aggregation(AggregationBuilders.max(name).field(fieldName));
(3):min(某个字段中最小值)
String name="min_name"
searchSourceBuilder.aggregation(AggregationBuilders.min(name).field(fieldName));
(4):avg平均值
String name="avg_name"
searchSourceBuilder.aggregation(AggregationBuilders.avg(name).field(fieldName));
(5):查询
searchRequest.source(searchSourceBuilder);
//、调用请求
SearchResponse response =restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
SearchHit[] hist = searchHits.getHits();
logger.info("total==>" + searchHits.getTotalHits().value);
//将数据转换成指定的数据类型
for (SearchHit hit : hist) {
Map map = JSONObject.toJavaObject(JSON.parseObject(hit.getSourceAsString()), Map.class);
logger.info("map==>" + map);
}
如果使用了聚合则通过如下来获取聚合数据
//从返回的结果中获取聚合返回的值
String name="avg_name"
ParsedAggregation parsedAggregation = response.getAggregations().get(name);
//转换程需要的格式。根据聚合的类型来决定
ParsedSum parsedSum = (ParsedSum)parsedAggregation;
long value=parsedSum.getValue()
7:分页
(1):非聚合分页
如果是非聚合分页,只需要
searchSourceBuilder.from((rankDto.getPageNum() - 1) * rankDto.getPageSize());
//每页数据量
searchSourceBuilder.size(rankDto.getPageSize());
(2):聚合分页
聚合的分页在聚合aggreation中添加就行了。
TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("sponsor").field("refreshOpenId.keyword").size(9999);
termsAggregationBuilder.subAggregation(new BucketSortPipelineAggregationBuilder("bucket_field",null).from((rankDto.getPageNum() - 1) * rankDto.getPageSize()).size(rankDto.getPageSize()));
searchSourceBuilder.aggregation(termsAggregationBuilder);
8:嵌套查询(NestedQueryBuilder)
我们有的时候会遇到嵌套查询
BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
if (null != value) {
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(key.substring(0, key.lastIndexOf(".")), QueryBuilders.termQuery(key, value), ScoreMode.None);
nestedBoolQueryBuilder.should(nestedQueryBuilder);
}
boolQueryBuilder.must(nestedBoolQueryBuilder);