前面基础部分参考自bilibili:【尚硅谷】ElasticSearch教程
目录
前言絮絮叨
俗话说的好呀!由纲及目,由浅入深。本文也是按照循序渐进的步调一步一步带各位探索Elasticsearch的查询的用法。前半部分基础简单的内容我就不多详解,大家可以直接看代码加上我在上面写的注释就可以理解了,模板都是一个模板。
俗话又说得好呀!治标不治本,恐怖难除根。在此之前,还是希望米娜桑可以先去熟悉一下原生的es语法结构,而不是一上来就去直接使用api。我本人也是一步一步通过原生的es语法再结合api才推导出具体使用的方法,本文后面介绍的一些高级聚合的查询现在在国内网上都很难找到用法教程,是需要自己不断摸索的!
俗话又又说得好呀!玫瑰给别人,芳香自己留。本文的初衷有二:分享 与 巩固。为了大家,也为了自己。
开始
项目构建与包的导入在用Elastic官方的API,elasticsearch-rest-high-level-client来简单操作Elasticsearch的增删改查等功能中已经介绍过了,不多说辣。
原始数据,在上面那个文章里已经插入过了:
首先开始之前需要建一个客户端
// 创建客户端
RestHighLevelClient client = new RestHighLevelClient(
// EsParam.HOSTNAME, EsParam.PORT, EsParam.SCHEME 换成自己的es的ip,端口,协议(http)
RestClient.builder(new HttpHost(EsParam.HOSTNAME, EsParam.PORT, EsParam.SCHEME))
);
1. 基础查询
1.1 查询全部文档
// TODO 查询AllDoc
@Test
void searchAllDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 填入builder
request.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
// 总命中数
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
结果:
其中的hits.getTotalHits()为es查询的命中数
1.2 分页查询
es提供分页功能,但只是提供。可以用但没必要
// TODO 分页查询Doc
@Test
void searchPageDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())
// 查看哪页:(页面数 - 1)* 每页数据 放到from里
.from(1)
.size(3);
// 发送请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit.getSourceAsMap() = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
result:
1.3 排序查询
// TODO 排序查询Doc
@Test
void searchSortDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder,按照递增排序 SortOrder.ASC
SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())
.sort("price", SortOrder.ASC);
// 发送请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit.getSourceAsMap() = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
result:
1.4 过滤字段
// TODO 过滤字段查询Doc
@Test
void searchFilterDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 过滤,这里只会查询出includes中出现的字段,而excludes中出现的字段不会被显示。
String[] includes = {"author"};
String[] excludes = {};
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())
.fetchSource(includes, excludes);
// 发送请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit.getSourceAsMap() = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
result:
1.5 条件查询
必须满足其中构造的must中的条件;must可以换成should,就变成了可以满足(and与or的关系)
// TODO 组合匹配must查询Doc
@Test
void searchMatchDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("author", "加西亚·马尔克斯"))
.must(QueryBuilders.matchQuery("title", "百年孤独"));
builder.query(boolQueryBuilder);
// 发送请求
request.source(builder);
// 插入数据,返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit.getSourceAsMap() = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
result:
1.6 范围查询
// TODO 范围查询Doc
@Test
void searchRangeDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder();
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price")
.gte(50)
.lte(80);
builder.query(rangeQueryBuilder);
// 发送请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit.getSourceAsMap() = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
result:
2. 复杂查询
说明一下,以后的builder我都会按照下面这种格式构造,这样条理比较清晰,可读性强一点。现在只有一条查询条件,后面会有多个条件结合,大家看到后面应该也会这么觉得。
// 打个比方:用聚合查询的就会这样单独点出一行.aggregation,用到聚合查询中的最大值就会将.max单独点出来一行
SearchSourceBuilder builder = new SearchSourceBuilder()
.aggregation(AggregationBuilders
.max("expensive").field("price"));
2.1 聚合查询最大值(max)
利用聚合查询中的max,找出所有数据中price的最大值
// TODO 聚合max查询Doc
@Test
void searchAggregateDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder()
.aggregation(AggregationBuilders
.max("expensive").field("price"));
// 发送请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
Max max = response.getAggregations().get("expensive");
System.out.println("expensive = " + max.getValueAsString());
System.out.println("response.getTook() = " + response.getTook());
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (SearchHit hit : hits) {
System.out.println("hit.getSourceAsMap() = " + hit.getSourceAsMap());
}
// 关闭连接
client.close();
}
result:
其中response.getTook()为es查询的耗时
2.2 聚合分组查询(term)
类似于Group by,按照作者分类。
其中terms().field()中需要是keyword,test文本是不可以的!
// TODO 聚合分组查询Doc
@Test
void searchAggregateTermDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder()
.aggregation(AggregationBuilders
.terms("authorGroup").field("author.keyword"));
// 创建请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
Terms terms = response.getAggregations().get("authorGroup");
System.out.println("response.getTook() = " + response.getTook());
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (Terms.Bucket bucket : terms.getBuckets()) {
System.out.println("bucket.getKeyAsString() = " + bucket.getKeyAsString());
}
// 关闭连接
client.close();
}
result:
2.3 聚合多分组查询(聚合组合composite)
composite是es6.1后新引入的功能,老版本没有。
聚合多分组,我自己起的名字。。国内网上极少关于high level client api中关于composite的教程。(反正我逛了那么久是没找到一篇es api 中关于 composite 的使用)
我的理解就是多group by,比如查看我们按作者和作品分组,看哪个作者写了哪本书。
注意:这里用builder里用了.composite,那么之后就需要用ParsedComposite去接受response,而不是用从前的term。
// TODO 聚合多分组查询Doc
@Test
void searchAggregateGroupByDoc() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder()
.aggregation(AggregationBuilders
.composite("buckets", Arrays.asList(
new TermsValuesSourceBuilder("authorGroup").field("author.keyword"),
new TermsValuesSourceBuilder("titleGroup").field("title.keyword")
)));
// 创建请求
request.source(builder);
// 返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
SearchHits hits = response.getHits();
ParsedComposite parsedComposite = response.getAggregations().get("buckets");
System.out.println("response.getTook() = " + response.getTook());
System.out.println("hits.getTotalHits() = " + hits.getTotalHits());
for (ParsedComposite.ParsedBucket bucket : parsedComposite.getBuckets()) {
System.out.println("bucket.getKey() = " + bucket.getKey());
}
// 关闭连接
client.close();
}
result:
3. 复杂多条件自由组合查询
再看一下我们的数据
类似于孟德尔的豌豆疯狂杂交,自己想怎么组合看自己,看需求。
// TODO 测试
@Test
void goTest() throws IOException {
// 创建请求
SearchRequest request = new SearchRequest();
request.indices("book1");
// 构造builder
SearchSourceBuilder builder = new SearchSourceBuilder()
// 排序:价格降序排序
.sort("price", SortOrder.DESC)
// query查询:
.query(QueryBuilders.boolQuery()
// 不要价格为58.9的书
.mustNot(QueryBuilders.matchQuery("price", 58.9))
// 要价格在50~100中间的书
.must(QueryBuilders.rangeQuery("price")
.lte(100)
.gte(50)))
// 聚合查询:
.aggregation(AggregationBuilders
// 按照价格分组
.terms("authorGroup").field("author.keyword"));
// 创建请求
request.source(builder);
// 发送请求,返回结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 打印结果
System.out.println("response.getTook() = " + response.getTook());
Terms terms = response.getAggregations().get("authorGroup");
for (Terms.Bucket bucket : terms.getBuckets()) {
System.out.println("bucket.getDocCount() = " + bucket.getDocCount());
System.out.println("bucket.getKeyAsString() = " + bucket.getKeyAsString());
}
// 关闭连接
client.close();
}
result:最后被选出来的就是我们的David!
结尾絮叨叨
es的灵魂就是查询。
es中还有极多的聚合查询手法,比如百分比、平均值等,本文只是列举了我常用的手法。
本文章是偏向实战的,利用es java api通过结合代码来让大家能极快的掌握es查询的使用。
如果能帮到大家,那我真的很高兴。正如俗话所说:“玫瑰赠,手里香”,我能帮到大家我很高兴,那么大家给我一个大拇哥我会巨高兴!因为这让我确实的感受到了我有帮到大家~
拥抱开源~