【ElasticSearch】使用 Java 客户端 RestClient 实现对文档的查询操作,以及对搜索结果的排序、分页、高亮处理

文章目录

  • 前言:RestClient 查询文档的 RestAPI
  • 一、全文检索查询
    • 1.1 match_all 查询
    • 1.2 match 查询
    • 1.3 multi_match 查询
  • 二、精确查询
    • 2.1 term 查询
    • 2.2 range 查询
  • 三、复合查询:Boolean 查询与 function score 查询的综合案例
  • 四、对查询结果的处理
    • 4.1 将查询结果按照自己的距离远近排序
    • 4.2 根据前端请求参数进行分页操作
    • 4.3 对搜索关键字进行高亮处理


前言:RestClient 查询文档的 RestAPI

在 Elasticsearch 中,通过 RestAPI 进行 DSL 查询语句的构建通常是通过 HighLevelRestClient 中的 resource() 方法来实现的。该方法包含了查询、排序、分页、高亮等所有功能,为构建复杂的查询提供了便捷的接口。

RestAPI示意图

RestAPI 中构建查询条件的核心部分是由一个名为 QueryBuilders 的工具类提供的。该工具类包含了各种查询方法,如下图所示:

QueryBuilders工具类

查询的基本步骤如下:

  1. 创建 SearchRequest 对象。
  2. 准备 Request.source(),也就是 DSL。
  3. 使用 QueryBuilders 构建查询条件。
  4. 将查询条件传入 Request.source().query() 方法。
  5. 发送请求,得到结果。
  6. 解析结果,可以参考 JSON 结果,从外到内逐层解析。

这种方式使得构建复杂的查询变得简单而灵活。在接下来的实例中,我们将深入学习如何使用 HighLevelRestClient 中的 resource() 方法构建各种查询,并充分利用 QueryBuilders 工具类来满足不同的搜索需求。

一、全文检索查询

1.1 match_all 查询

match_all 查询的单元测试代码:

@Test
void testMatchAll() throws IOException {// 1. 准备RequestSearchRequest request = new SearchRequest("hotel");// 2. 组织 DSL 请求request.source().query(QueryBuilders.matchAllQuery());// 3. 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果List<HotelDoc> hotelDocs = handleResponse(response);System.out.println(hotelDocs);
}

这个测试代码演示了如何使用 match_all 查询来获取指定索引(这里是 “hotel”)下的所有文档。具体步骤如下:

  1. 创建 SearchRequest 对象,指定索引为 “hotel”。
  2. 使用 QueryBuilders.matchAllQuery() 构建查询条件。
  3. 将查询条件添加到请求的 DSL 中。
  4. 发送请求,得到查询结果。
  5. 解析查询结果,将文档转换为 HotelDoc 对象。

1.2 match 查询

@Test
void testMatch() throws IOException {// 1. 准备RequestSearchRequest request = new SearchRequest("hotel");// 2. 组织 DSL 请求request.source().query(QueryBuilders.matchQuery("all", "如家"));// 3. 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果List<HotelDoc> hotelDocs = handleResponse(response);System.out.println(hotelDocs);
}

这个测试代码演示了如何使用 match 查询来搜索包含特定关键词(这里是 “如家”)的文档。具体步骤如下:

  1. 创建 SearchRequest 对象,指定索引为 “hotel”。
  2. 使用 QueryBuilders.matchQuery("all", "如家") 构建查询条件,表示在 “all” 字段中搜索包含 “如家” 的文档。
  3. 将查询条件添加到请求的 DSL 中。
  4. 发送请求,得到查询结果。
  5. 解析查询结果,将文档转换为 HotelDoc 对象。

1.3 multi_match 查询

@Test
void testMultiMatch() throws IOException {// 1. 准备RequestSearchRequest request = new SearchRequest("hotel");// 2. 组织 DSL 请求request.source().query(QueryBuilders.multiMatchQuery("如家", "name", "business"));// 3. 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果List<HotelDoc> hotelDocs = handleResponse(response);System.out.println(hotelDocs);
}

