Filtering

更新时间:
复制 MD 格式

When you add a WHERE clause to a nearest neighbor query, the filtering strategy you choose has a direct impact on recall and performance. This page describes the available strategies and when to use each one.

Choose a filtering strategy

ConditionStrategy
Matches a low percentage of rowsExact index on the filter column
Matches a high percentage of rowsApproximate index (HNSW or IVFFlat)

Exact indexes

Create an exact index on the filter column to get fast, precise nearest neighbor results. Supported index types:

  • B-tree (default)

  • Hash

  • GiST

  • SP-GiST

  • GIN

  • BRIN

For queries involving multiple columns, create a multicolumn index.

Exact indexes work best when the filter condition matches a low percentage of rows. For high-selectivity filters, use an approximate index instead.

Approximate indexes

With an approximate index (Hierarchical Navigable Small World (HNSW) or IVFFlat), filtering is applied *after* the index scan — not during it. The index returns its top candidates first, and only then are those candidates filtered by your WHERE clause.

For example: if your condition matches 10% of rows and you use an HNSW index with the default hnsw.ef_search of 40, only about 4 rows will match on average. If that is not enough, increase hnsw.ef_search.

For better recall, use iterative index scans, which continue scanning the index until enough matching rows are found.

Iterative index scans

Iterative index scans (available from version 0.8.0) automatically scan more of the index until enough filtered results are found, or until the limits set by hnsw.max_scan_tuples or ivfflat.max_probes are reached.

Check the extension version

Run the following query to check the installed version:

SELECT * FROM pg_extension WHERE extname = 'vector';

The extversion field shows the current version. Sample output:

  oid  | extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
-------+---------+----------+--------------+----------------+------------+-----------+--------------
 17941 | vector  |    17865 |         2200 | t              | 0.8.0      |           |
(1 row)

If the version is earlier than 0.8.0, upgrade the extension:

ALTER EXTENSION vector UPDATE TO '0.8.0';

Sorting modes

Iterative index scans support two sorting modes:

  • `strict_order`: results are sorted precisely by distance.

  • `relaxed_order`: results may be slightly out of distance-based order, but recall is better. Use a materialized CTE to enforce strict ordering when needed.

Enable iterative index scans

Strict ordering

-- HNSW index
SET hnsw.iterative_scan = strict_order;

-- IVFFlat index
SET ivfflat.iterative_scan = strict_order;

Relaxed ordering

-- HNSW index
SET hnsw.iterative_scan = relaxed_order;

-- IVFFlat index
SET ivfflat.iterative_scan = relaxed_order;

Get strict ordering in relaxed mode

In relaxed_order mode, use a materialized CTE to sort the final results precisely by distance:

WITH relaxed_results AS MATERIALIZED (
    SELECT id, embedding <-> '[1,2,3]' AS distance FROM items WHERE id = 1 ORDER BY distance LIMIT 5
) SELECT * FROM relaxed_results ORDER BY distance;

Filter by distance

For queries that filter by distance, use a materialized CTE and place the distance filter *outside* the CTE. Put all other filters *inside* the CTE:

WITH nearest_results AS MATERIALIZED (
    SELECT id, embedding <-> '[1,2,3]' AS distance FROM items ORDER BY distance LIMIT 5
) SELECT * FROM nearest_results WHERE distance < 5 ORDER BY distance;

This placement gives the best performance due to how the PostgreSQL executor processes the query.

Iterative index scan parameters

Scanning a large portion of an approximate index is costly. Use the following parameters to control when the scan stops:

Index typeParameterDefaultDescription
HNSWhnsw.max_scan_tuples20000Maximum number of tuples scanned during a query.
HNSWhnsw.scan_mem_multiplier1Maximum memory multiplier during HNSW candidate scanning. Works with hnsw.max_scan_tuples to cap memory usage. If increasing hnsw.max_scan_tuples does not improve recall, increase this value.
IVFFlativfflat.max_probesMaximum number of probes during a query. If this value is less than ivfflat.probes, ivfflat.probes is used.