elasticsearch-RestClient文档查询
上一章我们讲到用RestClient进行索引库以及对文档的基本操作,这一篇文章,我们学习用RestClient来进行文档的查询操作。
@Slf4j
public class HotelSearchTest {
private RestHighLevelClient client;
/**
* 初始化客户端
*/
@BeforeEach
void setup(){
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.113.130:9200")
));
}
@Test
public void matchAll() throws IOException {
SearchRequest request = new SearchRequest("hotel");
request.source()
.query(QueryBuilders.matchAllQuery());
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
long total = hits.getTotalHits().value;
log.info("总条数:{}",total);
SearchHit[] hitsHits = hits.getHits();
for (SearchHit hitsHit : hitsHits) {
String sourceAsString = hitsHit.getSourceAsString();
HotelDoc hotelDoc = JSONObject.parseObject(sourceAsString, HotelDoc.class);
log.info(String.valueOf(hotelDoc));
}
}
/**
* 关闭客户端
* @throws IOException
*/
@AfterEach
void close() throws IOException {
client.close();
}
}
关键的 API 有两个,一个是 request.source()
,其中包含了查询、排序、分页、高亮等所有功能,另一个是 QueryBuilders
,其中包含 match、term、function_score、bool 等各种查询
解析查询响应
Elasticsearch 返回的结果是一个 JSON 字符串,结构包含
hits
:命中的结果total
:总条数,其中的value是具体的总条数值max_score
:所有结果中得分最高的文档的相关性算分hits
:搜索结果的文档数组,其中的每个文档都是一个 json 对象_source
:文档中的原始数据,也是 json 对象
因此,我们解析响应结果,就是逐层解析 JSON 字符串,流程如下
SearchHits
:通过response.getHits()
获取,就是 json 中的最外层的 hits,代表命中的结果SearchHits.getTotalHits().value
:获取总条数信息SearchHits.getHits()
:获取 SearchHit 数组,也就是文档数组SearchHit.getSourceAsString()
:获取文档结果中的_source
,也就是原始的 json 文档数据
match查询
@Test
public void matchQuery() throws IOException {
SearchRequest request = new SearchRequest("hotel");
request.source()
.query(QueryBuilders.matchQuery("all","如家"));
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
System.out.println("hits.getTotalHits().条数 = " + searchHits.getTotalHits().value);
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
System.out.println(hotelDoc);
}
}
@Test
public void multiMatchQuery() throws IOException {
SearchRequest request = new SearchRequest("hotel");
request.source()
.query(QueryBuilders.multiMatchQuery("如家","name","brand"));
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
System.out.println("hits.getTotalHits().条数 = " + searchHits.getTotalHits().value);
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
System.out.println(hotelDoc);
}
}
精确查询
精确查询主要是两者
- term:词条精确匹配
- range:范围查询
布尔查询
布尔查询是用 must、must_not、filter等方式组合其它查询,代码示例如下
@Test
void testBool() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
request.source()
.query(
QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("city", "上海"))
.filter(QueryBuilders.rangeQuery("price").lte(300))
);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
SearchHits searchHits = response.getHits();
System.out.println("hits.getTotalHits().条数 = " + searchHits.getTotalHits().value);
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
System.out.println(hotelDoc);
}
}
排序、分页
搜索结果的排序和分页是与 query 同级的参数,因此同样是使用 request.source()
来设置。
对应的API如下
@Test
void testPageAndSort() throws IOException {
// 页码,每页大小
int page = 1, size = 5;
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchAllQuery());
// 2.2.排序 sort
request.source().sort("price", SortOrder.ASC);
// 2.3.分页 from、size
request.source().from((page - 1) * size).size(5);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
SearchHits searchHits = response.getHits();
System.out.println("hits.getTotalHits().条数 = " + searchHits.getTotalHits().value);
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
System.out.println(hotelDoc);
}
}
高亮
- 查询的 DSL:其中除了查询条件,还需要添加高亮条件,同样是与 query 同级。
- 结果解析:结果除了要解析
_source
文档数据,还要解析高亮结果
高亮请求的构建 API
@Test
void testHighlight() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchQuery("all", "如家"));
// 2.2.高亮
request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response); //代码在下文
}
高亮结果解析
高亮的结果与查询的文档结果默认是分离的,并不在一起。
因此解析高亮的代码需要额外处理:
- 第一步:从结果中获取 source。
hit.getSourceAsString()
,这部分是非高亮结果,json 字符串,需要反序列为 HotelDoc 对象 - 第二步:获取高亮结果。
hit.getHighlightFields()
,返回值是一个 Map,key 是高亮字段名称,值是HighlightField 对象,代表高亮值 - 第三步:从 map 中根据高亮字段名称,获取高亮字段值对象 HighlightField
- 第四步:从 HighlightField 中获取 Fragments,并且转为字符串。这部分是真正的高亮字符串
- 第五步:用高亮的结果替换 HotelDoc 中的非高亮结果
完整代码如下:
private void handleResponse(SearchResponse response) {
// 4.解析响应
SearchHits searchHits = response.getHits();
// 4.1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 4.2.文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 获取高亮结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (!CollectionUtils.isEmpty(highlightFields)) {
// 根据字段名获取高亮结果
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
// 获取高亮值
String name = highlightField.getFragments()[0].string();
// 覆盖非高亮结果
hotelDoc.setName(name);
}
}
System.out.println("hotelDoc = " + hotelDoc);
}
}
地理坐标查询
相关性得分
function_score 查询结构如下
对应的 JavaAPI 如下
elasticsearch-RestClient文档查询
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/1009