Elasticsearch(十四)搜索---搜索匹配功能⑤--全文搜索

一、前言

不同于之前的term。terms等结构化查询,全文搜索首先对查询词进行分析,然后根据查询词的分词结果构建查询。这里所说的全文指的是文本类型数据(text类型),默认的数据形式是人类的自然语言,如对话内容、图书名称、商品介绍和酒店名称等。结构化搜索关注的是数据是否匹配,全文搜索关注的是匹配程度;结构化搜索一般用于精确匹配,而全文搜索用于部分匹配。本章将详细介绍使用最多的全文搜索。

二、match查询

match查询是全文搜索的主要代表。对于最基本的match搜索来说,只要分词中的一个或者多个在文档中存在即可。例如搜索“京盛酒店”,查询词先被分词器切分为“京”“盛”“酒”“店”,因此,只要文档中包含这4个字中的任何一个字,都会被搜索到。
您可能会有疑问,为什么“京盛酒店被切分为4个字而不是“京盛”“酒店”两个词呢?这是因为在默认情况下,match查询使用的是标准分词器。该分词器比较适用于英文,如果是中文则按照字进行切分,因此默认的分词器不适合做中文搜索,在后面的章节中将介绍如何安装和使用中文分词器。
以下DSL示例为按照标题搜索“京盛酒店”:

