Elasticsearch实战——function_score 查询详解
Elasticsearch实战——function_score 查询详解
文章目录
- Elasticsearch实战——function_score 查询详解
- 1. function_score简介
- 2. function_score(field_value_factor具体实例)
- 2.1创建索引
- 2.2 添加文档
- 2.3 演示
- 2.4 modifier参数的支持的值
- 2.5 factor参数
- 2.6 综合应用
- 3. function_score (weight具体实例)
- 4. function_score (衰减函数 linear、exp、gauss 具体实例)
- 4.1 衰减函数的简介
- 4.2 衰减函数的参数
- 4.3 具体案例
- 5. 关注我
1. function_score简介
在使用ES进行全文搜索时,搜索结果会以文档的相关度进行排序,而这个相关度是可以通过function_score自己定义的。我们可以通过使用function_score来控制文档相关。
function_score是专门用来处理文档_score的DSL,它允许每个主查询query匹配的文档应用加强函数,以达到改变原始查询评分_score的目的。
function_score 会在主查询query结束后对每一个匹配的文档进行一系列的重打分操作,能够对多个字段一起进行综合评估,并且能够使用filter将结果划分为多个子集,并为每个子集使用不同的加强函数。
function_score提供了几种加强_score计算的函数:
-
weight:设置一个简单而不被规范化的权重提升值weight加强函数和boost参数类似,可以用于任何查询,不过有一点差别就是weight不会被Lucene nomalize成难以理解的浮点数,而是直接被应用(boost会被nomalize)。例如,当
weight为3时,最终结果为 :new_score = old_score * 3 -
field_value_factor:将某个字段的值乘以old_score将字段
shardCount(分享次数)或clickCount(点击次数)作为考虑因素:new_score = old_score * shardCount(文档的分享次数) -
random_score:为每个用户使用不同的随机评分对结果排序,但对某一具体用户来说,看到的顺序始终是一致的。 -
衰减函数(
linear、exp、guass):以某个字段的值为基准,距离某个值越近,得分越高。
function_score其他辅助的参数:
-
boost_mode:决定old_score和加强score如何合并multiply:new_score = old_score * 加强scoresum:new_score = old_score + 加强scoremin:new_score= min(old_score, 加强score)max:new_score= max(old_score, 加强score)replace:new_score = 加强score,直接替换old_score
-
score_mode:决定functions里面的加强score怎么合并,会先合并加强score成一个总加强score,再使用总加强score区和old_score做合并,换言之就是先执行score_mode,再执行boost_mode。multiply(默认)、sum、avg、first(使用第一个函数的结果作为最终结果)、max、min
-
max_boost:限制加强函数的最大效果,就是限制加强score最大能多少,但是不会限制old_score。如果加强score超过了max_boost的限制值,会把加强score的值设置成max_boost。
2. function_score(field_value_factor具体实例)
2.1创建索引
PUT func_index
{"settings": {"number_of_replicas": 1, "number_of_shards": 3},"mappings": {"info":{"properties":{"title":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"},"shardCount":{"type":"integer"}}}}
}
2.2 添加文档
PUT _bulk
{"index":{"_index":"func_index","_type":"info","_id":"1"}}
{"title":"ES入门","shardCount":2}
{"index":{"_index":"func_index","_type":"info","_id":"2"}}
{"title":"ES进阶","shardCount":5}
{"index":{"_index":"func_index","_type":"info","_id":"3"}}
{"title":"ES应用宝典","shardCount":10}
2.3 演示
使用普通的查询
GET func_index/info/_search
{"query": {"match": {"title": "ES"}}
}[{"_score": 0.2876821,"_source": {"title": "ES进阶","shardCount": 5}},{"_score": 0.19856805,"_source": {"title": "ES入门","shardCount": 2}},{"_score": 0.16853254,"_source": {"title": "ES应用宝典","shardCount": 10}}
]
使用function_score 的 field_value_factor改变_score,将old_score乘上shardCount的值
GET func_index/info/_search
{"query":{"function_score": {"query": {"match": {"title": "ES"}},"functions": [{"field_value_factor": {"field": "shardCount"}}]}}
}[{"_score": 1.6853254,#原来的得分:0.16853254"_source": {"title": "ES应用宝典","shardCount": 10}},{"_score": 1.4384104,#原来的得分:0.2876821"_source": {"title": "ES进阶","shardCount": 5}},{"_score": 0.3971361,#原来的得分:0.19856805"_source": {"title": "ES入门","shardCount": 2}}
]
加上max_boost,限制field_value_factor的最加强score。
可以看到ES入门的加强score是2,在max_boost限制里,所以不受影响。
而ES进阶和ES应用宝典的field_value_factor函数产生的加强score因为超过max_boost的限制,所以被设为3。
GET func_index/info/_search
{"query":{"function_score": {"query": {"match": {"title": "ES"}},"functions": [{"field_value_factor": {"field": "shardCount"}}],"max_boost": 3}}
}[{"_score": 0.8630463,#原来的得分:0.2876821"_source": {"title": "ES进阶","shardCount": 5}},{"_score": 0.5055976,#原来的得分:0.16853254"_source": {"title": "ES应用宝典","shardCount": 10}},{"_score": 0.3971361,#原来的得分:0.19856805"_source": {"title": "ES入门","shardCount": 2}}
]
2.4 modifier参数的支持的值
有时线性计算new_score=old * shardCount值的效果不是很好,field_value_factor中还支持modifier、factor参数,可以改变shardCount值对old_score的影响。
none:new_score = old * shardCount,默认值log1p:new_score = old_score * log(1+shardCount)log2p:new_score = old_score * log(2+shardCount)ln:new_score = old_score * ln(shardCount)ln1p:new_score = old_score * ln(1+shardCount)ln2p:new_score = old_score * ln(2+shardCount)square:计算平方sqrt:计算平方根reciprocal:计算倒数
2.5 factor参数
factor作为一个调节参数,没有modifier那么强大能改变整个曲线,它仅改变一些常量值,设置factor > 1会提升效果,factor < 1会降低效果。
假设modifier是log1p,那么加入了factor的公式就是:
new_score = old_score * log(1 + factor * shardCount)
2.6 综合应用
GET func_index/info/_search
{"query":{"function_score": {"query": {"match": {"title": "ES"}},"functions": [{"field_value_factor": {"field": "price","modifier": "log1p","factor": 1.2}}],"max_boost": 10,"boost_mode": "sum"}}
}
3. function_score (weight具体实例)
functions是一个数组,里面放着的是将要被使用的加强函数列表,我们在里面使用了3个filter去过滤数据,并且每个filter都设置了一个加强函数,并且还是用了一个会应用到所有文档的field_value_factor加强函数。
可以为列表里面的每个加强函数指定一个filter。这样只有在文档满足此filter的要求才会给该文档使用上加强函数。如果不使用filter,加强函数就会应用到全部的文档上。
一个文档可以一次满足多条加强函数和多个filter,如果同时满足多个加强函数,则会产生多个加强score,然后ES通过score——mode定义的方式来合并这些加强score,得到一个总的加强score。得到总加强score后,再用boost_mode定义的方式和old_score做合并。
GET func_index/info/_search
{"query": {"function_score": {"query": {"match": {"title": "ES"}},"functions": [{"filter": {"term": {"city": "北京市"}}, "weight": 10},{"filter": {"term": {"city": "深圳市"}}, "weight": 8},{"filter": {"term": {"city": "上海市"}}, "weight": 5}]}}
}
java使用示例:
FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = {new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("city","北京市"), ScoreFunctionBuilders.weightFactorFunction(10)),new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("city","深圳市"), ScoreFunctionBuilders.weightFactorFunction(8)),new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("city","上海市"), ScoreFunctionBuilders.weightFactorFunction(5)),};
FunctionScoreQueryBuilder query = QueryBuilders.functionScoreQuery(builders);
weight加强函数也可以用来调整每个语句的贡献度,weight的默认值是1.0,当设置了weight,这个weight就会和评分函数的值相乘,在通过score_mode和其他加强函数合并。
GET func_index/info/_search
{"query":{"function_score": {"query": {"match": {"title": "ES"}},"functions": [{"field_value_factor": {"field": "price","modifier": "log1p","factor": 1.5},"weight": 1.2}],"max_boost": 10,"boost_mode": "sum"}}
}
java使用示例:
FieldValueFactorFunctionBuilder factor = ScoreFunctionBuilders.fieldValueFactorFunction("price").modifier(FieldValueFactorFunction.Modifier.LOG1P).factor(1.5f).setWeight(1.2f);
QueryBuilders.functionScoreQuery(factor).maxBoost(10).boostMode(CombineFunction.SUM);
4. function_score (衰减函数 linear、exp、gauss 具体实例)
先看下面这个例子。用户选择酒店,希望酒店离市中心近一点,但是如果价格足够偏移,为了省钱。可以妥协,选择比较远的酒店。
如果我们使用一个filter排除市中心方圆1km外的酒店,再使用filter排除价格超过150的酒店。这种做法太过强硬,可能由一个酒店在1.2km,但是比较便宜,用户可能会因此妥协选择住这家酒店。
为了解决这个问题,function_score查询提供了一组衰减函数,让我们可以在两个滑动标准之间权衡。
4.1 衰减函数的简介
function_score支持三种衰减函数:linear、exp和gauss。
这三种函数在用法上完全一样,只是差别与他们的衰减曲线的形状。
- linear:线性函数是一条直线,一旦与Y轴相交,所有其他值的评分都是0.
- exp:指数函数,先剧烈衰减,然后变缓慢。
- guass(最常用):高斯函数。它钟型的,它的衰减速度时先缓慢,然后变快,最后放缓。

4.2 衰减函数的参数
衰减函数支持的参数:
- origin:中心点,落在原点上的文档评分
_score为满分1.0。支持数值、时间、经纬度坐标点。 - offset:从origin为中心,设置一个偏移量offset覆盖一个范围,在此范围内所有评分
_score也是都和origin一样是满分1.0。 - scale:衰减率,即一个文档从origin下落时,
_score改变的速度 - decay:从origin衰减到scale所得的评分
_score。默认0.5(一般不需要改变,这个参数使用默认的就行)
以上图为例:
- 所有曲线的origin都是40,offset是5,因此范围在 40-5 <= value <=40+5的文档的评分
_score都是满分1.0 - 在此范围外,评分会开始衰减,衰减率有scale值(此处为5)和decay值(此处是默认值0.5)决定,在origin +/- (offset + scale)处的评分是decay值,也就是在30、50的评分是0.5分
- 也就是说,在
origin + offset + scale或是origin - offset - scale的点上,得到的分数仅有decay分
4.3 具体案例
案例一:
准备数据,其中title为text类型,readCount为integer类型。
PUT blog
{
"settings": {"number_of_replicas": 0,"number_of_shards": 1},"mappings":{"properties":{"title":{"type":"text"},"readCount":{"type":"integer"}}}
}
{ "title": "Elasticsearch实战——搜索详解", "readCount": 5 }
{ "title": "Elasticsearch实战——环境搭建", "readCount": 10 }
{ "title": "Elasticsearch实战——JAVA客户端API", "readCount": 15 }
GET blog/_search
{"query": {"function_score": {"query": {"match_all": {}},"functions": [{"gauss": {"readCount": {"origin": "10","scale": "5","decay": 0.5}}}]}}
}
[{"_score": 1,"_source": {"title": "Elasticsearch实战——JAVA客户端API","readCount": 15}},{"_score": 0.5,//因为decay=0.5,所以当位于 origin-offset-scale=10 的位置时,分数为decay,就是0.5"_source": {"title": "Elasticsearch实战——环境搭建","readCount": 10}},{"_score": 0.0625,"_source": {"title": "Elasticsearch实战——搜索详解","readCount": 5}}
]
案例二:
假设有一个用户希望租一个离市区近一点的酒店,并且每晚不超过100元的酒店。而且与距离相比,用户对价格更敏感。那么使用衰减函数guass查询如下:
- 把price语句的origin点设为50,由于价格的特性一定是越低越好,所以0~100元的所有价格的酒店都认为是比较好的酒店,而100以上的酒店就慢慢衰减。如果把origin设置为100,那么价格低于100元的酒店的评分反而会降低。这并不是我们想要的结果。把origin和offset同时设置为50,只让price大于100元的评分才会变低。
{"query": {"function_score": {"query": {"match_all": {"boost": 1}},"functions": [{"filter": {"match_all": {"boost": 1}},"gauss": {"location": {"origin": {"lat": 40.15077,"lon": 116.28414},"scale": "10km","offset": "5km","decay": 0.5},"multi_value_mode": "MIN"}},{"filter": {"match_all": {"boost": 1}},"gauss": {"price": {"origin": 50,"scale": 20,"offset": 50,"decay": 0.5},"multi_value_mode": "MIN"}}],"score_mode": "multiply","boost_mode": "sum","max_boost": 3.4028235e+38,"boost": 1}}
}
java实例:
//原点
GeoPoint origin = new GeoPoint(40.15077, 116.28414);
//偏移量(offset):与原点相差在偏移量之内的值,也可以得到满分
String offset = "5km";
//衰减规模(scale):当值超出了原点到偏移量折叠范围,他所得的分数开始进行衰减
String scale = "10km";
//衰减值(decay):该字段可以被接受的值(默认0.5),相当于一个分阶段,具体效果与衰减的模式有关
double decay = 0.5;
GaussDecayFunctionBuilder location = ScoreFunctionBuilders.gaussDecayFunction("location", origin, scale, offset, decay);
GaussDecayFunctionBuilder price = ScoreFunctionBuilders.gaussDecayFunction("price", 50, 20, 50, decay);
FunctionScoreQueryBuilder.FilterFunctionBuilder[] filterFunctionBuilders = { new FunctionScoreQueryBuilder.FilterFunctionBuilder(location),new FunctionScoreQueryBuilder.FilterFunctionBuilder(price)
};
FunctionScoreQueryBuilder query = QueryBuilders.functionScoreQuery(filterFunctionBuilders).boostMode(CombineFunction.SUM);
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(query);
5. 关注我
搜索微信公众号:java架构强者之路

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