这个测试代码演示了如何使用 multi_match 查询来在多个字段(这里是 “name” 和 “business”)中搜索包含特定关键词(这里是 “如家”)的文档。具体步骤如下:

  1. 创建 SearchRequest 对象,指定索引为 “hotel”。
  2. 使用 QueryBuilders.multiMatchQuery("如家", "name", "business") 构建查询条件,表示在 “name” 和 “business” 字段中搜索包含 “如家” 的文档。
  3. 将查询条件添加到请求的 DSL 中。
  4. 发送请求,得到查询结果。
  5. 解析查询结果,将文档转换为 HotelDoc 对象。

可以看到,这些测试代码的结构类似,只是在构建查询条件时使用了不同的 QueryBuilders 方法,用于满足不同的查询需求。

二、精确查询

精确查询常见的有 term 查询和 range 查询,同样使用 QueryBuilders 指定具体的查询方式。

2.1 term 查询

@Test
void testTerm() throws IOException {// 1. 准备RequestSearchRequest request = new SearchRequest("hotel");// 2. 组织 DSL 请求request.source().query(QueryBuilders.termQuery("city", "上海"));// 3. 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果List<HotelDoc> hotelDocs = handleResponse(response);System.out.println(hotelDocs);
}

这个测试代码演示了如何使用 term 查询来搜索指定字段(这里是 “city”)中包含特定关键词(这里是 “上海”)的文档。具体步骤如下:

  1. 创建 SearchRequest 对象,指定索引为 “hotel”。
  2. 使用 QueryBuilders.termQuery("city", "上海") 构建查询条件,表示在 “city” 字段中搜索包含 “上海” 的文档。
  3. 将查询条件添加到请求的 DSL 中。
  4. 发送请求,得到查询结果。
  5. 解析查询结果,将文档转换为 HotelDoc 对象。

2.2 range 查询

@Test
void testRange() throws IOException {// 1. 准备RequestSearchRequest request = new SearchRequest("hotel");// 2. 组织 DSL 请求request.source().query(QueryBuilders.rangeQuery("price").gte(150).lte(200));// 3. 发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果List<HotelDoc> hotelDocs = handleResponse(response);System.out.println(hotelDocs);
}

这个测试代码演示了如何使用 range 查询来搜索指定字段(这里是 “price”)中在给定范围内的文档。具体步骤如下:

  1. 创建 SearchRequest 对象,指定索引为 “hotel”。
  2. 使用 QueryBuilders.rangeQuery("price").gte(150).lte(200) 构建查询条件,表示在 “price” 字段中搜索价格在 150 到 200 之间的文档。
  3. 将查询条件添加到请求的 DSL 中。
  4. 发送请求,得到查询结果。
  5. 解析查询结果,将文档转换为 HotelDoc 对象。

这些测试代码演示了如何使用精确查询来满足特定的搜索需求。在实际应用中,可以根据具体的业务场景和数据结构选择不同的查询方式。

三、复合查询:Boolean 查询与 function score 查询的综合案例

例如,现在通过一个酒店预订网址的查询功能不但可以在搜索框输入关键字进行查询,还可以勾选指定的筛选条件,比如城市、星级、品牌和价格范围:


另外,再所有的酒店数据中还存在一部分属于广告(ES 文档中新增一个布尔类型的 isAD 字段表示),要求查询结果中的广告需要顶置显示,因此整个查询分为两部分:即 boolean 查询和 function score 查询。

  • boolean 查询:实现对搜索关键字的查询,以及过滤:城市、星级、品牌和价格等条件;
  • function score 查询:实现对广告文档的相关性增加操作。

具体的查询代码如下:

