Elasticsearch 6 Java API中FunctionScoreQueryBuilder使用及升级问题
关于Elasticsearch 6 Java API中FunctionScoreQueryBuilder的使用及评分不生效问题解决
嘿,我来帮你搞定这两个问题!先从你升级ES后遇到的评分函数不生效的问题说起,再给你梳理FunctionScoreQueryBuilder的基础用法~
一、你的评分函数不生效的问题排查与修复
你提到升级到6.0后,添加自定义评分函数但结果和未添加时一致,结合你的代码来看,大概率是这几个原因,咱们一步步排查:
1. 脚本语法或字段问题
你的脚本"_score * (doc['field'].value!=0? 50000:1)"可能存在两个小问题:
- 没有判断字段是否存在:如果某个文档的
field字段缺失,脚本会报错,ES会默认忽略这个评分函数(用原始_score),导致看起来没生效。 - 类型匹配问题:如果
field是长整型(long),直接写0可能会有类型不匹配,建议写成0L。
修改后的脚本可以这样写:
ScoreFunctionBuilder fb = ScoreFunctionBuilders.scriptFunction( new Script( ScriptType.INLINE, "painless", "_score * (doc['field'].size() > 0 && doc['field'].value != 0 ? 50000 : 1)", Collections.emptyMap() ) );
另外要确保ES允许inline脚本,在elasticsearch.yml中添加(开发环境适用,生产环境建议用存储脚本):
script.inline: true script.stored: true
2. 传入的是Filter而非查询,导致原始_score固定为1
你用了new FunctionScoreQueryBuilder(filter, fb),这里的filter是过滤上下文,所有通过过滤的文档原始_score都是1,乘以系数后评分是50000或1,但如果你的查询原本需要基于某个查询的评分来调整,应该把**查询(QueryBuilder)**而不是filter传进去。比如把过滤条件放到bool查询的filter中,原始查询放到must里:
// 假设你的原始查询是originalQuery,过滤条件是filter BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() .must(originalQuery) // 原始查询,计算基础评分 .filter(filter); // 过滤文档,不影响评分 FunctionScoreQueryBuilder fsb = new FunctionScoreQueryBuilder(boolQuery, fb);
3. 没有按_score排序,评分变化看不到效果
如果你的查询设置了其他排序字段(比如时间、ID),即使评分变了,结果顺序也不会改变,看起来就和没加评分函数一样。一定要确保按_score排序:
SearchRequestBuilder srbPaged = client.prepareSearch(indexName) .setFetchSource(includes, excludes) .setQuery(fsb) .addSort(SortBuilders.scoreSort().order(SortOrder.DESC)); // 新增按评分降序排序
二、Elasticsearch 6 Java API中FunctionScoreQueryBuilder的基础使用
下面给你梳理完整的使用步骤,涵盖常见场景:
1. 创建评分函数(ScoreFunctionBuilder)
支持多种评分函数,比如脚本函数、字段值因子函数、随机评分等:
- 脚本评分函数(自定义逻辑,像你用的这种):
// 自定义脚本,基于字段调整评分 ScoreFunctionBuilder scriptFunc = ScoreFunctionBuilders.scriptFunction( new Script( ScriptType.INLINE, "painless", "_score * (doc['priority'].value + 1)", // 示例逻辑:基础评分乘以(优先级+1) Collections.emptyMap() ) );
- 字段值因子函数(基于字段值调整评分):
// 基于popularity字段,用LOG1P修饰,乘以1.2的因子 ScoreFunctionBuilder fieldFactorFunc = ScoreFunctionBuilders.fieldValueFactorFunction("popularity") .factor(1.2f) .modifier(FieldValueFactorModifier.LOG1P);
2. 构建FunctionScoreQueryBuilder
根据需求选择不同的构造方式:
- 单查询+单评分函数:
FunctionScoreQueryBuilder fsb = new FunctionScoreQueryBuilder(yourQuery, scriptFunc);
- 多评分函数组合:可以添加多个函数,设置结果组合方式(score_mode)和与原始评分的结合方式(boost_mode):
FunctionScoreQueryBuilder fsb = new FunctionScoreQueryBuilder(yourQuery) .add(scriptFunc) .add(fieldFactorFunc) .scoreMode(ScoreMode.SUM) // 多个评分函数的结果相加 .boostMode(CombineFunction.MULTIPLY); // 最终结果 = 原始评分 * 函数总和
- 过滤后重算评分:如果只想过滤文档,不基于原始查询评分,直接传入filter:
FunctionScoreQueryBuilder fsb = new FunctionScoreQueryBuilder(yourFilter, scriptFunc) .boostMode(CombineFunction.REPLACE); // 用评分函数的结果完全替换原始的1分
3. 执行查询
把FunctionScoreQueryBuilder设置到SearchRequestBuilder中,执行即可:
SearchResponse response = client.prepareSearch("your_index") .setQuery(fsb) .setSort(SortBuilders.scoreSort().order(SortOrder.DESC)) .setSize(10) .get(); // 处理结果 SearchHits hits = response.getHits(); for (SearchHit hit : hits) { System.out.println("文档ID:" + hit.getId() + ",评分:" + hit.getScore()); }
内容的提问来源于stack exchange,提问作者Betty Papagianni




