ES-API开发常用功能
创建索引
1.创建索引的索引名称必须为小写,大写会报错。
2.当没有为创建的索引指定Mapping时,es会为索引指定默认的Mapping(eg:字符串默认给keyword)。当查询的时候发现查不到,就有可能是忘记设置Mapping导致类型不对,无法进行分词检索。
3.当索引需要使用别名查询或者删除时,在创建索引的时候要为索引起别名。
es.addAlias([索引名], [别名]);
插入索引
1.将list插入索引中,使用现有框架的方法异步插入即可,注意数据类型的正确性(eg:数值类型的字段如果写成keyword将不能区间查询)。
查询索引
通过KQL进行查询
GET /temp.alias/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"projName": {
"value": "帅气"
}
}
}
]
}
}
}
通过ES的Java-API对索引文档进行条件查询
1.首先创建搜索请求对象SearchRequest
SearchRequest searchRequest = new SearchRequest(TEMP_ALIAS);
2.创建BoolQueryBuilder对象,进行筛选条件填充(类似于sql的where的条件查询must[and]should[or])。BoolQueryBuilder可以多次定义,嵌套使用。
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
其中查询的方式主要有以下几种:
分词查询
只能作用于text类型的字段。对关键词先进行分词处理,然后查询。Operator.AND表示对分词结果的条件进行and筛选,Operator.OR表示对分词结果进行or筛选
boolQueryBuilder.must(new MatchQueryBuilder("name", bo.getKeyword()).analyzer("ik_smart").operator(Operator.AND));
短语查询
有的需求需要支持像类似于mysql的模糊查询,例如名称/地址等条件搜索框。
MatchPhraseQueryBuilder name = QueryBuilders.matchPhraseQuery("name", name);
多条件and查询
当条件是集合入参时,使用termsQuery,类似于sql的in;单个入参时使用termQuery,类似于sql的=。
boolQueryBuilder.must(QueryBuilders.termsQuery("name", nameList));
boolQueryBuilder.must(QueryBuilders.termQuery("name", name));
多条件should查询
QueryBuilders.boolQuery().should();
嵌套查询
boolQueryBuilder.must(QueryBuilders.boolQuery());
范围查询
QueryBuilders.rangeQuery("date").gt(dateMin).lt(dateMax);
数组查询
数组查询只需要按照mapping的层级结构.对应的属性即可,其他查询方法完全相同。
boolQueryBuilder.must(QueryBuilders.rangeQuery("datre").gt(dateMin).lt(dateMax));
半径搜索
半径搜索功能需要在索引的时候建立特有的geo_point类型。
boolQueryBuilder.must(QueryBuilders.geoDistanceQuery("location")
.point(location.getLat(),location.getLon()).
distance(distance.toString(), DistanceUnit.KILOMETERS));
聚合查询-count
例如统计某个字段分别有多少条记录,类似与sql的count(*)
-
条件组装
sourceBuilder.aggregation(AggregationBuilders.terms("propertyTypeAgg").field("cityId"));
-
结果值的获取
private Map<String,String> getCountAggByName(SearchResponse response, String name) { Map<String,String> brandMap = new HashMap<>(); Aggregations aggregations = response.getAggregations(); // 4.1.根据聚合名称获取聚合结果 Terms brandTerms = aggregations.get(aggName); // 4.2.获取buckets List<? extends Terms.Bucket> buckets = brandTerms.getBuckets(); // 4.3.遍历 for (Terms.Bucket bucket : buckets) { // 4.4.获取key String key = bucket.getKeyAsString(); long docCount = bucket.getDocCount(); brandMap.put(key,String.valueOf(docCount)); } return brandMap; }
聚合查询-sum,max,min,avg,mid
例如要统计某个字段的最大值、最小值,平均值,中位数,汇总值,要求该字段必须为数字类型,不然会抛出异常:需要数字类型,但得到了[keyword]。类似与sql对应的同名函数。
-
条件组装
//最大值、最小值,平均值,中位数,汇总值sourceBuilder.aggregation(AggregationBuilders.stats("startingPriceAgg").field("startingPrice")); //中位数(50代表取50%的数据) sourceBuilder.aggregation(AggregationBuilders.percentiles("buildAreaAgg").field("buildArea").percentiles(50));
-
结果值的获取
private Map<String,String> getMetricsAggByName(SearchResponse response, String name) { Map<String,String> brandMap = new HashMap<>(); Aggregations aggregations = response.getAggregations(); //1.根据聚合名称获取聚合结果 ParsedStats aggregation = aggregations.get(aggName); //2.获取值 brandMap.put("max",aggregation.getMaxAsString()); brandMap.put("min",aggregation.getMinAsString()); brandMap.put("avg",aggregation.getAvgAsString()); brandMap.put("sum",aggregation.getSumAsString()); return brandMap; } private Map<String,String> getMiddleAggByName(SearchResponse response, String aggName) { Map<String,String> brandMap = new HashMap<>(); Aggregations aggregations = response.getAggregations(); //1.根据聚合名称获取聚合结果 ParsedTDigestPercentiles aggregate = aggregations.get(aggName); //2.获取buckets brandMap.put("mid",aggregate.percentileAsString(50)); return brandMap; }
聚合查询-区间段聚合
如果要根据单价段、面积段等区间条件聚合,就需要用到range聚合。
-
条件组装
//区间分组查询 sourceBuilder.aggregation(AggregationBuilders.range("buildAreaRangeAgg") .field("buildArea") .addUnboundedTo("100以下", 100) .addRange("100-200", 100,200) .addUnboundedFrom("200以上", 200));
-
结果值获取
private Map<String,String> getRangeAggByName(SearchResponse response, String name) { Map<String,String> brandMap = new HashMap<>(); Aggregations aggregations = response.getAggregations(); // 4.1.根据聚合名称获取聚合结果 ParsedRange aggregation = aggregations.get(aggName); // 4.2.获取buckets List<? extends Range.Bucket> buckets = aggregation.getBuckets(); for (Range.Bucket bucket : buckets) { // 4.4.获取key String key = bucket.getKeyAsString(); long docCount = bucket.getDocCount(); brandMap.put(key,String.valueOf(docCount)); } return brandMap; }
3.创建SearchSourceBuilder查询对象,并进行查询
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(boolQueryBuilder);
4.分页(类似于mysql的limit)
sourceBuilder.from((page - 1) * pageSize).size(pageSize);
5.排序(类似于sql的order by)
sourceBuilder.sort(orderColumn, SortOrder.ASC);
6.设置查询的超时时间
sourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
7.set进搜索请求对象
searchRequest.source(sourceBuilder);
8.发起请求进行搜索
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
部分响应字段:
RestStatus status = searchResponse.status();//响应状态
TimeValue took = searchResponse.getTook();//查询时间
Boolean terminatedEarly = searchResponse.isTerminatedEarly();//请求是否提前终止
boolean timedOut = searchResponse.isTimedOut();//是否超时
response.getHits().totalHits;//命中次数
9.以Json-String或者map的形式获得查询结果。
String sourceAsString = hit.getSourceAsString();
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
一般用法
//声明接收结果的集合
List<TempDto> tempList = new ArrayList<>();
//遍历结果
Arrays.stream(response.getHits().getHits()).forEach(i -> {
try {
//通过jackson的ObjectMapper转化为对象
TempDtoDoc d = new ObjectMapper().readValue(i.getSourceAsString(),TempDtoDoc.class);
TempDto temp= new TempDto();
BeanUtils.copyProperties(d, temp);
//add到我们需要的list中
tempList.add(temp);
} catch (IOException e) {
logger.error(e.getMessage());
}
});
推荐
在代码调试的过程中发现代码查不到数据,可以在拼接好查询条件后search之前打断点,拿到sourceBuilder的Json串,放到Kibana界面进行查询,寻找原因,这样可以更加直观的看到各个条件之间的逻辑关系。