private static void buildBasicQuery(RequestParams params, SearchRequest request) {// 1. 原始查询 QueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 关键字搜索 mustString key = params.getKey();if (key == null || key.isEmpty()) {boolQuery.must(QueryBuilders.matchAllQuery());} else {boolQuery.must(QueryBuilders.matchQuery("all", key));}// 城市条件if (params.getCity() != null && !params.getCity().isEmpty()) {boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));}// 品牌条件if (params.getBrand() != null && !params.getBrand().isEmpty()) {boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));}// 星级条件if (params.getStarName() != null && !params.getStarName().isEmpty()) {boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));}// 价格范围if (params.getMaxPrice() != null && params.getMinPrice() != null) {boolQuery.filter(QueryBuilders.rangeQuery("price").lte(params.getMaxPrice()).gte(params.getMinPrice()));}// 2. 算分查询FunctionScoreQueryBuilder functionScoreQuery =QueryBuilders.functionScoreQuery(// 原始查询,相关信算法的查询boolQuery,// function score 数组new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{// 其中一个具体的 function scorenew FunctionScoreQueryBuilder.FilterFunctionBuilder(// 过滤条件QueryBuilders.termQuery("isAD", true),// 算分函数ScoreFunctionBuilders.weightFactorFunction(10))});request.source().query(functionScoreQuery);
}

对上述代码的详细说明:

  • RequestParams 是一个封装了前端请求参数的对象,里面包含了 ES 文档中的各个字段;
  • 在这个方法中,首先构建了一个 boolQuery 作为原始查询条件,包含了关键字搜索、城市、品牌、星级和价格范围等多个条件;
  • 接着,构建了一个 functionScoreQuery,将原始查询作为参数传入,同时定义了一个 FilterFunctionBuilder,用于处理广告部分的查询,对广告文档的分数进行加权,使其在查询结果中更靠前显示;
  • 最后,将 functionScoreQuery 设置为请求的查询条件。

这个综合的查询案例涵盖了多个条件的组合查询以及对特定文档的加权分数处理。在实际应用中,可以根据业务需求扩展和修改这个查询方法。

四、对查询结果的处理

4.1 将查询结果按照自己的距离远近排序

在前端查询酒店数据的时候,一般都会定位获取到自己当前的位置,然后传递给后端。

  • RequestParams 作为前端参数的封装对象,包含了这个位置信息,可通过get 方法进行获取;
  • 然后可以通过 SortBuilders 中的 geoDistanceSort 方法计算距离并进行排序操作,即可获取酒店与自己的实际距离。
String location = params.getLocation();
if (location != null && !location.isEmpty()) {request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));
}

4.2 根据前端请求参数进行分页操作

分页需要的页码以及页面大小一般都由前端传递参数给后端,然后后端获取这两个参数进行分页操作。

  • RequestParams 作为前端参数的封装对象,包含了这两个参数,可通过get 方法进行获取;
  • 然后再根据分页偏移量的计算公式offset = (page - 1) * size 即可获取偏移量,然后通过 sourcefromsize 方法,即可实现分页查询。

例如:

int page = params.getPage();int size = params.getSize();request.source().from((page - 1) * size).size(size);

4.3 对搜索关键字进行高亮处理

要对搜索关键字进行高亮处理同样非常简单,只需要使用 resouce 中的 highlighter 方法指定要进行高亮的字段即可。

实现的示例代码如下:

@Test
void testHighlight() throws IOException {// 1. 准备RequestSearchRequest request = new SearchRequest("hotel");// 2. 组织 DSL 请求// 2.1 queryrequest.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. 解析结果List<HotelDoc> hotelDocs = handleResponse(response);System.out.println(hotelDocs);
}

说明:

注意requireFieldMatch方法的作用是高亮的字段是否需要和搜索的字段匹配。例如:使用字段 all 查询匹配 brand 字段,此时 brand 字段使用了 copy_toall 字段,因此要高亮的字段并不匹配,如果不设置 requireFieldMatchfalse 则会高亮失败。

对结果的解析:
实现高亮的原理是对搜索关键字加上了 <em> </em> 标签,通过在 Kibana 中使用高亮处理的 DSL 语句可以发现:


其实高亮的处理并没有作用到查询出的原始文档中,而是在每个 hits 里面新增了一个 highlight 字段,里面包含了高亮的关键字,因此还需要对 handleResponse 函数进行改造,使得能够处理高亮的查询结果:

