Store vector data in a regular Postgres table, query it with ivfplus's distance operators, and build an index once you need one.
Storing vectors
Add a vector column to a table, specifying the number of dimensions:
CREATE TABLE items ( id bigserial PRIMARY KEY, content text, embedding vector(768) );
The dimension count must match the output of your embedding model. Common values:
| Embedding model | Dimensions |
|---|---|
| OpenAI text-embedding-3-small | 1536 |
| OpenAI text-embedding-3-large | 3072 |
| Google text-embedding-004 | 768 |
| Nomic Embed Text | 768 |
Insert a row with a vector value:
INSERT INTO items (content, embedding) VALUES ('example text', '[...]');
Querying vectors
ivfplus supports the same distance operators as pgvector, through the matching opclass (see Access method and operator classes):
| Operator | Distance metric | Use case |
|---|---|---|
<-> | L2 (Euclidean) | General-purpose similarity, default for most embedding models |
<=> | Cosine | When vector magnitude varies, normalized embeddings |
<#> | Inner product | Maximum inner product search |
Find the 10 nearest neighbors by L2 distance:
SELECT id, content FROM items ORDER BY embedding <-> '[...]' LIMIT 10;
Building an index
Without an index, a query scans every row and returns exact results. To check whether that's still fast enough for your workload, run EXPLAIN (ANALYZE, BUFFERS) on your query as-is and compare its actual time against your latency budget. If it's within budget, skip the index for now; add one once it isn't.
Build an ivfplus index and set a starting probes value for your session:
CREATE INDEX ON items USING ivfplus (embedding vector_l2_ops) WITH (lists = 1000); SET ivfplus.probes = 32;
Once the index exists, your queries don't change — ivfplus only changes which rows the planner considers first. See Quickstart for a complete walkthrough with example data, and Tuning and sizing for how to choose lists and probes for your table.
Maintaining the index
Keep an eye on where the index lives in storage and how it handles writes after the initial build.
Storage
For best query performance, keep the index in memory (shared_buffers plus the OS page cache). If it's too large to fit, use fast NVMe storage to minimize the latency of index page reads.
Vacuuming and REINDEX
Plain VACUUM doesn't reclaim space in an ivfplus index — deleted or updated rows tombstone their lane, and only REINDEX repacks the index and reuses that space. See Writes, VACUUM, and REINDEX for the full behavior and how to monitor drift with check_ivfplus_index_health().