《Elastic Stack 实战手册》——四、应用实践——4.1 企业搜索应用场景 ——4.1.6.优化Elasticsearch中的打分(_score)(中) https://developer.aliyun.com/article/1226240
Boolean query
Boolean query 是较为常用的 bool 查询,可以将多个查询语句组合到一起,bool 查询有四个类型参数,must:表示文档必须满足的条件;filter:同 must 一样,文档必须满足条件,但是忽略相关性打分,查询条件不会对 _score 有影响;should:表示文档如果满足条件会加分,用 minimum_should_match 控制 should 条件中最少满足的个数;must_not:表示文档不能满足的条件,如果文档满足这里面的条件,则不会被搜出来。对于我们的 DSL,可能条件有很多,但是我们只要将需要相关性打分的条件放到 must 中,其他的放到 filter 中,这样既不会影响 _score 的计算,同时 Elasticsearch 对 filter 中部分条件语句有缓存,可以提升我们的查询效率。
POST _search { "query": { "bool" : { // 指定我们的查询为bool 查询 "must" : { "term" : { "user.id" : "kimchy" } // 文档的 user.id 字段必须等于kimchy }, "filter": { "term" : { "tags" : "production" } // 文档的 tags 字段必须有 production,这里 tags 为一个数组 }, "must_not" : { "range" : { "age" : { "gte" : 10, "lte" : 20 } // 文档的 age 字段不可以在10到20范围内 } }, "should" : [ { "term" : { "tags" : "env1" } }, // 如果文档的 tags 字段中有env1,增加评分 { "term" : { "tags" : "deployed" } } // 如果文档的 tags 字段中有deployed,增加评分 ], "minimum_should_match" : 1, // 表示 should 条件中满足一个就可以增加打分 "boost" : 1.0 } } }
Boosting query
Boosting query 有三个参数,positive、negative、negative_boost,使用这个查询以后,会返回满足 positive 条件的文档,同时如果文档满足 negative 的条件,就会降低这个文档的相关性分数,降低的比例由 negative_boost 指定。计算方法是如果文档满足 negative 的条件,就会将 positive 中计算好的打分乘以 negative_boost。negative_boost 取值范围在0~1之间。
GET /_search { "query": { "boosting": { // 指定查询为 boosting 查询 "positive": { "term": { "text": "apple" // 查询 text 字段等于 apple 的文档 } }, "negative": { // 满足该条件的文档将会降低打分 "term": { "text": "pie tart fruit crumble tree" } }, "negative_boost": 0.5 } } }
Disjunction max query
如果我们有多个条件,我们需要将条件中分数最高的作为相关性分数,我们就可以用这个查询语句,我们可以在 queries 中指定多个查询或者使用 multi_match。同时如果需要将除了最高分语句以外的其他语句也进行计算得分,使用 tie_breaker 指定这些语句的得分比例。
GET /_search { "query": { "dis_max": { // 表示这个查询是一个 Disjunction max query "queries": [ // 指定多个查询,取打分最高的 { "term": { "title": "Quick pets" } }, { "term": { "body": "Quick pets" } } ], "tie_breaker": 0.7 } } } GET /_search { "query": { "dis_max": { "queries": [ { "multi_match": { "query": "apple", "fields": ["name", "desc"] // 也可以这样写,表示在多个字段中查询 apple, 优先取分数高的 } } ] } } }
function_score query
这个查询主要用来修改查询返回的分数,例如在搜索商品时,需要考虑商品的受欢迎程度,增加店铺的热度;点外卖时需要考虑店铺的距离,好评率等。在使用 function_score 时,需要指定一个或者多个计算函数,通过这些函数来为查询返回的每个文档计算一个新的分数。首先,通过参数 score_mode 指定如何组合计算分数,有以下选项:
1、multiply:分数相乘(默认)
2、sum:分数相加
3、avg:分数是平均值
4、first:具有匹配filter条件的第一个函数被应用
5、max:使用最高分
6、min:使用最低分
使用参数 boost_mode 定义新计算的分数与查询的分数合并,有以下选项:
1、multiply:查询分数和新计算的分数相乘(默认)
2、replace:仅使用新计算的分数,查询分数将被忽略
3、sum:查询分数和新计算的分数相加
4、avg:查询分数和新计算的分数的平均值
5、max:查询分数和新计算的分数的最大值
6、min:查询分数和新计算的分数的最小值
然后就需要如何计算这个新的分数了,可以使用 script_score 指定一个脚本来计算分数,这里可以使用上面说的 Script Score query 中的脚本语句,script_score 函数部分不需要进行修改,可以直接拷贝到这里就可以运行;random_score 生成一个随机分数;field_value_factor 可以使用文档中的字段来影响得分,filed 指定要从文档中提取的字段,factor指定字段乘以的可选因子,modifier 指定对该字段的计算方法:log,log1p,log2p,ln,ln1p,ln2p,平方,sqrt 或 reciprocal,默认为无。如果我们需要我们文档的一些字段来影响打分,就可以用这个函数,具体如何使用,需要根据业务来定。
GET /_search { "query": { "function_score": { // 定义一个 function_score query "field_value_factor": { "field": "my-int", // 需要计算的字段 "factor": 1.2, "modifier": "sqrt", // 计算方式是取平方根 "missing": 1 } } } }
限制文档的返回数量
在总文档数很多的情况下,可能一个 query 会返回成千上万的文档,如果我们想要在不改变 query 的前提下限制返回的数量,就需要用到另一个 search 参数:min_score,设置一个分数,匹配的文档如果 _score 低于这个分数,则相当于不满足条件,不返回。这个参数可以应用在这样的场景中:用户指定某一条件排序,比如说价格排序,可能 query 命中了很多文档,按照价格排序后会导致很多不是那么相关的文档排在前面,影响用户体验,如果我们在搜索之前,先拿到相关性符合我们预期的最后一篇文档的相关性分数,就可以在其他排序中指定min_score 为这个分数,这样不管怎么排序,出来的文档都是比较相关的。例如:
GET /_search { "min_score": 0.5, // 表示我们搜出来的文档需要满足打分大于等于0.5,不满足则不会被搜索出来 "query" : { // min_score 需要于下面定义好的查询计算好的得分去比较 "term" : { "user" : "kimchy" } } }
小结
以上的方法都是使用 Elasticsearch 自带的参数和方法实现的,大部分的业务场景都可以用到,如果公司只有几个人来负责搜索模块,且没有专门的搜索算法工程师,我们可以考虑用
Elasticsearch 自带的这些功能来满足我们的需求。如果需要粗排+精排,或者使用算法等来计算打分,那我们可以去实现一个打分插件来支持。本文只是讲了 Elasticsearch 中的一些基本功能,如何用好这些功能,可以参考官方的文档好好琢磨。