例如,在handleResponse 函数的 result.add(hotelDoc) 这句代码之前,新增了以下代码用于处理高亮的情况,即使用高亮处理了的关键字替换查询结果中对应的关键字:

// 获取高亮结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(highlightFields != null && !highlightFields.isEmpty()) {// 获取高亮名称HighlightField highlightField = highlightFields.get("name");// 获取高亮的值String name = highlightField.getFragments()[0].toString();hotelDoc.setName(name);
}

当运行这段测试代码,即可发现成功对 name 字段的 “如家” 添加了 <em> </em> 标签:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/107153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ArcGIS笔记5_生成栅格文件时保存报错怎么办

本文目录 前言Step 1 直接保存到指定文件夹会报错Step 2 先保存到默认位置再数据导出到指定文件夹 前言 有时生成栅格文件时&#xff0c;保存在自定义指定的文件夹内会提示出错&#xff0c;而保存到默认位置则没有问题。因此可以通过先保存到默认位置&#xff0c;再数据导出到…

【Eclipse】设置自动提示

前言&#xff1a; eclipse默认有个快捷键&#xff1a;alt /就可以弹出自动提示&#xff0c;但是这样也太麻烦啦&#xff01;每次都需要手动按这个快捷键&#xff0c;下面给大家介绍的是&#xff1a;如何设置敲的过程中就会出现自动提示的教程&#xff01; 先按路线找到需要的页…

来入门一下C语言打印Hello World

&#x1f4da;目录 安装程序官网下载软件启动程序运行代码解决乱码 安装环境&#xff08;必看&#xff09;IntelliJ Clion 环境执行运行的exe出问题了安装MinGW官网&#xff08;太慢了跳过&#xff09;Clion软件的MinGW 在这里插入图片描述 ![在这里插入图片描述](https://img-…

OpenAI开放gpt-3.5turbo微调fine-tuning测试教程

文章目录 openai微调 fine-tuning介绍openai微调地址jsonl格式数据集准备点击上传文件 openai微调 fine-tuning介绍 openai微调地址 网址&#xff1a;https://platform.openai.com/finetune jsonl格式数据集准备 使用Chinese-medical-dialogue-data数据集git clone进行下载 …

18.(开发工具篇Gitlab)Git如何回退到指定版本

首先: 使用git log命令查看提交历史,找到想要回退的版本的commit id. 使用git reset命令 第一步:git reset --hard 命令是强制回到某一个版本。执行后本地工程回退到该版本。 第二步:利用git push -f命令强制推到远程 如下所示: 优点:干净利落,回滚后完全回到最初状态…

C# 图解教程 第5版 —— 第4章 类型、存储和变量

文章目录 4.1 C# 程序是一组类型声明4.2 类型是一种模板&#xff08;*&#xff09;4.3 实例化类型4.4 数据成员和函数成员4.5 预定义类型4.6 用户定义类型4.7 堆和栈&#xff08;*&#xff09;4.8 值类型和引用类型4.9 变量4.9.1 变量声明4.9.2 多变量声明&#xff08;*&#x…

【LeetCode】每日一题两数之和寻找正序数组的中位数找出字符串中第一个匹配项的下标在排序数组中查找元素的第一个和最后一个位置

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法&#xff1a;算法专栏 C头…

iCloud涨价不用慌!学会使用群晖生态将本地SSD“上云”

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是想使用群晖生态软件&#xff0c;就必须要在服务端安装群晖系统&#xff0c;具体如何安装群晖虚拟机请参考&#xff1a; 1. 安装并配置synology drive1.1 安装群辉drive套件1.2 在局域…

【node】nodemailer配置163、qq等邮件服务指南

上一章 【node】发送邮件及附件简要使用说明 邮箱配置 参数配置参考如下&#xff1a; let transporter nodemailer.createTransport({host: smtp.qq.com,port: 465,secure: true,auth: {user: **********,pass: your-password} });邮箱服务提供商的要求&#xff0c;配置SM…

Python数据挖掘:入门、进阶与实用案例分析——自动售货机销售数据分析与应用

