41 · 相关性调优(boosting、function_score、filter vs query 上下文)
阶段:第五阶段 / 进阶与实战
主题:
_score怎么来的、怎么调,以及 filter/query 上下文的性能差异
1. 概念
- query 上下文 :算
_score(相关性打分),回答「有多匹配」。 - filter 上下文 :只判断 yes/no,不算分,可缓存、更快,回答「符不符合」。
- 调分手段:字段
boost、boosting查询(降权而非排除)、function_score(按业务规则改分)。
PostgreSQL 没有相关性评分概念,这是 ES 相对 SQL 的核心增量能力。
2. PostgreSQL 对照
sql
-- SQL 没有 _score,只能靠 ORDER BY 表达优先级
SELECT *,
CASE WHEN geo_cd = 'AP' THEN 2 ELSE 1 END AS weight
FROM salesdata
WHERE material ILIKE '%thinkpad%'
ORDER BY weight DESC;
3. ES DSL
filter 上下文(不算分、可缓存)
GET salesdata_idx/_search
{
"query": {
"bool": {
"must": [ { "match": { "material": "notebook" } } ], // 算分
"filter": [ { "term": { "geo_cd": "AP" } } ] // 不算分、缓存
}
}
}
字段 boost
{ "match": { "material": { "query": "notebook", "boost": 3 } } }
function_score(按业务改分)
GET salesdata_idx/_search
{
"query": {
"function_score": {
"query": { "match": { "material": "notebook" } },
"functions": [
{ "filter": { "term": { "geo_cd": "AP" } }, "weight": 2 },
{ "field_value_factor": { "field": "net_amount", "modifier": "log1p" } }
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
4. Spring Boot 实现
java
@Component
public class Doc41Relevance {
@Autowired
private ElasticsearchClient elasticsearchClient;
/** must 算分 + filter 精确过滤(不算分、可缓存) */
public List<Map<String, Object>> searchScored(String indexName, String kw, String geo)
throws IOException {
SearchResponse<Map> resp = elasticsearchClient.search(s -> s
.index(indexName)
.query(q -> q.bool(b -> b
.must(m -> m.match(mt -> mt.field("material").query(kw).boost(3f)))
.filter(f -> f.term(t -> t.field("geo_cd").value(geo))))),
Map.class);
return resp.hits().hits().stream()
.map(Hit::source)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
想看每条为什么这么打分,可用 explain(
.explain(true))或_explainAPI 调试。
5. 坑与最佳实践
- 精确条件放 filter :等值/范围/枚举放
filter,省去打分开销并利用缓存。 - 只想排序不想过滤用
should/boosting:boosting能降权而不是直接排除。 function_score谨慎 :脚本/函数打分贵,boost_mode/score_mode要选对。- 别对海量结果排相关性 :能用业务字段排序(日期、金额)就别都靠
_score。 - 调分先 explain:打分不符预期时用 explain 看每项贡献,别盲改。