Elasticsearch 5.4基于句中词位置的评分优化方案咨询
解决Elasticsearch 5.4中开头匹配文档的优先排序问题
嘿,我刚好处理过类似的排序需求,结合你用的Elasticsearch 5.4和FOSElasticaBundle,给你一套可行的解决方案——核心思路是通过自定义评分规则,给那些文本以查询词开头的文档额外加分,让它们排在更前面。
问题根源
默认的match查询会基于词频、逆文档频率等因素计算评分,像“protection de disque de frein”这类包含查询词但不在开头的文档,可能因为词的位置或权重计算拿到更高分,所以我们需要手动干预评分逻辑。
步骤1:给目标字段添加Keyword子字段
因为text类型的字段会被分词,无法直接判断原始文本的开头,所以得给目标字段加一个keyword子字段,用来存储未分词的原始内容。在FOSElasticaBundle的映射配置里这么写:
fos_elastica: indexes: your_index_name: types: your_type_name: mappings: # 假设你的目标字段叫product_name product_name: type: text fields: keyword: type: keyword ignore_above: 256 # 根据你的文本长度调整
步骤2:用Function Score查询实现自定义评分
我们需要用function_score查询,在基础的match查询之上,添加一个脚本评分规则——如果文档的keyword子字段以查询词开头,就给它加一个高权重(比如10分,你可以根据实际情况调整)。
方式一:通过FOSElasticaBundle配置文件实现
如果你的查询是固定场景,可以直接写在配置里:
fos_elastica: indexes: your_index_name: types: your_type_name: # ... 其他映射、持久化配置 query: function_score: query: match: product_name: query: "disque de frein" # 这里可以动态替换成用户输入的查询词 functions: - script_score: script: inline: "doc['product_name.keyword'].value.startsWith(params.query_term) ? 10 : 0" params: query_term: "disque de frein" boost_mode: sum # 把原始评分和脚本额外评分相加
方式二:在代码中动态构建查询(更灵活,适合用户输入的动态查询)
如果是在Repository里处理用户的动态查询,可以这么写:
use Elastica\Query; use Elastica\Query\FunctionScore; use Elastica\Script; // 获取用户输入的查询词 $userQuery = "disque de frein"; // 1. 构建基础的match查询,确保能匹配到所有包含查询词的文档 $matchQuery = new Query\Match(); $matchQuery->setField('product_name', $userQuery); // 2. 构建脚本评分规则:判断product_name的keyword子字段是否以查询词开头 $script = new Script([ 'inline' => "doc['product_name.keyword'].value.startsWith(params.query_term) ? 10 : 0", 'params' => ['query_term' => $userQuery] ]); $scriptScoreFunction = new FunctionScore\ScriptScore($script); // 3. 组合function_score查询 $functionScoreQuery = new FunctionScore(); $functionScoreQuery->setQuery($matchQuery); $functionScoreQuery->addFunction($scriptScoreFunction); $functionScoreQuery->setBoostMode('sum'); // 叠加原始评分和额外评分 // 4. 执行查询并获取结果 $results = $this->find($functionScoreQuery);
注意事项
- 脚本权限:ES5.4默认可能限制了inline脚本的执行,你需要在
elasticsearch.yml里添加配置:script.inline: on和script.stored: on(生产环境建议做更严格的脚本权限控制,比如使用脚本白名单)。 - 权重调整:脚本里的
10是额外加分的数值,你可以根据实际测试结果调整,比如如果开头匹配的文档需要更靠前,可以调到20,反之降低。 - 字段名称替换:记得把示例中的
product_name换成你实际使用的字段名。
这样配置后,当用户搜索“disque de frein”时,以该短语开头的文档会因为额外的评分加成,排在包含该短语但不在开头的文档前面啦!
内容的提问来源于stack exchange,提问作者Maxime Détaille
相关产品推荐
相关产品推荐