文章目录 摘要01 案例背景02 分析目标03 分析过程04 数据预处理1. 清洗数据1.1 合并订单表并处理缺失值1.2 增加“市”属性1.3 处理订单表中的“商品详情”属性1.4 处理“总金额&#xff08;元&#xff09;”属性 2.属性选择3.属性规约 05 销售数据可视化分析1.销售额和自动售货…

Linux 文件系统逻辑结构图的解释

task_struct进程结构体&#xff0c;表示一个运行的进程。 task_struct中的fs指向fs_struct结构体。fs_struct表示这个进程支持的文件系统。 root指向根目录dentry&#xff0c;dentry中的d_inode指向改进程根目录在存储设备中的inode节点。 pwd指向当前进程所在的目录结构体den…

深入理解Java线程

1. 线程基础知识 1.1 线程和进程 进程&#xff1a;进程是代码在数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的 基本单位。线程&#xff1a;线程是进程的一个执行路径&#xff0c;一个进程中至少有一个线程&#xff0c;进程中的多个 线程共享进程的资源 1…

对地址解析协议ARP进一步探讨

之前在讨论MAC地址和IP地址时&#xff0c;顺便对ARP协议做了初步的总结 &#xff08;计网第三章&#xff08;数据链路层&#xff09;&#xff08;四&#xff09;&#xff08;MAC地址和IP地址、ARP协议、集线器和交换机&#xff09;&#xff09;&#xff0c;但是当时对ARP请求的…

数据库实验一:学生信息管理系统数据库结构搭建和表的创建

实验项目名称&#xff1a;学生信息管理系统数据库结构搭建和表的创建 实验目的与要求实验原理与内容1. 数据库的组织结构2. 数据库的分离和附加3. 数据库表的创建&#xff0c;修改和删除 实验过程与结果1. 根据学生信息管理系统创建相关的数据库2. 数据库表初步设计及实现3. 实…

构建高效问题解答平台:使用Cpolar和Tipas在Ubuntu上搭建专属问答网站

文章目录 前言2.Tipask网站搭建2.1 Tipask网站下载和安装2.2 Tipask网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 4. 公网访问测试5. 结语 前…

kafka安装

win10 来源:https://blog.csdn.net/tianmanchn/article/details/78943147 进入:http://kafka.apache.org/downloads.html点击Scala 2.12 - kafka_2.12-2.1.0.tgz点击http://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.1.0/kafka_2.12-2.1.0.tgz下载后解压缩 &#x1f604;:\…

微信小程序使用阿里巴巴iconfont,报错Failed to load font http://at.alicdn.com/t/..........

介绍 上篇文章&#xff0c;介绍了&#xff0c;在微信小程序里导入并使用阿里巴巴iconfont图标&#xff1b;但是在页面里使用后&#xff0c;可以看到后台日志有打印错误信息&#xff0c;具体报错如下&#xff1a; 分析 报这个错&#xff0c;是因为项目里使用了 iconfont字体…

主机jvisualvm连接到tomcat服务器查看jvm状态

​使用JMX方式连接到tomcat&#xff0c;连接后能够查看前边的部分内容&#xff0c;但是不能查看Visual GC&#xff0c;显示不受此JVM支持&#xff0c; 对了&#xff0c;要显示Visual GC&#xff0c;首先要安装visualvm工具&#xff0c;具体安装方式就是根据自己的jdk版本下载…

基于RuoYi-Flowable-Plus的若依ruoyi-nbcio支持自定义业务表单流程(二)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 之前讲到了流程保存的时候还要看是否是自定义业务流程应用类型&#xff0c;若是保存的时候不再检查是否有关…

AWS香港Web3方案日,防御云安全实践案例受关注

9月26日&#xff0c;AWS合作伙伴之Web3解决方案日在香港举办。来自人工智能、Web3等领域的创业公司、技术专家、风险投资商&#xff0c;就元宇宙时代未来发展进行了深入交流。现场展示了顶象防御云在金融与Web3领域的安全实践案例。 Web3为互联网体系架构的一个整体演进和升级&…