POST /hotel/_search
{"query": {"match": {   //匹配title字段为"金都酒店"的文档"title":  "京盛酒店"}}
}

或者按照如下形式搜索:

POST /hotel/_search
{"query": {"match": {"title": {"query": "京盛酒店"}}}
}

搜索结果如下:

{..."hits" : {"total" : {"value" : 3,"relation" : "eq"},"max_score" : 1.3428942,"hits" : [{"_index" : "hotel","_type" : "_doc","_id" : "002","_score" : 1.3428942,"_source" : {"title" : "京盛酒店","city" : "北京","price" : "337.00","create_time" : "2020-07-29 13:00:00","amenities" : "充电停车场/可升降停车场","full_room" : false,"location" : {"lat" : 39.911543,"lon" : 116.403},"praise" : 60}},{"_index" : "hotel","_type" : "_doc","_id" : "30","_score" : 1.2387041,"_source" : {"title" : "京盛酒小店","city" : "上海","price" : "300.00","create_time" : "2022-01-29 22:52:00","amenities" : "露天游泳池,普通/充电停车场","full_room" : false,"praise" : 2000}},{"_index" : "hotel","_type" : "_doc","_id" : "27","_score" : 0.5495611,"_source" : {"title" : "盛况精选酒店","city" : "南昌","price" : "900.00","create_time" : "2022-07-29 22:50:00","amenities" : "露天游泳池,普通/充电停车场","full_room" : false,"location" : {"lat" : 56.918229,"lon" : 126.422011},"praise" : 200}}]}
}

从结果中可以看到,匹配度最高的文档是002,该酒店的名称和查询词相同,得分为1.3428942;次之的文档是30,因为该酒店名称中包含“京”“盛”“酒”“店”。但是想比前一个文档多了一个“小”字,所以部分匹配。再次之的文档是27,它只有“盛”“酒”“店”三个字和查询词部分匹配,因此排在最后。
假设用户搜索名称中同时包含“京”和“盛”的酒店,显然之前最后一个文档27就不是用户想要命中的文档。那么在ES中,match搜索可以设置operator参数,该参数决定文档按照分词后的词集合进行“与”还是“或”匹配。在默认情况下,该参数的值为“或”关系,即operator的值为or,这也解释了搜索结果中包含部分匹配的文档。如果希望各个词之间的匹配结果是“与”关系,则可以设置operator参数的值为and。
下面的请求示例设置查询词之间的匹配结果为“与”关系:

POST /hotel/_search
{"query": {"match": {"title": {"query": "京盛酒店","operator": "and"}}}
}

搜索结果如下:

{..."hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 1.3428942,"hits" : [{"_index" : "hotel","_type" : "_doc","_id" : "002","_score" : 1.3428942,"_source" : {"title" : "京盛酒店","city" : "北京","price" : "337.00","create_time" : "2020-07-29 13:00:00","amenities" : "充电停车场/可升降停车场","full_room" : false,"location" : {"lat" : 39.911543,"lon" : 116.403},"praise" : 60}},{"_index" : "hotel","_type" : "_doc","_id" : "30","_score" : 1.2387041,"_source" : {"title" : "京盛酒小店","city" : "上海","price" : "300.00","create_time" : "2022-01-29 22:52:00","amenities" : "露天游泳池,普通/充电停车场","full_room" : false,"praise" : 2000}}]}
}

有时搜索多个关键字,关键词和文档在某一个比例上匹配即可,如果使用“与”操作过于严苛,如果使用“或”操作又过于宽松。这时可以采用minimum_should_match参数,该参数叫作最小匹配参数,其值为一个数值,意义为可以匹配上的词的个数.在一般情况下将其设置为一个百分数,因为在真实场景中并不能精确控制具体的匹配数量。以下示例设置最小匹配为80%的文档:

POST /hotel/_search
{"query": {"match": {"title": {"query": "京盛酒店","operator": "or","minimum_should_match": "80%"    //设置最小匹配度为80%}}}
}

这样的话就需要满足最后命中的文档字数占查询条件中“京盛酒店”的80%(向下取整),例如这里4*80%,其实查询结果只需要有条件中任意三个字符即可。
在Java客户端上可以使用QueryBuilders.matchQuery()方法构建match请求,分别给该方法传入字段名称和查询值即可进行match查询。以下代码展示了match请求的使用逻辑:
service层

	public List<Hotel> matchQuery(HotelDocRequest hotelDocRequest) throws IOException {//新建搜索请求String indexName = getNotNullIndexName(hotelDocRequest);SearchRequest searchRequest = new SearchRequest(indexName);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();//查询title且查询值之间关系是or,并且最小匹配参数为80%MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("title", hotelDocRequest.getTitle()).operator(Operator.OR).minimumShouldMatch("80%");searchSourceBuilder.query(matchQueryBuilder);searchRequest.source(searchSourceBuilder);return getQueryResult(searchRequest);}

controller层

	@PostMapping("/query/match")public FoundationResponse<List<Hotel>> matchQuery(@RequestBody HotelDocRequest hotelDocRequest) {try {List<Hotel> hotelList = esQueryService.matchQuery(hotelDocRequest);if (CollUtil.isNotEmpty(hotelList)) {return FoundationResponse.success(hotelList);} else {return FoundationResponse.error(100,"no data");}} catch (IOException e) {log.warn("搜索发生异常,原因为:{}", e.getMessage());return FoundationResponse.error(100, e.getMessage());} catch (Exception e) {log.error("服务发生异常,原因为:{}", e.getMessage());return FoundationResponse.error(100, e.getMessage());}}

postman调用截图
在这里插入图片描述

三、multi_match查询

有时用户需要在多个字段中查询关键词,除了使用布尔查询封装多个match查询之外,可替代的方案是使用multi_match。可以在multi_match的query子句中组织数据匹配规则,并在fields子句中指定需要搜索的字段列表。
下面的示例在title和amenities两个字段中同时搜索“假日”关键词:

POST /hotel/_search
{"query": {"multi_match": {"query": "假日","fields": ["amenities","title"]}}
}

搜索结果如下:

{"took" : 14,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 4,"relation" : "eq"},"max_score" : 4.2939954,"hits" : [{"_index" : "hotel","_type" : "_doc","_id" : "28","_score" : 4.2939954,"_source" : {"title" : "京盛假日酒店","city" : "上海","price" : "600.00","create_time" : "2021-04-29 22:52:00","amenities" : "露天游泳池,普通/充电停车场","full_room" : false,"praise" : 200}},{"_index" : "hotel","_type" : "_doc","_id" : "003","_score" : 1.9696801,"_source" : {"title" : "文雅文化酒店","city" : "天津","price" : "260.00","create_time" : "2021-02-27 22:00:00","amenities" : "提供假日party,免费早餐,浴池,充电停车场","full_room" : true,"location" : {"lat" : 39.186555,"lon" : 117.162767},"praise" : 30}},{"_index" : "hotel","_type" : "_doc","_id" : "29","_score" : 1.9163029,"_source" : {"title" : "京盛欣欣酒店","city" : "上海","price" : "700.00","create_time" : "2022-01-29 22:52:00","amenities" : "提供假日party,露天游泳池,普通/充电停车场","full_room" : false,"praise" : 200}},{"_index" : "hotel","_type" : "_doc","_id" : "004","_score" : 1.6876338,"_source" : {"title" : "京盛集团酒店","city" : "上海","price" : "800.00","create_time" : "2021-05-29 21:35:00","amenities" : "浴池(假日需预订),室内游泳池,普通停车场/充电停车场","full_room" : true,"location" : {"lat" : 36.940243,"lon" : 120.394},"praise" : 100}}]}
}

根据结果可以看到,命中的文档要么在title中包含“假日”关键词,要么在amenities字段中包含“假日”关键词。
且之前在Match搜索讲到的operator,minimum_should_match等参数在multi_match搜索中同样适用
在Java客户端上可以使用QueryBuilders.multiMatchQuery()方法或者直接new MultiMatchQueryBuilder()构建multi_match请求
可以看到,我们构造MultiMatchQueryBuilder,除了查询值,字段它接收的是一个可变长String数组
在这里插入图片描述
所以我们可以在传参hotelDocRequest加两个参数,一个是multiQueryValue代表要查询的值,另一个是multiQueryPropertyNames代表想要在哪些字段查询
分别给该方法传入查询值和多个字段名称即可进行multi_match查询。以下代码展示了multi_match请求的使用逻辑:
Service层
由于上面讲到构造MultiMatchQueryBuilder接收的是可变长String数组,所以我们要对传参的List通过list.stream().toArray(String[]::new);转化为String可变长数组(String…等价于String[])。

	public List<Hotel> multiMatchQuery(HotelDocRequest hotelDocRequest) throws IOException {//新建搜索请求String indexName = getNotNullIndexName(hotelDocRequest);SearchRequest searchRequest = new SearchRequest(indexName);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();MultiMatchQueryBuilder multiMatchQueryBuilder = new MultiMatchQueryBuilder(hotelDocRequest.getMultiQueryValue(), hotelDocRequest.getMultiQueryPropertyNames().toArray(new String[0]));searchSourceBuilder.query(multiMatchQueryBuilder);searchRequest.source(searchSourceBuilder);return getQueryResult(searchRequest);}

controller层

	@PostMapping("/query/multiMatch")public FoundationResponse<List<Hotel>> multiMatchQuery(@RequestBody HotelDocRequest hotelDocRequest) {try {List<Hotel> hotelList = esQueryService.multiMatchQuery(hotelDocRequest);if (CollUtil.isNotEmpty(hotelList)) {return FoundationResponse.success(hotelList);} else {return FoundationResponse.error(100,"no data");}} catch (IOException e) {log.warn("搜索发生异常,原因为:{}", e.getMessage());return FoundationResponse.error(100, e.getMessage());} catch (Exception e) {log.error("服务发生异常,原因为:{}", e.getMessage());return FoundationResponse.error(100, e.getMessage());}}

postman运行截图
在这里插入图片描述

四、match_phrase查询

match_phrase用于匹配短语,与match查询不同的是,match_phrase用于搜索确切的短语或临近的词语。假设在酒店标题中搜索“京盛酒店”,希望酒店标题中的“京盛酒店”四字完全按照搜索词的顺序并且紧邻,此时就需要使用match_phrase查询:

POST /hotel/_search
{"query": {"match_phrase": {"title": {"query": "京盛酒店"}}}
}

结果如下:

{..."hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.3428942,"hits" : [{"_index" : "hotel","_type" : "_doc","_id" : "002","_score" : 1.3428942,"_source" : {"title" : "京盛酒店","city" : "北京","price" : "337.00","create_time" : "2020-07-29 13:00:00","amenities" : "充电停车场/可升降停车场","full_room" : false,"location" : {"lat" : 39.911543,"lon" : 116.403},"praise" : 60}}]}
}

根据上述结果可知,使用match_phrase查询后,只有文档002命中,而类似之前的“京盛集团酒店”等类似文档没有被命中,这是为什么呢?
我们知道,在默认标准分词器的情况下,文档002的title字段被切分为“京”“盛”“酒”“店”,其中这些分词后的文档下标“京”代表0,盛”代表1,“酒”代表2,“店”代表3,而对于match_phrase查询,在不去设置下标移动步长的情况下这些分词文档想要移动到理想位置(查询词的位置,这里就是京盛酒店)的步数默认就是0,而可以发现,我们命中的文档002“京盛酒店”,这个文档下标其实就已经是理想位置了,不需要额外移动,相当于步长就是0,所以能够命中。而对于“京盛集团酒店”,分词后“盛”想要移动到“酒”这个下标,需要移动2次,所以步长是2,不符合默认的步长,所以无法命中。
那么如果需要“京盛集团酒店”也能够被命中,则可以设置match_phrase查询的slop参数,它用来调节匹配词之间的距离阈值,即上面说的步长,下面的DSL将slop设置为2

POST /hotel/_search
{"query": {"match_phrase": {"title": {"query": "京盛酒店","slop":2}}}
}

可以看到这样就能命中“京盛集团酒店”了
在这里插入图片描述
在Java客户端上可以使用QueryBuilders.matchPhraseQuery()方法构建match_phrase请求,分别给该方法传入查询字段和值即可运行multi_match查询。这一点和match搜索很像。以下代码展示了match_phrase请求的使用逻辑:
Service层

	public List<Hotel> matchPhraseQuery(HotelDocRequest hotelDocRequest) throws IOException {//新建搜索请求String indexName = getNotNullIndexName(hotelDocRequest);SearchRequest searchRequest = new SearchRequest(indexName);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();//构造MatchPhraseQueryBuilder且设置步长为2MatchPhraseQueryBuilder matchPhraseQueryBuilder = new MatchPhraseQueryBuilder("title", hotelDocRequest.getTitle()).slop(2);searchSourceBuilder.query(matchPhraseQueryBuilder);searchRequest.source(searchSourceBuilder);return getQueryResult(searchRequest);}

Controller层

	@PostMapping("/query/matchPhrase")public FoundationResponse<List<Hotel>> matchPhraseQuery(@RequestBody HotelDocRequest hotelDocRequest) {try {List<Hotel> hotelList = esQueryService.matchPhraseQuery(hotelDocRequest);if (CollUtil.isNotEmpty(hotelList)) {return FoundationResponse.success(hotelList);} else {return FoundationResponse.error(100,"no data");}} catch (IOException e) {log.warn("搜索发生异常,原因为:{}", e.getMessage());return FoundationResponse.error(100, e.getMessage());} catch (Exception e) {log.error("服务发生异常,原因为:{}", e.getMessage());return FoundationResponse.error(100, e.getMessage());}}

Postman运行截图:
在这里插入图片描述

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

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

相关文章

VictoriaLogs:一款超低占用的 ElasticSearch 替代方案

image.png 背景 前段时间我们想实现 Pulsar 消息的追踪流程&#xff0c;追踪实现的效果图如下&#xff1a; 实现其实比较简单&#xff0c;其中最重要的就是如何存储消息。 消息的读取我们是通过 Pulsar 自带的 BrokerInterceptor 实现的&#xff0c;对这个感兴趣的朋友后面会单…

用大白话来讲讲多线程的知识架构

感觉多线程的知识又多又杂&#xff0c;自从接触java&#xff0c;就在一遍一遍捋脉络和深入学习。现在将这次的学习成果展示如下。 什么是多线程&#xff1f; 操作系统运行一个程序&#xff0c;就是一个线程。同时运行多个程序&#xff0c;就是多线程。即在同一时间&#xff0…

python爬虫的js逆向入门到进阶教程文章分享汇总~持续更新

目录 一、内容介绍二 、专栏内容-持续更新1、JS逆向入门2、Js逆向进阶3、爬虫基础知识4、工具与安装5、漫星内容分享 三、星球使用四、b站up主视频推荐 一、内容介绍 二 、专栏内容-持续更新 1、JS逆向入门 2023-08-25》11.常见加密>xx音乐RSA加密 https://articles.zsxq.c…

项目进度管理(4-1)关键链法

1 关键链法产生的背景 关键链法&#xff08;Critical Chain Method&#xff0c;CCM&#xff09;起源于20世纪80年代&#xff0c;是由Eliyahu M. Goldratt在他的著作《关键链》&#xff08;"Critical Chain"&#xff09;中首次提出和阐述的。Eliyahu M. Goldratt是以…

玩转git第7章节,本地git的用户名和密码的修改

一 本地git的用户名和密码 1.1 本地用户名和密码修改 1.本地用户名修改 2.凭据管理 3.进行修改密码 1.2 代码提交操作

ChatGPT在医疗系统的应用探索动态

注意&#xff1a;本信息仅供参考&#xff0c;发布该内容旨在传递更多信息的目的&#xff0c;并不意味着赞同其观点或证实其说法。 生成式人工智能&#xff0c;如OpenAI开发的ChatGPT&#xff0c;被认为是可以颠覆医疗行业的工具。尽管该技术刚刚起步&#xff0c;但已有许多医…

接口测试总结分享(http与rpc)

接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 一、了解一下HTTP与RPC 1. HTTP&#xff08;H…

SpringCloud超详细教程

1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff1a;将业务的所有…

C语言(第三十二天)

1. 递归是什么&#xff1f; 递归是学习C语言函数绕不开的一个话题&#xff0c;那什么是递归呢&#xff1f; 递归其实是一种解决问题的方法&#xff0c;在C语言中&#xff0c;递归就是函数自己调用自己。 写一个史上最简单的C语言递归代码&#xff1a; #include <stdio.h>…

二、前端监控之方案调研

前端监控体系 一个完整的前端监控体系包括了日志采集、日志上报、日志存储、日志切分&计算、数据分析、告警等流程。 对于一名前端开发工程师来说&#xff0c;也就意味着工作不再局限于前端业务的开发工作&#xff0c;需要有Nginx服务运维能力、实时/离线分析能力、Node应…

常见的时序数据库

1.概念 时序数据库全称为时间序列数据库。时间序列数据库指主要用于处理带时间标签&#xff08;按照时间的顺序变化&#xff0c;即时间序列化&#xff09;的数据&#xff0c;带时间标签的数据也称为时间序列数据。 时间序列数据主要由电力行业、化工行业、气象行业、地理信息…

Redis通信协议

文章目录 Redis通信协议RESP协议数据类型 模拟Redis客户端 Redis通信协议 RESP协议 Redis是一个CS架构的软件&#xff0c;通信一般分为两步(不包含pipeline和PubSub)&#xff1a; 客户端(client)向服务端(server)发送一条命令。服务器解析并执行命令&#xff0c;返回响应结果…

远程调试环境配置

远程调试环境配置 前期准备ssh连接 前期准备 安装vscode中的两个扩展包php-debug和remote-ssh 然后安装与PHP版本对应的xdebug 访问xdebug的官方网页&#xff0c;复制自己的phpinfo源码到方框中&#xff0c;再点击Analyse ssh连接 输入&#xff0c;你想要远程连接的主机i…

Unity中的数学基础——贝塞尔曲线

一&#xff1a;前言 一条贝塞尔曲线是由一组定义的控制点P0到 Pn&#xff0c;n1为线性&#xff0c;n2为二次......第一个和最后一个控制点称为起点和终点&#xff0c;中间的控制点一般不会位于曲线上 获取两个点之间的点就是通过线性插值&#xff08; Mathf.Lerp&#xff09…

分布式 - 服务器Nginx:一小时入门系列之TCP反向代理和负载均衡

文章目录 1. HTTP反向代理和TCP反向代理2. http 块和 stream 块3. TCP反向代理配置4. TCP 负载均衡 1. HTTP反向代理和TCP反向代理 Nginx可以作为HTTP反向代理和TCP反向代理。 HTTP反向代理是指Nginx作为Web服务器的代理服务器&#xff0c;接收客户端的HTTP请求&#xff0c;然…

计算机竞赛 基于大数据的社交平台数据爬虫舆情分析可视化系统

文章目录 0 前言1 课题背景2 实现效果**实现功能****可视化统计****web模块界面展示**3 LDA模型 4 情感分析方法**预处理**特征提取特征选择分类器选择实验 5 部分核心代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于大数据…

C语言练习4(巩固提升)

C语言练习4 选择题 前言 面对复杂变化的世界&#xff0c;人类社会向何处去&#xff1f;亚洲前途在哪里&#xff1f;我认为&#xff0c;回答这些时代之问&#xff0c;我们要不畏浮云遮望眼&#xff0c;善于拨云见日&#xff0c;把握历史规律&#xff0c;认清世界大势。 选择题 …

新型数据中心网络安全体系研究

当前&#xff0c;新型基础设施建设已上升至国家战略&#xff0c;被赋予了数字化、智能化的新意义。作为算力基础设施&#xff0c;新型数据中心是信息基础设施中的重要内容&#xff0c;是数字政府、数字经济的“信息底座”&#xff0c;是未来引领数字经济发展的关键载体和支柱&a…

java八股文面试[多线程]——sleep wait join yield

sleep和wait有什么区别 sleep 方法和 wait 方法都是用来将线程进入阻塞状态的&#xff0c;并且 sleep 和 wait 方法都可以响应 interrupt 中断&#xff0c;也就是线程在休眠的过程中&#xff0c;如果收到中断信号&#xff0c;都可以进行响应并中断&#xff0c;且都可以抛出 In…

npm常用命令 + 前端常用的包管理工具 以及 npm淘宝镜像配置等

npm常用命令 前端常用的包管理工具 以及 npm淘宝镜像配置等 1. 前言1.1 NodeJs的下载安装1.2 windows上1.3 常用包管理工具 2. npm2.1 npm 的安装2.2 npm初始化包2.3 npm 安装、卸载包2.3.1 非全局安装2.3.1.1 单个包的安装2.3.1.1.1 默认版本安装2.3.1.1.2 指定版本安装 2.3.…