Elasticsearch(十三)搜索---搜索匹配功能④--Constant Score查询、Function Score查询

一、前言

之前我们学习了布尔查询,知道了filter查询只在乎查询条件和文档的匹配程度,但不会根据匹配程度对文档进行打分,而对于must、should这两个布尔查询会对文档进行打分,那如果我想在查询的时候同时不去在乎文档的打分(对搜索结果的排序),只想过滤文本字段是否包含这个词,除了filter查询,我们还会介绍Constant Score查询。相反,如果想干预这个分数,我们会使用Function Score查询,这些都会在后面介绍到。

二、Constant Score查询

如果不想让检索词频率TF(Term Frequency)对搜索结果排序有影响,只想过滤某个文本字段是否包含某个词,可以使用Constant Score将查询语句包装起来。
假设需要查询city字段是否包含关键词“上海”的酒店,则请求的DSL如下:

POST /hotel/_search
{"query": {"constant_score": {    //满足条件即打分为1(默认值是1)"filter": {"term": {   //term查询city中是上海的城市"city": "上海"}}}}
}

查询结果如下:

{..."hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "hotel","_type" : "_doc","_id" : "004","_score" : 1.0,"_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}},{"_index" : "hotel","_type" : "_doc","_id" : "006","_score" : 1.0,"_source" : {"title" : "京盛集团精选酒店","city" : "上海","price" : "500.00","create_time" : "2022-01-29 22:50:00","full_room" : true,"location" : {"lat" : 40.918229,"lon" : 118.422011},"praise" : 20}}]}
}

通过结果可以看到,使用Constant Score搜索时,命中的酒店文档对应的city字段都包含“上海”一词。但是不论该词在文档中出现多少次,这些文档的得分都是一样的1.0.
PS:很多人可能会把constant_score查询中的filter和布尔查询的filter搞混,constant_score中的filter可以把它想象成普通的query,它后面接的就是各种各样的查询子句。如term,terms,exists,bool等等。
比如我想同时使用must查询创建时间大于等于2022-01-29 22:50:00的hotel且不在乎打分,那么可以使用下面的DSL:

POST /hotel/_search
{"query": {"constant_score": {"filter": {"bool": {"must": [{"range": {"create_time": {"gte": "2022-01-29 22:50:00"}}}]}}}}
}

在Constant Score搜索中,参数boost可以控制命中文档的得分,默认值都是1.0,以下为更改boost参数为2.0的例子:

POST /hotel/_search
{"query": {"constant_score": {"boost":2.0,"filter": {"term": {"city": "上海"}}}}
}

查询结果如下:

{..."hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 2.0,"hits" : [{"_index" : "hotel","_type" : "_doc","_id" : "004","_score" : 2.0,"_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}},{"_index" : "hotel","_type" : "_doc","_id" : "006","_score" : 2.0,"_source" : {"title" : "京盛集团精选酒店","city" : "上海","price" : "500.00","create_time" : "2022-01-29 22:50:00","full_room" : true,"location" : {"lat" : 40.918229,"lon" : 118.422011},"praise" : 20}}]}
}

根据搜索结果可以看到,设定Boost值为2.0后,所有的命中的文档得分都为2.0。
然后对于Constant Score的效率问题,我们拿它和上一节讲到的filter查询做一个对比:

  1. Constant Score查询实际上就是一个没有分值函数的查询,它会将所有匹配文档的分值设置为一个常量。这种查询不需要计算每个匹配文档的相关度,所以效率会比普通查询高
  2. 但是Constant Score查询还需要执行查询本身,比如匹配查询条件、过滤文档等步骤。而filter查询仅仅过滤文档,不计算分值,所以整体效率比Constant Score查询更高。
  3. Constant Score查询不会像filter查询那样缓存过滤结果。因为Constant Score查询还需要计算每个匹配文档的分值,而这一步不受过滤结果缓存的影响。
  4. 所以总的来说,在效率方面: filter查询 > Constant Score查询 > 普通查询

