Elasticsearch 定制评分(自定义评分)
本文与作者在csdn上的博文【Elasticsearch 定制评分(自定义评分)
原力计划】保持同步
Elasticsearch 的相似度算法被定义为检索词频率/反向文档频率, TF/IDF 。
一. 相关概念:
- 检索词频率:tf
词 t 在文档 d 的词频( tf )是该词在文档中出现次数的平方根。
tf(t in d) = √frequency
检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
- 反向文档频率:idf
词 t 的逆向文档频率( idf )是:索引中文档数量除以所有包含该词的文档数,然后求其对数。
idf(t) = 1 + log ( numDocs / (docFreq + 1))
每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
二、计算公式为:
score(q,d) = queryNorm(q) · coord(q,d) · ∑ (tf(t,d) · idf(t)² · t.getBoost() · norm(t,d))
其它参数定义:
- 字段长度准则:norm
字段长度归一值( norm )是字段中词数平方根的倒数。
norm(d) = 1 / √numTerms
字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。
- 查询归一因子
查询归一因子 ( queryNorm
)试图将查询 归一化 ,这样就能将两个不同的查询结果相比较。
这个因子是在查询过程的最前面计算的,具体的计算依赖于具体查询
queryNorm = 1 / √sumOfSquaredWeights
sumOfSquaredWeights
是查询里每个词的 IDF 的平方和。
以上是对于一个词项检索时的相关度计算,当查询多个词项时,得出多个相关度,则需要按照向量空间模型来计算整体的相似度:
向量空间模型:vector
向量空间模型(vector space model) 提供一种比较多词查询的方式,单个评分代表文档与查询的匹配程度
在向量空间模型里,向量空间模型里的每个数字都代表一个词的 权重 ,与 词频/逆向文档频率(term frequency/inverse document frequency) 计算方式类似。
3、控制相关度
一般来说,控制相关度的需求,分为两种:
-
忽略TF/IDF
即不需要评分,可以使用constant_score来达成,在constant_score
查询中,它可以包含查询或过滤,为任意一个匹配的文档指定评分1
,忽略 TF/IDF 信息。 -
定制评分
function_score
查询 是用来控制评分过程的终极武器,它允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分_score
的目的。本文主要介绍使用script_score
函数。
使用脚本计算评
script_score
自定义脚本可以完全控制评分计算:
{
"function_score": {
"functions": {
"script_score": {
"script": "doc['price'].value + doc['margin'].value"
}
}
}
}
4、Painless
es脚本引擎,简单安全,无痛使用,Painless使用白名单来限制函数与字段的访问,针对es的场景来进行优化,只做es数据的操作,更加轻量级。
Painless中变量可以声明为基本数据类型、引用类型、字符串、void
(不返回值)、数组以及动态类型。其支持下面基本类型:
byte, short, char, int, long, float, double, boolean.声明变量与java类似:
int i = 0; double a; boolean g = true;
数组类型支持一维和多维,初始值为null。与引用类型一样,使用new关键字,并为每个维度设置中括号
int[] x = new int[2];
x[0] = 3;
x[1] = 4;
int[] b = new int[] {1,2,3,4,5};
painless支持动态类型,elasticsearch会自动推断类型
def a = 1;
def b = "foo";
def[][] h = new def[2][2];
条件语句和运算符
Painless包含完整的操作符列表,除了它们的优先级和结合性之外,这些操作符与其他高级语言几乎兼容。
if (doc['foo'].value = 5) {
doc['foo'].value *= 10;
}
else {
doc['foo'].value += 10;
}
Painless支持if else
,但不支持else if
或switch
循环
def total = 0;
for (def i = 0; i
5、控制相关度实践
该实例中将使用script_score,将评分设置为:doc['download_cnt'].value * 2.5 +doc['replication_cnt'].value * 1.2
{
"query": {
"function_score": {
"query": {
"match": {
"name": "1"
}
},
"functions": [
{
"script_score": {
"script": {
"params": {
"download_ratio": 2.5,
"replication_ratio": 1.2
},
"lang": "painless",
"inline": "doc['download_cnt'].value * params.download_ratio + doc['replication_cnt'].value * params.replication_ratio"
}
}
}
]
}
}
}
_search操作中所有的返回值,都可以通过一个
map
类型变量doc获取。和所有其他脚本语言一样,用[]获取map
中的值。这里要强调的是,doc只可以在_search中访问到
猜你喜欢
Elasticsearch 数据写入原理
阅读 3331、elasticsearch 如何使文档可以被搜索 为了支持全文检索而采用倒排索引,倒排索引包含一个有序列表,列表包含所有文档出现过的词项 ,对于每一个词项,包含了它所有曾出现过文档的列表。 早期的倒排索引,会在文档变化时,重建新的索引,直到完成后替换掉旧的索引,这样新的变化就可以被搜索到。 倒...
Elasticsearch 模糊搜索
阅读 547Es 实现类似于mysql的模糊搜索: 比如:对字段 keywords 进行 模糊搜索 带“愉”字的文档: { "query": { "bool": { "filter": [ { ...
elasticsearch 去重计数
阅读 342去重计数不是精确计数,数据量大的情况下会有误差,官方文档说的是,默认的情况下百万级数据会有5%的误差,实测如下: 实际文档数:1924920 去重计数:1912715 误差率:(1924920 - 1912715)/ 1924920 = 0.006 %0.6的误差率还能接受,如果需要更高的准确...
elasticsearch查询文档数量
阅读 555查询文档数量时很常见的操作,一般可以直接使用count获取文档数,但是获取到数量信息,在【分页】应用中,意味着需要查询分页然后再查询总数。 有另一种方法,可以让我们在一次查询中获取分页数据并得到总量。 在搜索时,结果中的 hits.total 信息中会包含一个整数值表示文档数,当实际文档数小于10...
Elasticsearch实战:给博客打造全文检索
阅读 598学习和使用Elasticsearch有一段时间了,项目中大量使用到了es,但对于我来说都是部分或者局部地去使用,所以得找个时间好好整理并且再完整时实践一下es,于是就有了这篇文章,本文将先简单介绍一下使用到的相关技术,然后再整体讲解实战内容。首先系统架构是LNMP,很简单的个人博客网站(逐步前...
记一次ElasticSearch 更改 mapping 字段类型的过程
阅读 1113首先,es不支持直接跟那个该mappinng,所以,更改 mapping 实质上是重建索引。 操作步骤如下: 1、为当前这个索引old_index设置一个别名my_index: curl -XPOST localhost:9200/_aliases -d ' { "act...
Elasticsearch 搜索数组字段
阅读 9701、搜索 数组字段 tags 中同时存在元素 str_a、str_b{ "query": { "bool": { "filter": [ { "term":...