扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
今天就跟大家聊聊有关ElasticSearch相关性打分机制是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
专注于为中小企业提供成都网站设计、成都做网站服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业西固免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了数千家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
ElasticSearch 2.3版本全文搜索默认采用的是相关性打分TFIDF,在实际的运用中,我们采用Multi-Match给各个字段设置权重、使用should给特定文档权重或使用更高级的Function_Score来自定义打分,借助于Elasticsearch的explain功能,我们可以深入地学习一下其中的机制。
PUT /gino_test { "mappings": { "tweet": { "properties": { "text": { "type": "string", "term_vector": "with_positions_offsets_payloads", "store" : true, "analyzer" : "fulltext_analyzer" }, "fullname": { "type": "string", "term_vector": "with_positions_offsets_payloads", "analyzer" : "fulltext_analyzer" } } } }, "settings" : { "index" : { "number_of_shards" : 1, "number_of_replicas" : 0 }, "analysis": { "analyzer": { "fulltext_analyzer": { "type": "custom", "tokenizer": "whitespace", "filter": [ "lowercase", "type_as_payload" ] } } } } }
插入测试数据:
POST gino_test/_search { "explain": true, "query": { "match": { "text": "my cup" } } }
查询结果: score_simple.json
打分分析:
ElasticSearch目前采用的默认相关性打分采用的是Lucene的TF-IDF技术。
我们来深入地分析一下这个公式:
score(q,d) = queryNorm(q) · coord(q,d) · ∑ (tf(t,d) · idf(t)² · t.getBoost() · norm(t,d))
score(q,d) 是指查询输入Q和当前文档D的相关性得分;
queryNorm(q) 是查询输入归一化因子,其作用是使最终的得分不至于太大,从而具有一定的可比性;
coord(q,d) 是协调因子,表示输入的Token被文档匹配到的比例;
tf(t,d) 表示输入的一个Token在文档中出现的频率,频率越高,得分越高;
idf(t) 表示输入的一个Token的频率级别,它具体的计算与当前文档无关,而是与索引中出现的频率相关,出现频率越低,说明这个词是个稀缺词,得分会越高;
t.getBoost() 是查询时指定的权重.
norm(t,d) 是指当前文档的Term数量的一个权重,它在索引阶段就已经计算好,由于存储的关系,它最终值是0.125的倍数。
注意:在计算过程中,涉及的变量应该考虑的是document所在的分片而不是整个index。
score(q,d) = _score(q,d.f) --------- ① = queryNorm(q) · coord(q,d) · ∑ (tf(t,d) · idf(t)² · t.getBoost() · norm(t,d)) = coord(q,d) · ∑ (tf(t,d) · idf(t)² · t.getBoost() · norm(t,d) · queryNorm(q)) = coord(q,d.f) · ∑ _score(q.ti, d.f) [ti in q] --------- ② = coord(q,d.f) · (_score(q.t1, d.f) + _score(q.t2, d.f))
① 相关性打分其实是查询与某个文档的某个字段之间的相关性打分,而不是与文档的相关性;
② 根据公式转换,就变成了查询的所有Term与文档中字段的相关性求和,如果某个Term不相关,则需要处理coord系数;
POST /gino_test/_search { "explain": true, "query": { "multi_match": { "query": "gino cup", "fields": [ "text^8", "fullname^5" ] } } }
查询结果:score_bestfields.json
打分分析:
score(q,d) = max(_score(q, d.fi)) = max(_score(q, d.f1), _score(q, d.f2)) = max(coord(q,d.f1) · (_score(q.t1, d.f1) + _score(q.t2, d.f1)), coord(q,d.f2) · (_score(q.t1, d.f2) + _score(q.t2, d.f2)))
对于multi-field的best_fields模式来说,相当于是对每个字段对查询分别进行打分,然后执行max运算获取打分最高的。
在计算query weight的过程需要乘上字段的权重,在计算fieldNorm的时候也需要乘上字段的权重。
默认operator为or,如果使用and,打分机制也是一样的,但是搜索结果会不一样。
POST /gino_test/_search { "explain": true, "query": { "multi_match": { "query": "gino cup", "type": "cross_fields", "fields": [ "text^8", "fullname^5" ] } } }
查询结果:score_crossfields.json
打分分析:
score(q, d) = ∑ (_score(q.ti, d.f)) = ∑ (_score(q.t1, d.f), _score(q.t1, d.f)) = ∑ (max(coord(q.t1,d.f) · _score(q.t1, d.f1), coord(q.t1,d.f) · _score(q.t1, d.f2)), max(coord(q.t2,d.f) · _score(q.t2, d.f1), coord(q.t2,d.f) · _score(q.t2, d.f2)))
coord(q.t1,d.f)函数表示搜索的Term(如gino)在multi-field中有多少比率的字段匹配到;best_fields模式中coord(q,d.f1)表示搜索的所以Term(如gino和cup)有多少比率存在与特定的field字段(如text字段)里;
对于multi-field的cross_fields模式来说,相当于是对每个查询的Term进行打分(每个Term执行best_fields打分,即看下哪个field匹配更高),然后执行sum运算。
默认operator为or,如果使用and,打分机制也是一样的,但是搜索结果会不一样。这是一个使用operator为or的报文:score_crossfields_or.json
为了增加filter的测试,给gino_test/tweet增加一个tags的字段。
PUT /gino_test/_mapping/tweet { "properties": { "tags": { "type": "string", "analyzer": "fulltext_analyzer" } } }
增加tags的标签
POST /gino_test/_search { "explain": true, "query": { "bool": { "must": { "bool": { "must": { "multi_match": { "query": "gino cup", "fields": [ "text^8", "fullname^5" ], "type": "best_fields", "operator": "or" } }, "should": [ { "term": { "tags": { "value": "goods", "boost": 6 } } }, { "term": { "tags": { "value": "hobby", "boost": 3 } } } ] } } } } }
查询结果:score_should.json
打分分析:
增加了should的权重之后,相当于多了一个打分参考项,打分的过程见上面的计算过程。
DSL格式:
{ "function_score": { "query": {}, "boost": "boost for the whole query", "functions": [ { "filter": {}, "FUNCTION": {}, "weight": number }, { "FUNCTION": {} }, { "filter": {}, "weight": number } ], "max_boost": number, "score_mode": "(multiply|max|...)", "boost_mode": "(multiply|replace|...)", "min_score" : number } }
支持四种类型发FUNCTION:
script_score: 自定义的高级打分机制,涉及的字段只能是数值类型的
weight: 权重打分,一般结合filter一起使用,表示满足某种条件加多少倍的分
random_score: 生成一个随机分数,比如应该uid随机打乱排序
field_value_factor: 根据index里的某个字段值影响打分,比如销量(涉及的字段只能是数值类型的)
decay functions: 衰减函数打分,比如越靠近市中心的打分越高
来做一个实验。先给index增加一个查看数的字段:
PUT /gino_test/_mapping/tweet { "properties": { "views": { "type": "long", "doc_values": true, "fielddata": { "format": "doc_values" } } }
给三条数据分别加上查看数的值:
POST gino_test/tweet/1/_update { "doc" : { "views" : 56 } }
最终数据的样子:
执行一个查询:
{ "explain": true, "query": { "function_score": { "query": { "multi_match": { "query": "gino cup", "type": "cross_fields", "fields": [ "text^8", "fullname^5" ] } }, "boost": 2, "functions": [ { "field_value_factor": { "field": "views", "factor": 1.2, "modifier": "sqrt", "missing": 1 } }, { "filter": { "term": { "tags": { "value": "goods" } } }, "weight": 4 } ], "score_mode": "multiply", "boost_mode": "multiply" } } }
查询结果:score_function.json
打分分析:
score(q,d) = score_query(q,d) * (score_fvf(`view`) * score_filter(`tags:goods`))
score_mode表示多个FUNCTION之间打分的运算法则,需要注意不同的FUNCTION的打分的结果级别可能相差很大;
boost_mode表示function_score和query_score打分的运算法则,也需要注意打分结果的级别;
ES官网介绍: Rescoring | Elasticsearch Reference [2.3] | Elastic
重打分机制并不会应用到所有的数据中。比如需要查询前10条数据,那么所有的分片先按默认规则查询出前10条数据,然后应用rescore规则进行重打分返回给master节点进行综合排序返回给用户。
rescore支持多个规则计算,以及与原先的默认打分进行运算(权重求和等)。
rescore因为计算的打分的document较少,性能应该会更好一点,但是这个涉及到全局排序,实际运用的场景要注意。
看完上述内容,你们对ElasticSearch相关性打分机制是什么有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注创新互联行业资讯频道,感谢大家的支持。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流