在java客户端上构建Constant Score搜索时,可以使用ConstantScoreQueryBuilder类的实例进行构建,它接收一个QueryBuilder参数,即可以接收termQueryBuilder,termsQueryBuilder,boolQueryBuilder等等,和之前的DSL是一样的,那么比如我们查询一个城市是上海或者北京的酒店,代码如下:
Service层,getQueryResult()可以看往期的博客,有具体的方法实现:

	public List<Hotel> constantScore(HotelDocRequest hotelDocRequest) throws IOException {//新建搜索请求String indexName = getNotNullIndexName(hotelDocRequest);SearchRequest searchRequest = new SearchRequest(indexName);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();TermQueryBuilder termQueryBuilder1 = QueryBuilders.termQuery("city", "北京");TermQueryBuilder termQueryBuilder2 = QueryBuilders.termQuery("city", "上海");BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.should(termQueryBuilder1).should(termQueryBuilder2);//构建ConstantScoreBuilderConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(boolQueryBuilder);//设置固定分数2.0constantScoreQueryBuilder.boost(2.0f);searchSourceBuilder.query(constantScoreQueryBuilder);searchRequest.source(searchSourceBuilder);return getQueryResult(searchRequest);}

Controller层:

	@PostMapping("/query/constant_score")public FoundationResponse<List<Hotel>> constantScoreQuery(@RequestBody HotelDocRequest hotelDocRequest) {try {List<Hotel> hotelList = esQueryService.constantScore(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实现:
在这里插入图片描述

三、Function Score查询

当你使用ES进行搜索时,命中的文档默认按照相关度进行排序,有些场景下用户需要干预该“相关度”,此时就可以使用Function Score查询。使用时,用户必须定义一个查询以及一个或多个函数,这些函数为每一个文档计算一个新分数
它允许每个主查询query匹配的文档应用加强函数,以达到改变原始查询评分_score的目的

3.1、function_score 查询模板

function_score 查询模板可以分为两类,分别为单个加强函数的查询和多个加强函数的查询。
单个加强函数的查询模板

{"query": {"function_score": {"query": {.....}, //  主查询,查询完后会有一个 _score 评分"field_value_factor": {...}, //  在 _score 的基础上进行强化评分"boost_mode": "multiply", //  指定用哪种方式结合 _score 和 强化 score"max_boost": 1.5 //  限制强化 score 的最高分,但是不会限制 _score}}
}

多个加强函数的查询模板

{"query": {"function_score": {"query": {.....},"functions": [   //  可以有多个加强函数(或是 filter+加强函数),每一个加强函数会产生一个加强 score{ "field_value_factor": ... },{ "gauss": ... },{ "filter": {...}, "weight": ... }],"score_mode": "sum", //  决定加强 score 们如何整合"boost_mode": "multiply" //  决定最后的 functions 中 score 和 query score 的结合方式}}
}

3.2、function_score 参数

强化 _score 计算的函数
function_score 提供了几种内置加强 _score 计算的函数功能:

  • weight:设置一个简单而不被规范化的权重提升值。

weight 加强函数和 boost 参数比较类似,可以用于任何查询,不过有一点差别是 weight 不会被 Lucene 规范化(normalize)成难以理解的浮点数,而是直接被应用。
例如,当 weight 为 2 时,最终得分为 new_score = 2 * _score

POST /hotel/_search
{"query": {"function_score": {"query": {"term": {"city": {"value": "上海"}}},"weight":2}}
}

输出后可以对比一下不加weight的默认分数,基本分数都翻了2倍

  • field_value_factor:指定文档中某个字段的值结合 _score 改变分数

属性如下:
field指定字段名
factor对字段值进行预处理,乘以(或者加,取决于boost_mode)指定的数值(默认为1)
modifier:将字段值进行加工,有以下的几个选项:

  1. none:不处理
  2. log:计算对数
  3. log1p:先将字段值+1,再计算对数
  4. log2p:先将字段值+2,再计算对数
  5. ln:计算自然对数
  6. ln1p:先将字段值+1,再计算自然对数
  7. ln2p:先将字段值+2,再计算自然对数
  8. square:计算平方
  9. sqrt:计算平方根
  10. reciprocal:计算倒数
{"query": {"function_score": {"query": {.....},"field_value_factor": {"field": "price","modifier": "none","factor": 1.2},"boost_mode": "multiply", "max_boost": 1.5}}
}

调整后的 function 分数公式为,factor * doc['price'].value;如果boos_mode设定为sum,那么分数公式为factor + doc['price'].value

例如我们让最终的分数以price字段进行增强,在原分数基础上*1.2

POST /hotel/_search
{"query": {"function_score": {"query": {"term": {"city": {"value": "上海"}}},"field_value_factor": {"field":"price","factor": 1.2},"boost_mode": "multiply"}}
}

再例如我想对字段值先乘1.2再+1再取对数,那么DSL如下:

POST /hotel/_search
{"query": {"function_score": {"query": {"term": {"city": {"value": "上海"}}},"field_value_factor": {"field":"price","modifier": "ln1p","missing":1.0,"factor": 1.2},"boost_mode": "multiply"}}
}

在这里插入图片描述
function 分数为,ln1p(1.2 * doc['view_cnt'].value)如果指定字段缺失用 missing 对应的值,至于和匹配的相关性分数 _score 如何结合需要下面的 boost_mode 参数来决定

  • random_score使用一致性随机分值计算来对每个用户采用不同的结果排序方式,对相同用户仍然使用相同的排序方式其本质上用的是seed 种子参数,用户相关的 id 与 seed 构造映射关系,就可千人千面的效果,seed 不同排序结果也不同。具体示例如下:
    ①字段值相同,例如通过full_room,由上面查询结果可知,两个结果的full_room相同,此时使用random_score,两个的排序结果仍然是一致的:
POST /hotel/_search
{"query": {"function_score": {"query": {"term": {"city": {"value": "上海"}}},"random_score": {"field":"full_room","seed": 10},"boost_mode": "multiply"}}
}

在这里插入图片描述
如果对price进行随机加强,那么排序就会不一样:

POST /hotel/_search
{"query": {"function_score": {"query": {"term": {"city": {"value": "上海"}}},"random_score": {"field":"price","seed": 10},"boost_mode": "multiply"}}
}

在这里插入图片描述
我们可以调整seed,就会发现排序不一样。

  • 衰减函数(decay function):es 内置了三种衰减函数,分别是 linear、exp 和 gauss;

三种衰减函数的差别只在于衰减曲线的形状,在 DSL 的语法上的用法完全一样;
linear : 线性函数是条直线,一旦直线与横轴0香蕉,所有其他值的评分都是0
exp : 指数函数是先剧烈衰减然后变缓
guass(最常用) : 高斯函数则是钟形的,他的衰减速率是先缓慢,然后变快,最后又放缓
在这里插入图片描述

origin:中心点 或字段可能的最佳值,落在原点 origin 上的文档评分 _score 为满分 1.0 。

scale:衰减率,即一个文档从原点 origin 下落时,评分 _score 改变的速度。(例如,每 £10 欧元或每 100 米)。

decay:从原点 origin 衰减到 scale 所得的评分 _score ,默认值为 0.5 。

offset:以原点 origin 为中心点,为其设置一个非零的偏移量 offset 覆盖一个范围,而不只是单个原点。在范围 -offset <= origin <= +offset 内的所有评分 _score 都是 1.0 。不设置默认是0

POST /hotel/_search
{"query": {"function_score": {"query": {"term": {"city": {"value": "上海"}}},"gauss": {"price": {// 如果不设置offset,offset默认为0  公式 : origin-offset <= value <= origin+offset// 范围在800-0 <= value <= 800+0的文档的评分_score都是满分1.0//而在此范围之外,评分会开始衰减,衰减率由scale值(此处是300)和decay值(此处是0.2)决定// 也就是说,在origin + offset + scale或是origin - offset - scale的点上,得到的分数仅有decay分"origin": "800","scale": "300","decay": 0.2}}, "boost_mode": "multiply"}}
}

在这里插入图片描述
对衰减函数感兴趣的小伙伴可以浏览这篇文章,讲的很详细,尤其是最后对于用户同时对于酒店的地理位置和价格去做一个筛选。

  • script_score:当需求超出以上范围时,可以用自定义脚本完全控制评分计算。

3.3、其它辅助函数

  • boost_mode 参数:决定 query 中的相关性分数和加强的函数分数的结合方式。

multiply默认的配置,两者分数相乘,new_score = _score * boost_score;
sum:两者相加,new_score = _score + boost_score;
min:取两者最小值,new_score = min(_score, boost_score);
max:取两者最大值,new_score = max(_score, boost_score);
replace:用 boost_score 替换 _score 值。有时候我们可以通过replace看具体的函数得分是多少,便于我们排查问题

  • score_mode 参数决定 functions 里面的强化 score 如何结合

function_score 先会执行 score_mode 的设置,即先整合所有的强化计算,再执行 boost_mode 的配置,就是将 query 相关性分数和整合强化分数的结合
multiply:默认的配置,多个强化分数相乘;
sum:多个强化分数相加;
min:取多个强化分数最小值;
max:取多个强化分数最大值;
avg:取多个强化分数平均值;
first:使用首个函数的结果作为最终结果。

  • max_boost:限制加强函数的最大效果,就是限制加强 score 最大能多少,但要注意不会限制 old_score。

如果加强 score 超过了 max_boost 限制的值,会把加强 score 的值设成 max_boost 的值
假设加强 score 是5,而 max_boost 是2,因为加强 score 超出了 max_boost 的限制,所以 max_boost 就会把加强 score 改为2。简单的说,就是 final_score = min(整合后的 score, max_boost)。

3.4、java实现

funtion_score的参数我们可以通过ScoreFunctionBuilders.xxx构筑
在这里插入图片描述
Service层实现:

	public List<Hotel> functionScoreScore(HotelDocRequest hotelDocRequest) throws IOException {//新建搜索请求String indexName = getNotNullIndexName(hotelDocRequest);SearchRequest searchRequest = new SearchRequest(indexName);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("city", "上海");//构建FunctionScoreBuilder,比如这里构筑高斯函数(衰减函数)GaussDecayFunctionBuilder gaussDecayFunctionBuilder = ScoreFunctionBuilders.gaussDecayFunction(hotelDocRequest.getPropertiesName(), 800, 200, 0, 0.2);//构建Function Score查询FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(termQueryBuilder, gaussDecayFunctionBuilder).boostMode(CombineFunction.MULTIPLY);searchSourceBuilder.query(functionScoreQueryBuilder);searchRequest.source(searchSourceBuilder);return getQueryResult(searchRequest);}

controller层实现:

	@PostMapping("/query/function_score")public FoundationResponse<List<Hotel>> functionScoreQuery(@RequestBody HotelDocRequest hotelDocRequest) {try {List<Hotel> hotelList = esQueryService.functionScoreScore(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.mzph.cn/news/55350.shtml

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

相关文章

Redis(缓存预热,缓存雪崩,缓存击穿,缓存穿透)

目录 一、缓存预热 二、缓存雪崩 三、缓存击穿 四、缓存穿透 一、缓存预热 开过车的都知道&#xff0c;冬天的时候启动我们的小汽车之后不要直接驾驶&#xff0c;先让车子发动机预热一段时间再启动。缓存预热是一样的道理。 缓存预热就是系统启动前&#xff0c;提前将相关的…

C语言基础之——指针(下)

前言&#xff1a;本篇文章将继续讲解有关指针的剩余基础知识。 学无止境&#xff0c;一起加油叭&#xff01;&#xff01; 目录 一.指针运算 1.指针 - 整数 2.指针的关系运算 3.指针 - 指针 二.指针与数组 三.二级指针 四.指针数组 总结 一.指针运算 指针运算包括以下三…

【TI毫米波雷达笔记】UART串口外设配置及驱动(以IWR6843AOP为例)

【TI毫米波雷达笔记】UART串口外设初始化配置及驱动&#xff08;以IWR6843AOP为例&#xff09; 最基本的工程建立好以后 需要给SOC进行初始化配置 int main (void) {//刷一下内存memset ((void *)L3_RAM_Buf, 0, sizeof(L3_RAM_Buf));int32_t errCode; //存放SOC初…

c#设计模式-创建型模式 之 原型模式

概述 原型模式是一种创建型设计模式&#xff0c;它允许你复制已有对象&#xff0c;而无需使代码依赖它们所属的类。新的对象可以通过原型模式对已有对象进行复制来获得&#xff0c;而不是每次都重新创建。 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a;规定了具…

AliOS-Things引入

目录 一、简介 1.1 硬件抽象层 1.2 AliOS-Things内核 rhino ​编辑 1.3 AliOS-Things组件 二、如何进行AliOS-Things开发 三、安装环境 安装python pip git 修改pip镜像源 安装aos-cube 一、简介 AliOS-Things是阿里巴巴公司推出的致力于搭建云端一体化LoT软件。AliOS-…

【python】python智能停车场数据分析(代码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

azure data studio SQL扩展插件开发笔记

node.js环境下拉取脚手架 npm install -g yo generator-azuredatastudio yo azuredatastudio 改代码 运行 调试扩展&#xff0c;在visual studio code中安装插件即可 然后visual studio code打开进行修改运行即可 image.png 运行后自动打开auzre data studio了&#xff0c; 下面…

spring整合mybatis教程(详细易懂)

一、引言 1、Spring整合MyBatis的目的是&#xff1f; 将两个框架结合起来&#xff0c;以实现更好的开发体验和效果。Spring提供了一种轻量级的容器和依赖注入的机制&#xff0c;可以简化应用程序的配置和管理。而MyBatis是一个优秀的持久层框架&#xff0c;可以方便地进行数据…

C# .aspx网页获取RFID读卡器HTTP协议提交的访问文件Request获得卡号、机号,Response回应驱动读卡器显示响声

本示例使用的设备&#xff1a;RFID网络WIFI无线TCP/UDP/HTTP可编程二次开发读卡器POE供电语音-淘宝网 (taobao.com) 服务端代码&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.…

快速理解 X server, DISPLAY 与 X11 Forwarding

​ X server X server是X Window System &#xff08;简称X11或者X&#xff09;系统中的显示服务器&#xff08;display server&#xff09;&#xff0c;用于监听X client发送来的图形界面显示请求&#xff0c;并且将图形界面绘制并显示在屏幕&#xff08;screen&#xff09;…

Mybatis查询数据

上一篇我们介绍了在pom文件中引入mybatis依赖&#xff0c;配置了mybatis配置文件&#xff0c;通过读取配置文件创建了会话工厂&#xff0c;使用会话工厂创建会话获取连接对象读取到了数据库的基本信息。 如果您需要对上面的内容进行了解&#xff0c;可以参考Mybatis引入与使用…

再见 Xshell替代工具Tabby

替代Xshell 之前经常使用Xshell来操作Linux虚拟机&#xff0c;基本上是够用了。但是Xshell免费使用只供非商业用途&#xff0c;而且如果你想用FTP来进行文件传输的话&#xff0c;还需单独下载Xftp。 无意中发现了另一款开源的终端工具Tabby&#xff0c;它直接集成了SFTP功能&…

十几款拿来就能用的炫酷表白代码

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 表白代码 1、坐我女朋友好吗&#xff0c;不同意就关机.vbs2、坐我女朋友好吗&…

基于静电放电算法优化的BP神经网络(预测应用) - 附代码

基于静电放电算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于静电放电算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.静电放电优化BP神经网络2.1 BP神经网络参数设置2.2 静电放电算法应用 4.测试结果&#xff1a;5…

【mindspore学习】环境配置

本次实验搭配的环境是 CUDA 11.6 CUDNN v8.9.4 TensorRT-8.4.1.5 mindspore 2.1.0。 1、配置 Nvidia 显卡驱动 如果原来的主机已经安装了 nvidia 驱动&#xff0c;为避免版本的冲突&#xff0c;建议先清除掉旧的 nvidia驱动 sudo apt-get --purge remove nvidia* sudo apt…

信息化发展2

信息系统生命周期 1 、软件的生命周期通常包括&#xff1a;可行性分析与项目开发计划、需求分析、概要设计、详细设计、编码、测试、维护等阶段。 2 、信息系统的生命周期可以简化为&#xff1a;系统规划&#xff08;可行性分析与项目开发计划&#xff09;&#xff0c;系统分析…

Unity 之 transform.rotate() 实现旋转

文章目录 详细介绍默认情况下&#xff0c;以局部坐标 详细介绍 在Unity中&#xff0c;Transform.Rotate() 是一个用于在物体上进行旋转的函数。它可以用来在局部坐标系下对物体进行旋转&#xff0c;也可以在世界坐标系下进行旋转。下面是关于 Transform.Rotate() 的详细介绍&a…

2. 使用IDEA创建Spring Boot Hello项目并管理依赖——Maven入门指南

前言&#xff1a;本文将介绍如何使用IDEA创建一个Spring Boot Hello项目&#xff0c;并通过Maven来管理项目的依赖。我们从项目的创建到代码的编写&#xff0c;再到项目的构建和运行&#xff0c;一步步演示了整个过程。 &#x1f680; 作者简介&#xff1a;作为某云服务提供商的…

DataFun:推荐系统峰会

工程架构与训练推理 TFDE 多场景多目标融合 企业知识推荐系统 ATA内部社区 房产推荐场景的算法实践

chatGPT界面

效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html> <head><title>复选框样式示例</title> </head> <style>* {padding:0;margin: 0;}.chatpdf{display: flex;height: 100vh;flex-direction: row;}.chatpdf .pannel{widt…