SpringBoot - Elasticsearch Rest Client使用详解2(查询数据、高亮显示)
作者:hangge | 2024-11-01 08:36
在 Elasticsearch 中,要查询单条数据,可以使用 Get。要查询一批满足条件的数据,则需要使用 Search。本文将通过样例演示如何使用 Search 进行数据查询。
四、查询数据
1.查询所有数据
(1)下面代码使用 Search 查询指定索引库 emp 中的所有数据:
// 获取 RestClient 连接 RestHighLevelClient client = new RestHighLevelClient(RestClient.builder( new HttpHost("192.168.60.9", 9200, "http"), new HttpHost("192.168.60.10", 9200, "http"), new HttpHost("192.168.60.11", 9200, "http"))); // 创建查询请求 SearchRequest searchRequest = new SearchRequest(); // 指定索引库,支持指定一个或者多个,也支持通配符,例如:user* searchRequest.indices("emp"); // 执行查询操作 SearchResponse searchResponse =client.search(searchRequest,RequestOptions.DEFAULT); // 获取查询返回的结果 SearchHits hits =searchResponse.getHits(); // 获取数据总数 long numHits = hits.getTotalHits().value; System.out.println("数据总数:"+numHits); // 获取具体内容 SearchHit[] searchHits =hits.getHits(); // 迭代解析具体内容 for(SearchHit hit : searchHits) { String sourceAsString = hit.getSourceAsString(); System.out.println(sourceAsString); } // 关闭与 Elasticsearch 集群的连接,释放资源 client.close();
(2)运行结果如下:
2.指定过滤条件
(1)在使用 Search 查询数据时,可以根据需求指定过滤条件,实现自定义多条件组合查询。下面代码我们查询条件为查询所有数据:
// 获取 RestClient 连接 RestHighLevelClient client = new RestHighLevelClient(RestClient.builder( new HttpHost("192.168.60.9", 9200, "http"), new HttpHost("192.168.60.10", 9200, "http"), new HttpHost("192.168.60.11", 9200, "http"))); // 创建查询请求 SearchRequest searchRequest = new SearchRequest(); // 指定索引库,支持指定一个或者多个,也支持通配符,例如:user* searchRequest.indices("emp"); // 指定查询条件 SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); // 查询所有,可以不指定,默认是查询索引库中的所有数据 searchSourceBuilder.query(QueryBuilders.matchAllQuery()); // 将 SearchSourceBuilder 添加到 SearchRequest searchRequest.source(searchSourceBuilder); // 执行查询操作 SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT); // 获取查询返回的结果 SearchHits hits = searchResponse.getHits(); // 获取数据总数 long numHits = hits.getTotalHits().value; System.out.println("数据总数:"+numHits); // 获取具体内容 SearchHit[] searchHits =hits.getHits(); // 迭代解析具体内容 for(SearchHit hit : searchHits) { String sourceAsString = hit.getSourceAsString(); System.out.println(sourceAsString); } // 关闭与 Elasticsearch 集群的连接,释放资源 client.close();
(2)下面代码对指定字段的值进行过滤。
注意:
- 在查询数据时会对数据进行分词。
- 如果指定了多个 query,则后面的 query 会覆盖前面的 query。
- 对字符串类型内容的查询,不支持通配符
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchQuery("age","20")); //age 的值可以指定为字符串或者数字 searchSourceBuilder.query(QueryBuilders.matchQuery("name","word")); searchRequest.source(searchSourceBuilder);
(3)对于字符串类型内容的查询,支持通配符,但是性能较差,可以认为是全表扫描。
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.wildcardQuery("name","h*")); searchRequest.source(searchSourceBuilder);
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.rangeQuery("age").from(0).to(30)); searchSourceBuilder.query(QueryBuilders.rangeQuery("age").gte(0).lte(30)); searchSourceBuilder.query(QueryBuilders.rangeQuery("age").gte(null).lte(30)); //不限制边界指定为 null searchRequest.source(searchSourceBuilder);
(5)可以同时指定多个条件,条件之间的关系支持 and(must)、or(should)
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.boolQuery() .should(QueryBuilders.matchQuery("name","hangge")) .should(QueryBuilders.matchQuery("age",99))); searchRequest.source(searchSourceBuilder);
- 在多条件组合查询时,可以设置条件的权重值,将满足高权重值条件的数据排到结果列表的前面:
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.boolQuery() .should(QueryBuilders.matchQuery("name","hangge").boost(1.0f)) .should(QueryBuilders.matchQuery("age",99).boost(5.0f))); searchRequest.source(searchSourceBuilder);
(6)可以对多个指定字段的值进行过滤,比如下面代码查询 name 或者 age 这两个字段为 20 的数据:
注意:多个字段的数据类型必须一致,否则会报错,如果查询的字段不存在则不会报错
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.multiMatchQuery("20","name","age")); searchRequest.source(searchSourceBuilder);
(7)通过 queryStringQuery 可以支持 Lucene 的原生查询语法,更加灵活。
注意:AND、OR、TO 之类的关键字必须大写
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.queryStringQuery("name:hangge AND age:[15 TO 30]")); searchRequest.source(searchSourceBuilder);
- queryStringQuery 支持通配符,但是性能也是比较差:
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.queryStringQuery("name:h*")); searchRequest.source(searchSourceBuilder);
(8)通过 termQuery 可以实现精确查询的字段,即查询时不分词,且区分大小写。适用于针对人名、手机号、主机名、邮箱号码等不需要分词查询的场合。
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.termQuery("age","20")); searchRequest.source(searchSourceBuilder);
- 默认情况下中文人名在建立索引时会被拆分成多个词,比如“方大同”会被切分为方、大、同这 3 个词。如果遇到某个字段已经进行了分词,我们使用 termQuery 精确查询是查不出数据的,如果还想要实现精确查询可以借助 queryStringQuery 来解决。
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.termQuery("name","方大同")); // 查不出 searchSourceBuilder.query(QueryBuilders.queryStringQuery("name:\"方大同\"")); // 可以查出 searchRequest.source(searchSourceBuilder);
- 我们也可以使用 matchQuery 解决某个字段已经分词建立索引了,后期还想要实现精确查询的问题。matchQuery 默认会根据分词的结果进行 OR 操作,满足任意一个词语的数据都会被查询出来。我们可以通过 operator 进行设置使得 matchQuery 的分词结果实现 AND 操作,从而间接实现需求(其实是查询了满足方、大、同这 3 个词的内容)
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.termQuery("name","方大同")); // 查不出 searchSourceBuilder.query(QueryBuilders.matchQuery("name","方大同").operator(Operator.AND));//可以查出 searchRequest.source(searchSourceBuilder);
- matchQuery 还有另一种实现针对某个已经分词字段的精确查询方法,就是在 matchQuery 中指定字段的 keyword 类型来实现精确查询,不管在建立索引时有没有被分词都不影响使用:
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.termQuery("name","方大同")); // 查不出 searchSourceBuilder.query(QueryBuilders.matchQuery("name.keyword","方大同"));//可以查出 searchRequest.source(searchSourceBuilder);
3,指定分页条件
Elasticsearch 每次返回的数据默认最多是 10 条(可以认为是一页的数据),这个数据量是可以控制的。核心代码如下:
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.from(0); // 设置每页的起始位置,默认是 0 searchSourceBuilder.size(2); // 设置每页的数据量,默认是 10 searchRequest.source(searchSourceBuilder);
4,指定排序条件
(1)Elasticsearch 默认按照搜索条件的匹配度返回数据,我们可以按照指定的要求对数据进行排序。比如下面代码按照 age 字段倒序排序:SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.sort("age", SortOrder.DESC); searchRequest.source(searchSourceBuilder);
(2)age 字段是数字类型,不需要分词;name 宇段是宇符串类型(ext),默认会被分词,所以不支持排序和聚合操作。如果想根据这些会被分词的字段进行排序或者聚合,则需要指定使用它们的 keyword 类型,这个类型表示不会对数据分词。
SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); searchSourceBuilder.sort("name.keyword",SortOrder.DESC); searchRequest.source(searchSourceBuilder);
5,指定高亮条件
(1)对于用户搜索时的关键词,如果匹配到了,则最终在页面展现时会高亮显示,看起来比较清晰。设置高亮的核心代码如下:
// 获取 RestClient 连接 RestHighLevelClient client = new RestHighLevelClient(RestClient.builder( new HttpHost("192.168.60.9", 9200, "http"), new HttpHost("192.168.60.10", 9200, "http"), new HttpHost("192.168.60.11", 9200, "http"))); // 创建查询请求 SearchRequest searchRequest = new SearchRequest(); // 指定索引库,支持指定一个或者多个,也支持通配符,例如:user* searchRequest.indices("emp"); // 指定查询条件 SearchSourceBuilder searchSourceBuilder =new SearchSourceBuilder(); // 指定查询条件 searchSourceBuilder.query(QueryBuilders.matchQuery("name","word")); // 设置高亮字段(支持多个高亮字段,使用多个 field()方法指定即可) HighlightBuilder highlightBuilder = new HighlightBuilder(); // 设置高亮字段的前缀和后缀内容 highlightBuilder.preTags("<font color='red'>"); highlightBuilder.postTags("</font>"); // 设置高亮字段 highlightBuilder.field("name"); searchSourceBuilder.highlighter(highlightBuilder); searchRequest.source(searchSourceBuilder); // 执行查询操作 SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT); // 获取查询返回的结果 SearchHits hits = searchResponse.getHits(); // 获取数据总数 long numHits = hits.getTotalHits().value; System.out.println("数据总数:"+numHits); // 获取具体内容 SearchHit[] searchHits =hits.getHits(); //迭代解析具体内容 for(SearchHit hit : searchHits) { // 获取文档的源数据(以 Map 形式) Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // 从源数据中提取姓名和年龄信息 String name = sourceAsMap.get("name").toString(); int age = Integer.parseInt(sourceAsMap.get("age").toString()); // 获取高亮字段的内容 Map<String, HighlightField> highlightFields = hit.getHighlightFields(); // 获取 name 宇段的高亮内容 HighlightField highlightField = highlightFields.get("name"); // 检查是否存在高亮字段 if (highlightField != null) { // 获取高亮字段的碎片(可能有多个) Text[] fragments = highlightField.getFragments(); // 重置姓名为高亮后的文本 name = ""; // 将高亮字段的碎片拼接成完整的文本 for (Text text : fragments) { name += text; } } //获取最终的结果数据 System.out.println("姓名:" + name + " 年龄:" + age); } // 关闭与 Elasticsearch 集群的连接,释放资源 client.close();
(2)运行结果如下,可以看到 highlight 中的 word 查询关键字前后已经加上了设置的前缀和后缀了:
全部评论(0)