Vector retrieval often requires scalar filters such as price or category constraints. The optimizer automatically selects the optimal filtering strategy based on filter selectivity.
Filtering strategies
The optimizer selects one of three strategies based on filter selectivity:
-
pre-filtering
-
post-filtering
-
inline-filtering
Pre-filtering
When a filter is highly selective, the execution plan first uses a scalar index (such as a BTree index) to build an initial candidate set, then calculates similarity only for vectors in that set.
Post-filtering
When a scalar filter has low selectivity, post-filtering is more efficient. The vector index first retrieves an initial candidate set by similarity, then the scalar filter is applied. A potential issue is that fewer candidates may remain than the top-K value specified in the query.
addresses this with iterative scan: if too few candidates remain after filtering, the system continues vector retrieval until enough candidates are found or a scan limit is reached. To enable iterative scan with an HNSW index:
SET hnsw.iterative_scan = 'relaxed_order';
SET hnsw.max_scan_tuples = 100000;
Parameters:
-
iterative_scan: Controls result ordering by similarity.-
relaxed_order: Better performance, approximate ordering. -
strict_order: Strict similarity ordering with higher overhead. -
off: Disables the feature.
-
-
max_scan_tuples: Maximum tuples to scan during iterative scan. Prevents unlimited iterations.
Inline-filtering
For medium-selectivity filters where pre-filtering and post-filtering both perform poorly, supports inline-filtering, which combines scalar and vector indexes within a single query.
Supported versions
Supported versions:
-
(minor kernel version 2.0.14.20.44.0 and later)
-
(minor kernel version 2.0.16.13.16.0 and later)
-
(minor kernel version 2.0.17.9.6.0 and later)
-
(minor kernel version 2.0.18.3.2.0 and later)
You can view the minor kernel version in the console or run SHOW polardb_version; to check it. If your cluster does not meet the version requirement, upgrade the minor kernel version.
Example
SELECT id, category_id, embedding, embedding <-> '[50,50,50]' AS distance
FROM items
WHERE category_id = 3 -- B-Tree bitmap filter
ORDER BY embedding <-> '[50,50,50]' -- HNSW ordered scan
LIMIT 10;
The execution plan is as follows:
Limit
-> Bitmap Filtered Index Scan using idx_items_hnsw on items
Filter: (category_id = 3)
TIDs Checked: 40 -- The HNSW index returned 40 TIDs
Bitmap Hits: 6 -- 6 TIDs matched the bitmap
-> Bitmap Index Scan on idx_items_category
Index Cond: (category_id = 3)
Inline-filtering uses a bitmap pre-filter to skip unnecessary heap fetches, reducing I/O overhead. This strategy is especially effective for queries following the WHERE scalar_col = X ORDER BY vector_col <-> query LIMIT K pattern.
Performance at different filter rates
maintains stable performance across different filter rates. The following table shows which execution plan is selected at each rate:
|
filter_rate |
Performance improvement |
Execution plan |
|
0.001 |
No change |
pre-filtering |
|
0.1 |
No change |
post-filtering |
|
0.5 |
No change |
post-filtering |
|
0.9 |
No change |
post-filtering |
|
0.95 |
No change |
inline-filtering |
|
0.98 |
1.4x |
inline-filtering |
|
0.99 |
2.9x |
inline-filtering |
|
0.995 |
4.3x |
inline-filtering |
|
0.999 |
No change |
post-filtering |