【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.rhkb.cn/news/159494.html

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

相关文章

050:mapboxGL加载geojson数据,同时包含点、多边形的处理示例

第050个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载geojson数据,既显示点又显示多边形。这个示例是显示了一种处理方式,通过过滤的方式将数据分离化,点和多边形通过两个不同的图层来加载表示。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实…

2018-2019 ACM-ICPC, Asia Nanjing Regional Contest G. Pyramid(组合数学 计数)

题目 t(t<1e6)组样例&#xff0c;每次给定一个n(n<1e9)&#xff0c;统计边长为n的上述三角形的等边三角形个数 其中等边三角形的三个顶点&#xff0c;可以在所有黑色三角形&白色三角形的顶点中任取&#xff0c; 答案对1e97取模 思路来源 申老师 & oeis A0003…

本地生活将成快手新的营收增长点

监制 | 何玺 排版 | 叶媛 快手本地生活开始强化B端市场。 据了解&#xff0c;快手 “本地商家”APP已经正式上线。这是快手为本地生活商家推出的独立工作平台&#xff0c;有助于商家提升经营效率。 新APP的上线&#xff0c;标志着快手本地生活业务布局&#xff0c;正从过去侧…

深入理解Kafka分区副本机制

1. Kafka集群 Kafka 使用 Zookeeper 来维护集群成员 (brokers) 的信息。每个 broker 都有一个唯一标识 broker.id&#xff0c;用于标识自己在集群中的身份&#xff0c;可以在配置文件 server.properties 中进行配置&#xff0c;或者由程序自动生成。下面是 Kafka brokers 集群自…

TLS/SSL 详解

目录 基础理论入门HTTPS对称加密非对称加密证书TLS握手过程握手总结 TLS 定义(记录层/握手层)HTTPS HTTP over TLS加密记录层分片 (Fragmentation)记录压缩和解压缩 (Record compression and decompression)空或标准流加密 (Null or standard stream cipher)CBC 块加密 (分组加…

VS2022新建项目时没有ASP.NET Web应用程序 (.NET Framework)

问题&#xff1a;如图&#xff0c;VS2022新建项目时没有“ASP.NET Web应用程序 &#xff08;.NET Framework&#xff09;”的选项解决方法&#xff1a;点击跳转至修改安装选项界面选择安装该项即可&#xff1a;

k8s-13 存储之secret

Secret 对象类型用来保存敏感信息&#xff0c;例如密码、OAuth 令牌和 ssh key。 敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活 。 Pod 可以用两种方式使用 secret:作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里 当 kubelet 为 pod 拉…

python:从Excel或者CSV中读取因变量与多个自变量,用于训练机器学习回归模型,并输出预测结果

作者:CSDN @ _养乐多_ 本文详细记录了从Excel读取用于训练机器学习模型的数据,包括独立变量和因变量数据,以供用于机器学习模型的训练。这些机器学习模型包括但不限于随机森林回归模型(RF)和支持向量机回归模型(SVM)。随后,我们将测试数据集应用于这些模型,进行预测和…

[开源]基于Vue+ElementUI+G2Plot+Echarts的仪表盘设计器

一、开源项目简介 基于SpringBoot、MyBatisPlus、ElementUI、G2Plot、Echarts等技术栈的仪表盘设计器&#xff0c;具备仪表盘目录管理、仪表盘设计、仪表盘预览能力&#xff0c;支持MySQL、Oracle、PostgreSQL、MSSQL、JSON等数据集接入&#xff0c;对于复杂数据处理还可以使用…

彩虹易支付 9.27 最新版加订单查询 sy 更新版

彩虹易支付 9.27 最新版加订单查询 sy 更新版 修复客服 2023/09/25&#xff1a; 1. 新增支付宝红包支付插件 2. 新增支付宝 APP 支付转 H5 支付 3. 更新了几个支付插件 安装教程&#xff1a; 环境&#xff1a;php7.2 上传后访问域名进行安装即可 源码下载&#xff1a;ht…

KdMapper扩展实现之SOKNO S.R.L(speedfan.sys)

1.背景 KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动&#xff0c;本文是利用其它漏洞&#xff08;参考《【转载】利用签名驱动漏洞加载未签名驱动》&#xff09;做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。 2.驱动信息 驱动名称spee…

POI报表的高级应用

POI报表的高级应用 掌握基于模板打印的POI报表导出理解自定义工具类的执行流程 熟练使用SXSSFWorkbook完成百万数据报表打印理解基于事件驱动的POI报表导入 模板打印 概述 自定义生成Excel报表文件还是有很多不尽如意的地方&#xff0c;特别是针对复杂报表头&#xff0c;单…

macbook电脑删除app怎么才能彻底清理?

macBook是苹果公司推出的一款笔记本电脑&#xff0c;它的操作系统是macOS。在macBook上安装的app可能会占用大量的存储空间&#xff0c;因此&#xff0c;当我们不再需要某个app时&#xff0c;需要将其彻底删除。macbook删除app&#xff0c;怎么才能彻底呢&#xff1f;本文将给大…

c#设计模式-行为型模式 之 备忘录模式

&#x1f680;简介 备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为型设计模式&#xff0c;它保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象。所谓备忘录模式就是在不破坏封装的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象…

测试中Android与IOS分别关注的点

目录 1、自身不同点 2、测试注重点 3、其他测试点 主要从本身系统的不同点、系统造成的不同点、和注意的测试点做总结 1、自身不同点 研发商&#xff1a;Adroid是google公司做的手机系统&#xff0c;IOS是苹果公司做的手机系统   开源程度&#xff1a;Android是开源的&a…

06-React的路由

06-React的路由 1.相关理解 1).SPA的理解 单页Web应用&#xff08;single page web application&#xff0c;SPA&#xff09;。整个应用只有一个完整的页面。点击页面中的链接不会刷新页面&#xff0c;只会做页面的局部更新。数据都需要通过ajax请求获取, 并在前端异步展现。…

在pycharm中运行js文件,附加node.js下载步骤

文章目录 一、前言二、node.js安装和配置(如果之前就安装好了可以直接跳过)1、进入官网下载安装包2、在本地安装node.js3、环境配置4、验证是否安装成功5、修改下载位置(默认是在c盘&#xff0c;这个根据个人需求)6、设置默认模块包7、测试一下是否修改成功(要进入管理员模式的…

Qt 布局(QSplitter 类QDockWidget 类) 总结

一、QSplitter 类(窗口分割) QSplitter类是一个Qt框架提供的基础窗口控件类&#xff0c;用于分割窗口&#xff0c;使得用户可以通过拖动分隔条来调节子窗口的大小。QSplitter在用户界面设计中非常常见&#xff0c;经常用于划分窗口区域&#xff0c;使得程序可以同时显示多个子…

【算法|动态规划No.20】leetcode416. 分割等和子集

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

软件开源快速开发框架:降本增效,助力流程化办公!

随着时代的进步和社会的发展&#xff0c;应用软件开源快速开发框架的优势特点&#xff0c;可以让不少客户朋友顺利实现流程化办公&#xff0c;朝着数字化方向迈进。流辰信息是专业研发低代码技术平台的服务商&#xff0c;一直在低代码平台领域深耕细作&#xff0c;努力钻研&…