ES查询代码详细分析

java 复制代码
  //1.检查参数
        if(dto == null || StringUtils.isBlank(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.设置查询条件
        SearchRequest searchRequest = new SearchRequest("app_info_article");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //关键字的分词之后查询
        QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(dto.getSearchWords()).field("title").field("content").defaultOperator(Operator.OR);
        boolQueryBuilder.must(queryStringQueryBuilder);

        //查询小于mindate的数据
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime().getTime());
        boolQueryBuilder.filter(rangeQueryBuilder);

        //分页查询
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(dto.getPageSize());

        //按照发布时间倒序查询
        searchSourceBuilder.sort("publishTime", SortOrder.DESC);

        //设置高亮  title
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);


        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);


        //2.结果封装返回

        List<Map> list = new ArrayList<>();

        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            String json = hit.getSourceAsString();
            Map map = JSON.parseObject(json, Map.class);
            //处理高亮
            if(hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0){
                Text[] titles = hit.getHighlightFields().get("title").getFragments();
                String title = StringUtils.join(titles);
                //高亮标题
                map.put("h_title",title);
            }else {
                //原始标题
                map.put("h_title",map.get("title"));
            }
            list.add(map);
        }

        return ResponseResult.okResult(list);

1. 设置查询条件

java 复制代码
 SearchRequest searchRequest = new SearchRequest("app_info_article");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

app_info_article 是传了个索引库名称。
searchRequest 是一个请求对象,真正的条件不会放在这里面。

我们调用 是source ()函数,searchRequest.source()中的参数为 SearchSourceBuilder类型。我们所有的条件都是通过这个对象创建。

1.1 设置布尔查询

java 复制代码
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

因为我们的查询条件不止一个,不能写完一个就把查询放进 builder 里。所以我们构建了一个布尔查询的构建器。

注意: 布尔只是多条件查询 具体逻辑必须或者可以那要看后续的调用是must还是should。


1.2 关键词分词之后再去查询

java 复制代码
QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders
                            .queryStringQuery(dto.getSearchWords())
                            .field("title")
                            .field("content")
                            .defaultOperator(Operator.OR);

boolQueryBuilder.must(queryStringQueryBuilder);
  1. 我们设置queryStringQuery(),就是分词之后再去查询。
  2. 我们在两个域中去查询,一个是 title,一个是 content。
  3. 然后设置了一个逻辑关系or。
  4. 最后放进布尔查询器,must是指 and 关系.

1.3查询小于min date的数据

java 复制代码
RangeQueryBuilder rangeQueryBuilder = QueryBuilders
                                      .rangeQuery("publishTime")
                                      .lt(dto.getMinBehotTime()
                                      .getTime());
boolQueryBuilder.filter(rangeQueryBuilder);

rangeQuery是指我们按照范围去查询,域的名称为"publishTime"。

小于dto.getMinBehotTime()

getTime()是一个方法,它返回Date对象的毫秒数表示。

这里的最后为什么要加.getTime()呢?因为这样可以让Elasticsearch知道要比较的值是一个日期类型的数值,而不是一个日期类型本身 。这样可以避免出现类型转换错误或者精度损失。

如果不加.getTime(),那么Elasticsearch会认为最小被动时间是一个日期类型本身,而不是一个日期类型的数值。这样可能会导致以下问题:

  • 如果最小被动时间不在当前时间之前或者之后,那么Elasticsearch可能无法正确地计算出匹配范围。
  • 如果最小被动时间在当前时间之前或者之后,并且有多个文档满足条件(例如同时发布了多篇文章),那么Elasticsearch可能无法正确地排序或过滤文档。
  • 如果最小被动时间在当前时间之前或者之后,并且有多个文档满足条件(例如同时发布了多篇文章),那么Elasticsearch可能无法正确地评分或分数文档。

1.4 分页查询

java 复制代码
searchSourceBuilder.from(0);
searchSourceBuilder.size(dto.getPageSize());

因为是滚动页面,我们 from 设置为 0 即可。

1.5 设置高亮 title

java 复制代码
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
highlightBuilder.postTags("</font>");
searchSourceBuilder.highlighter(highlightBuilder);
go 复制代码
  ` highlightBuilder.preTags();`设置高亮前缀
go 复制代码
   ` highlightBuilder.postTags();`设置高亮后缀

2.结果封装返回

java 复制代码
List<Map> list = new ArrayList<>();

SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
	String json = hit.getSourceAsString();
	Map map = JSON.parseObject(json, Map.class);
	//处理高亮
	if(hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0){
		Text[] titles = hit.getHighlightFields()
		                   .get("title")
		                   .getFragments();
		String title = StringUtils.join(titles);
		//高亮标题
		map.put("h_title",title);
	}else {
		//原始标题
		map.put("h_title",map.get("title"));
	}
	list.add(map);
}

return ResponseResult.okResult(list);
java 复制代码
SearchHit[] hits = searchResponse.getHits().getHits();

hits 是我们的命中数,因为包装了两层所以要调用两次getHits

  • 在Elasticsearch中,SearchResponse对象包含了搜索请求的响应。getHits()方法返回一个SearchHits对象,它包含了所有匹配搜索条件的文档(称为"hits")。
  • **SearchHits对象有一个getHits()方法,它返回一个SearchHit数组。**每个SearchHit对象代表一个匹配的文档,包含了文档的元数据(如索引名、类型、id等)和源数据(即原始的文档内容)。
  • 因此,**SearchHit[] hits = searchResponse.getHits().getHits();**这行代码的作用是获取所有匹配的文档,并将它们存储在**SearchHit**数组中。
java 复制代码
String json = hit.getSourceAsString();
Map map = JSON.parseObject(json, Map.class);

hit 是我们的某一个结果,因为是 JASON 格式,所以我们要将他转成 map 结果。

java 复制代码
Text[] titles = hit.getHighlightFields()
		.get("title")
		.getFragments();
String title = StringUtils.join(titles);

如果您不调用**getFragments()**方法,那么您将无法获取到高亮片段的具体内容。这意味着,您无法在结果中看到哪些部分的内容匹配了搜索条件,也就无法突出显示这些部分。

最后将 titles 连成字符串。

相关推荐
郑祎亦39 分钟前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
本当迷ya1 小时前
💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)
后端·程序员
计算机毕设指导62 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
paopaokaka_luck2 小时前
[371]基于springboot的高校实习管理系统
java·spring boot·后端
捂月3 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
瓜牛_gn4 小时前
依赖注入注解
java·后端·spring
Estar.Lee4 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪4 小时前
Django:从入门到精通
后端·python·django
一个小坑货4 小时前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet274 小时前
【Rust练习】22.HashMap
开发语言·后端·rust