上一篇我们学习了如何使用 Java High Level REST Client 的相关 API 来操作索引、文档,在此基础上,今天我们来学习如何文档查询。如果你之前已经掌握了如何使用 RESTful API 来实现文档查询,那么使用 Java High Level REST Client 相关的 API 进行文档查询时,你会发现似曾相似,简直就是一个模式,下边我们具体来看。
1、QueryBuilders
我们先认识一个类QueryBuilders
,它提供了许多构建文档查询条件的静态方法,例如:
public static MatchQueryBuilder matchQuery(String name, Object text) {}
public static MatchPhraseQueryBuilder matchPhraseQuery(String name, Object text) {}
public static TermQueryBuilder termQuery(String name, String value) {}
public static RangeQueryBuilder rangeQuery(String name) {}
public static BoolQueryBuilder boolQuery() {}
......
......
可以看到这些静态方法都返回了一个 Builder 类型,其实我们也可以自己new
对应的 Builder 类,QueryBuilders
的相关静态方法就是这么做的,只是简化了我们的操作。通过这些 Builder 类就可以来构建我们的查询条件了,单从它们的名字来看就和我们之前学习 RESTful API 时的match
、match_phrase
、term
、range
、bool
很类似,其实就是一回事。
例如,查询school
是北大
,并且age
大于等于10
小于等于50
的数据,可以使用如下方式构建查询条件:
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(new TermQueryBuilder("school.keyword", "北大"))
.must(new RangeQueryBuilder("age").gte(10).lte(50));
2、SearchSourceBuilder
前边我们已经可以构建查询条件了,那么接下来就是如何去接收已有的查询条件了。这里需要使用SearchSourceBuilder
类了:
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
...省略boolQueryBuilder的构建...
searchSourceBuilder.query(boolQueryBuilder);
SearchSourceBuilder
的query
方法用来接收一个查询条件的对象。SearchSourceBuilder
除了接收查询条件,还可以对查询结果排序、分页、查询结果包含文档的那些字段、超时、高亮等等,下边我们来看几个常用的功能:
- 排序
按照age
降序排列查询结果:
searchSourceBuilder.sort("age", SortOrder.DESC);
- 分页
从第0行开始查询20条数据:
searchSourceBuilder.from(0);
searchSourceBuilder.size(20);
- 字段过滤
只返回文档中name
、age
、school
三个字段:
String[] includeFields = new String[]{"name", "age", "school"};
searchSourceBuilder.fetchSource(includeFields, new String[]{});
- 高亮
设置高亮的字段以及高亮包裹的 html 标签:
HighlightBuilder highlightBuilder = new HighlightBuilder()
.field("name")
.preTags("<span style='color:red'>")
.postTags("</span>");
searchSourceBuilder.highlighter(highlightBuilder);
- 超时时间
设置查询的超时时间为10秒:
searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
3、SearchRequest
通过QueryBuilders
、SearchSourceBuilder
类,我们已经可以构建出复杂的查询了,接下就是去查询了:
SearchRequest request = new SearchRequest("user");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
...省略查询的构建过程...
request.source(searchSourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
4、实例
在编写实例之前,先根据上一篇的内容准备好如下数据:
数据有了,就开始我们的查询吧。下边的查询代码基本用到了我们上边介绍的内容,关键的都有注释:
public void searchDocument() throws IOException {
SearchRequest request = new SearchRequest("user");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// school 是清华或北大的
BoolQueryBuilder schoolQueryBuilder = QueryBuilders.boolQuery()
.should(QueryBuilders.termQuery("school.keyword", "北大"))
// .should(QueryBuilders.matchPhraseQuery("school", "北大"))
.should(QueryBuilders.termQuery("school.keyword", "清华"));
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(schoolQueryBuilder)
// name 以王开头的
.must(QueryBuilders.matchPhrasePrefixQuery("name", "王"))
// age 大于等于10小于等于70
.must(QueryBuilders.rangeQuery("age").gte(10).lte(70));
// 设置查询条件
searchSourceBuilder.query(boolQueryBuilder);
// 字段过滤
String[] includeFields = new String[]{"name", "age", "school"};
searchSourceBuilder.fetchSource(includeFields, new String[]{});
// 设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder()
.field("name")
.preTags("<span style='color:red'>")
.postTags("</span>");
searchSourceBuilder.highlighter(highlightBuilder);
// 排序
searchSourceBuilder.sort("age", SortOrder.DESC);
// 分页
searchSourceBuilder.from(0);
searchSourceBuilder.size(20);
// 超时时间
searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
request.source(searchSourceBuilder);
// 发起查询请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
for (SearchHit hit : response.getHits()) {
// 提取高亮的字段内容,因为查询出来的文档数据和高亮字段的数据是分开的
String highlightName = hit.getHighlightFields().get("name").fragments()[0].toString();
// 提取查询出的文档数据,并转成对象
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
// 用高亮的字段内容覆盖覆盖原文档字段
user.setName(highlightName);
System.out.println(JSON.toJSONString(user));
}
}
查询结果如下:
上边分页时每次的开始数据行数是一个固定值0,并不能真正的分页,要分页我们就要先通过总数据条数计算出总页数,总数数据条数可通过如下方法获得:
long totalHits = response.getHits().getTotalHits().value;
接下来具体的分页操作就简单了。
关于文档的查询就先介绍这么多了,下一篇介绍文档的聚合查询。