先看现象。Elasticsearch 8.13 做带条件的向量检索,把条件写在 post_filter 里,返回结果只有 3 条,而同条件用普通查询能匹配上百条,top_k 设的是 10。

把日志和配置对完,能解释这个现象的基本就这几处。post_filter 在 kNN 之后执行,先截断再过滤。还有一种是kNN 的过滤条件应该写在 filter 子句里。

少数情况下是num_candidates 太小,候选里符合条件的不够。

不容易想到的是,向量字段与过滤字段不在同一索引分片。

第一件事是确认 filter 与 post_filter 的返回条数差异

python -c "print({'k':10,'num_candidates':100})"

再往下把条件移进 kNN 的 filter 参数

curl -s http://localhost:9200/docs/_search -H 'Content-Type: application/json' -d '{"knn":{"field":"vec","query_vector":[0.1,0.2],"k":10,"num_candidates":200,"filter":{"term":{"tenant":"a1"}}}}'

调大 num_candidates 观察召回变化

curl -s http://localhost:9200/docs/_search -H 'Content-Type: application/json' -d '{"knn":{"field":"vec","query_vector":[0.1,0.2],"k":10,"num_candidates":800}}'

等前面都确认过,再对比两种写法在同一批查询上的命中数

python es_eval.py --cases cases.jsonl --mode filter,post_filter

手上没有现成的复现脚本,就照着线上请求手工拼了一个。

怎么算真的好了,看的是同一批现象。带条件的检索稳定返回 10 条且都满足租户条件,召回数与不加条件的比例接近。

这里还有个坑。post_filter 是先取后筛,用在向量检索上几乎必然丢结果,条件一定要进 kNN 的 filter。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。

作者 9823189