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 连成字符串。

相关推荐
hummhumm1 小时前
第 10 章 - Go语言字符串操作
java·后端·python·sql·算法·golang·database
man20173 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
hlsd#3 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@3 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端
幸运小圣3 小时前
Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
开发语言·后端·rust
前端SkyRain4 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁4 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
老猿讲编程4 小时前
Rust编写的贪吃蛇小游戏源代码解读
开发语言·后端·rust
黄小耶@4 小时前
python如何使用Rabbitmq
分布式·后端·python·rabbitmq
宅小海6 小时前
Scala-List列表
开发语言·后端·scala