返回 导航

SpringBoot / Cloud

hangge.com

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);

(4)区间查询,主要针对数据类型,可以使用 from + to 或者 gtgte + ltlte
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)可以同时指定多个条件,条件之间的关系支持 andmust)、orshould
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 的原生查询语法,更加灵活。
注意ANDORTO 之类的关键字必须大写
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)

回到顶部