Filter-aware ANN · 结构化谓词 + 向量组合¶
一句话定位
"找相似商品 AND price < 1000 AND category = '电子'"——结构化 WHERE 过滤和向量相似度查询组合执行的问题。看起来简单 · 实际上是生产向量检索最常见的难题之一。
和其他页的边界
- 本页 · 结构化谓词 + 向量搜索组合 的算法和工程问题
- Hybrid Search · 稀疏 + 稠密 向量融合(不是同一个问题!)
- HNSW / IVF-PQ · 向量索引本身的算法
两个 Hybrid 别混——业界有时笼统都叫 "hybrid search": - Vector + Filter = 本页(结构化谓词 + ANN) - Sparse + Dense = hybrid-search.md(两种 embedding 融合)
TL;DR
- 三种实现策略:Pre-filter · Post-filter · In-filter (Filterable HNSW)
- 选哪种取决于过滤选择性:过滤后剩 < 5% 用 pre · > 50% 用 post · 中间用 in-filter
- Filterable HNSW(2023+ · Qdrant 最早商业化 · Milvus / pgvector 0.8+ 跟进)是 2024-2026 中等选择性场景的常见方案
- pgvector 0.8+(2024-10)iterative_scan 三种模式(off / strict_order / relaxed_order)· 0.8.2+(2026-02) 修复并行 HNSW 缓冲区溢出 · 是 PG 场景新突破
- 量化和 filter-aware 互相影响 · 组合设计要一起考虑
1. 问题定义 · 为什么"看起来简单"不简单¶
典型业务场景¶
-- 找"和这张图片相似 + 价格 < 1000 + 有库存 + 分类是电子"的商品
SELECT id, name
FROM products
WHERE category = 'electronics'
AND price < 1000
AND stock > 0
ORDER BY vector_distance(embedding, :query_vec)
LIMIT 10;
为什么难?¶
纯 ANN 索引(HNSW / IVF / DiskANN)建立时不知道结构化字段。查询时: - 如果先过滤再 ANN(pre-filter)· 过滤后的集合可能破坏 ANN 索引的连通性 · 图走不到 Top-K - 如果先 ANN 再过滤(post-filter)· 可能要拉出超多候选(10,000+ 才能剩下 10 个)· 成本高 - 过滤选择性天然动态 · 同一 query "category = 电子" 过滤率 20% · "category = 罕见二级分类" 过滤率 0.01%——一种策略不可能都最优
2. 三种实现策略¶
Pre-filter(先过滤再 ANN)¶
问题: - S 小(< 5% 全集)· 有效 —— ANN 索引里大部分点被 mask · 但图索引的连通性被破坏 · 往往走完全图或失败 - S 中等(5-50%)· 经常最差——既不能用索引剪枝 · 又破坏 ANN 遍历 - S 大(> 50%)· 浪费——其实不需要提前过滤
适合:过滤极严格(剩 < 5%) · 或者数据量小可以暴力扫 S
Post-filter(先 ANN 再过滤)¶
问题: - 过滤选择性高(保留 < 1%)· 需要扩到几千几万 · 成本爆 - ANN Top-K 顺序和过滤后顺序不严格一致 · 扩展倍数不够可能漏真正的近邻 - "扩多少" 是查询时猜 · 业务 query 分布变了就失准
适合:过滤宽松(保留 > 50%)· 简单查询
In-filter (Filterable HNSW) · 2023+ 主流¶
思路:让 HNSW 图在遍历时感知过滤——遇到不满足 WHERE 的点 · 跳过但继续探索邻居 · 不破坏图连通。
HNSW 图遍历:
candidate = get_neighbor(current)
if matches_filter(candidate):
add_to_result(candidate)
else:
continue_to_neighbors(candidate) # 不剪枝 · 只是不加结果
工程落地:
- Qdrant 的 Filterable HNSW (2023+) · 较早商业化 · 通过在图构建时考虑过滤场景 · 或动态调整图遍历
- Milvus 2.3+ 的 Filtered Search · 支持 bitmap / expression-based
- pgvector 0.8.0+(2024-10 发布) · 新增
iterative_scan三种模式(off / strict_order / relaxed_order) · 0.8.2+(2026-02) 修复并行 HNSW 缓冲区溢出 · PostgreSQL 场景新突破 - LanceDB · filter 推到 Lance 文件层做 pruning
优势: - 选择性通用(5-70% 过滤都好用) - 延迟接近纯 ANN
劣势: - 索引构建时间可能增加 - 不是所有 ANN 库都实现
3. 选型决策¶
按过滤选择性(filter 后保留的比例)分:
| 过滤选择性 | 推荐策略 | 典型产品 |
|---|---|---|
| < 5%(极严格 · 结果极少) | Pre-filter | 所有向量库都支持 |
| 5-50%(中等选择性) | In-filter / Filterable HNSW | Qdrant / Milvus / pgvector 0.8+ |
| > 50%(宽松 · 过滤少数排除) | Post-filter + 合适扩展倍数 | 所有向量库都支持 |
动态策略:生产场景无法知道 query 的选择性 · 两种应对:
- 运行时评估(Qdrant / Milvus 在做)· query planner 先估算过滤命中数 · 自动选策略
- 多索引并行(复杂)· pre-filter 和 post-filter 都跑 · 看谁先出结果 · 不推荐
4. 和其他机制的组合¶
Filter + Quantization¶
量化(见 Quantization)会降低精度 · 叠加 filter 风险放大:
- PQ + post-filter · 精度双重打折——扩展倍数要更大
- Binary Quantization + pre-filter · 过滤后余量小 · Hamming 距离失真——不推荐组合
- 组合验证 · 自家数据上实测 Recall@K · 不要假设
Filter + Hybrid Search¶
Hybrid Search(稀疏+稠密融合)和 filter 组合时:
- 各路 retrieval 都要各自处理 filter(BM25 自然支持 · Dense 走 filter-aware ANN)
- RRF 融合在 filter 后· 不要先融合再 filter
Filter + Rerank¶
两阶段架构(详见 Rerank):
一阶段的 filter 推进减少了二阶段 rerank 的候选数· 延迟优化关键。
5. 陷阱¶
- 假设 post-filter 够用 · 过滤选择性高时会漏真近邻 · 必须评估扩展倍数
- pre-filter 破坏 HNSW 连通 · 用在中等选择性 · 得到的 Top-K 其实不是真的 Top-K
- 忘记索引重建 · 加新的过滤字段 · filter-aware HNSW 要重新构图(不是所有库自动)
- 没做选择性估算 · 让一个 query 同时走极端场景(一次 10% 一次 0.01%)· 延迟 P99 飞
- 把 WHERE 丢到向量库而非用外部数据库预筛 · Qdrant/Milvus 的 filter 对字段索引能力有限 · 复杂 WHERE 效率低
- 混淆 Hybrid 的两种含义 · Filter-aware ≠ Sparse+Dense
相关¶
- HNSW · Filterable HNSW 的算法底座
- Quantization · 量化和 filter 的组合影响
- Hybrid Search · Sparse+Dense 融合(不同概念别混)
- Qdrant · Filterable HNSW 较早商业化
- pgvector · PG 场景 filter-aware 0.8+
- Milvus · Filtered Search 支持