1. 什么是function score?
- function score 就是 elasticsearch 提供的一种通过函数来对相关性评分进行二次计算的方法。这里的函数可以大致分为两种。
2. 为什么需要function score?
- 我们做搜索出来的数据排序的时候,往往不能只考虑字符的相关性,还需要考虑产品方的各种定制化的需求。而function score则可以用来解决定制化需求。
- 这里有可能有同学会说,我们可以把数据搜索出来以后,自己用自己熟悉的编程语言进行分数计算,以及排序。这是一种办法,但是会有两个问题。
3. function对应解决的业务需求?
3.1 script score(解决复杂的自定义需求)
- **可以解决的业务需求:**可太多了,因为支持自己写一个函数去针对score进行计算,函数中可以获取到每个document的值,以及这个document当下的分数。
- 代码示例(一):相关性分数等于某个值/10,同时由于这是一个函数脚本,其实还可以写更多的逻辑,可以看第二个示例
GET /_search
{
"query": {
"script_score": {
"query": {
"match": { "message": "elasticsearch" }
},
"script": {
"source": "doc['my-int'].value / 10 "
}
}
}
}
- 代码示例(二):
- 这里可以看到通过函数实现余弦相似度,当然这里强调的重点不是余弦相似度,而是我们可以用这个实现很复杂的计算逻辑。从而对于相关性分数有一个兜底计算方案。
GET my-index-000001/_search
{
"query": {
"script_score": {
"query" : {
"bool" : {
"filter" : {
"term" : {
"status" : "published"
}
}
}
},
"script": {
"source": """
float[] v = doc['my_dense_vector'].vectorValue;
float vm = doc['my_dense_vector'].magnitude;
float dotProduct = 0;
for (int i = 0; i < v.length; i++) {
dotProduct += v[i] * params.queryVector[i];
}
return dotProduct / (vm * (float) params.queryVectorMag);
""",
"params": {
"queryVector": [4, 3.4, -0.2],
"queryVectorMag": 5.25357
}
}
}
}
}
3.2 weight
- 可以解决的业务需求:通过对文档进行加权。解决比如我们搜索文章,那么文章title命中关键词得到的分数,应该比文章content命中关键词得到的分数更高。
- weight 是 5 ,即自定义函数得分 func_score = 5 ,最终结果的 score 等于 query_score * 5
{
"query": {
"function_score": {
"query": { "match": { "message": "elasticsearch" } },
"functions": [
{
"filter": { "match": { "title": "elasticsearch" } },
"weight": 5
}
]
}
}
}
3.3 random
- 可以解决的业务需求:通过随机打分,生成 [0, 1) 之间均匀分布的随机分数值。可以解决比如我们的广告推荐,我们希望每个用户都能看到一个不同的随机顺序(尽可能多的曝光不同的广告),但是对于相同的用户,当他点击第二页,第三页或者后续页面时,看到的顺序应该是相同的。这就是所谓的一致性随机(Consistently Random)
- 示例:
- random_score 影响因素之一(seed),比如用户ID,这样每一个用户看到的数据都是不一样的。
- random_score 影响因素之二(field),可以理解为是另外一个 seed。当某个字段发生变化的时候,整 最终生成的 random score 也会发生变化。
GET test_index/_search
{
"query": {
"function_score": {
"random_score": {
"seed": 10,
"field": "id"
}
}
}
}
3.4 field value factor
- 可以解决的业务需求:把一个文章的 popularity 或 votes (受欢迎或赞)作为相关性分数计算中的一部分,比如同样命中关键词的文章,点赞更多的应该更加前面( 哪怕命中的关键词稍微少一点 )。
- 举例:
{
"query": {
"function_score": {
"query": { "match": { "message": "elasticsearch" } },
"field_value_factor": {
"field": "likes",
"factor": 1.2,
"missing": 1,
"modifier": "log1p"
}
}
}
}
- field : 参与计算的字段。
- factor : 乘积因子,默认为 1 ,将会与 field 的字段值相乘。
- missing : 如果 field 字段不存在则使用 missing 指定的缺省值。
- modifier : 计算函数,为了避免分数相差过大,用于平滑分数,可以是以下之一:
- none : 不处理,默认
- log : log(factor * field_value)
- log1p : log(1 + factor * field_value)
- log2p : log(2 + factor * field_value)
- ln : ln(factor * field_value)
- ln1p : ln(1 + factor * field_value)
- ln2p : ln(2 + factor * field_value)
- square : 平方,(factor * field_value)^2
- sqrt : 开方,sqrt(factor * field_value)
- reciprocal : 求倒数,1/(factor * field_value)
- 假设某个匹配的文档的点赞数是 1000 ,那么例子中其打分函数生成的分数就是 log(1 + 1.2 * 1000),最终的分数是原来的 query 分数与此打分函数分数相差的结果。
3.5 decay function
- 可以解决的业务需求:通过一个函数,对某个数值进行一个递减的操作,然后将递减后的数值融入到相关性分数计算中。从而解决:比如我们搜索外卖的时候,肯定是地理位置离我们更近的店要展示的在更加前面。又比如我们索一些研究论文的时候,发布时间更新的,肯定也是需要更加靠前。
- 代码示例(一):
"DECAY_FUNCTION": {
"FIELD_NAME": {
"origin": "30, 120",
"scale": "2km",
"offset": "0km",
"decay": 0.33
}
}
上例的意思就是在距中心点方圆 2 公里之外,分数减少到三分之一(乘以 decay 的值 0.33)。
-
DECAY_FUNCTION 可以是以下任意一种函数:
- linear : 线性函数
- exp : 指数函数
- gauss : 高斯函数
-
origin : 中心点,只能是数值、日期、geo-point
-
scale : 定义到中心点的距离
-
offset : 偏移量,默认 0
-
decay : 衰减指数,默认是 0.5
-
代码示例(二)
GET /_search
{
"query": {
"function_score": {
"gauss": {
"@timestamp": {
"origin": "2013-09-17",
"scale": "10d",
"offset": "5d",
"decay": 0.5
}
}
}
}
}
- 中心点是 2013-09-17 日期,scale 是 10d 意味着日期范围是 2013-09-12 到 2013-09-22 的文档分数权重是 1 ,日期在 scale + offset = 15d 之外的文档权重是 0.5 。
4. 调试相关性时的一些监控指标
- 可以解决的业务需求:我们对搜索进行优化,是希望让客户能够更快的从数据库是检索到他想要的数据,而怎么判断我们的优化是否真的起到了作用,我们就可以通过一些标准来进行判断。我这里提供两种方式。
- 第一种:搜索引擎常用的 DCG:
- 搜索引擎一般采用PI(per item)的方式进行评测,简单地说就是逐条对搜索结果进行分等级的打分。假设我们在Google上搜索一个词,然后得到5个结果。我们对这些结果进行3个等级的区分:Good(好)、Fair(一般)、Bad(差),然后赋予他们分值分别为3、2、1,假定通过逐条打分后,得到这5个结果的分值分别为3、2 、1 、3、 2。然后再对这些分数进行一些数学计算,具体可以参看:baike.baidu.com/item/DCG
- 第二种:简单易用的的数据埋点,结合个人的业务经验。
- 第一种:搜索引擎常用的 DCG: