
Pgvector
- 37 installs
- 22 repo stars
- Updated August 1, 2026
- itechmeat/llm-code
Store vectors in PostgreSQL and run nearest-neighbor search with pgvector: distance operators and HNSW/IVFFlat index tuning.
About
A guide to the pgvector Postgres extension covering vector types, distance operators and HNSW/IVFFlat indexing with client-library usage. Use it when storing embeddings in Postgres, running similarity search, or tuning ANN index recall versus speed.
- Distance operator cheatsheet: L2 <->, inner product <#>, cosine <=>, L1 <+>, Hamming/Jaccard for binary
- HNSW for better speed/recall vs IVFFlat for faster builds; index needs ORDER BY <op> ... LIMIT
Pgvector by the numbers
- 37 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #464 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/itechmeat/llm-code --skill pgvectorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 37 |
|---|---|
| repo stars | ★ 22 |
| Last updated | August 1, 2026 |
| Repository | itechmeat/llm-code ↗ |
What it does
Store vectors in PostgreSQL and run nearest-neighbor search with pgvector: distance operators and HNSW/IVFFlat index tuning.
Files
pgvector
PostgreSQL extension for storing vectors and running exact/approximate nearest-neighbor search in SQL.
Quick Navigation
- Installation:
references/installation.md - Core concepts and SQL recipes:
references/core.md - Indexing (HNSW / IVFFlat) and tuning:
references/indexing.md - Filtering, iterative scans, and performance:
references/performance-and-filtering.md - Types and functions reference (vector/halfvec/bit/sparsevec):
references/types-and-functions.md - Troubleshooting:
references/troubleshooting.md - Client libraries (priority):
- Python:
references/python.md - Go:
references/go.md - Node (JS/TS):
references/node.md - Java:
references/java.md - Swift:
references/swift.md
When to Use
- You need vector similarity search inside Postgres (keep vectors with relational data).
- You want SQL-native ANN indexes (HNSW or IVFFlat) with tunable recall/speed.
- You want consistent patterns to store/query embeddings across multiple application languages.
Quick Start (already installed)
Prerequisite: pgvector is installed on the Postgres server. See: references/installation.md.
Enable per database and run a first query:
CREATE EXTENSION vector;
CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;Choosing distance operators
- L2 (Euclidean): use
<-> - Inner product: use
<#>(note: returns negative inner product) - Cosine distance: use
<=> - L1: use
<+> - Binary vectors: Hamming
<~>/ Jaccard<%>
Indexing rules of thumb
- Exact search: no pgvector index; may use parallel scan on large tables.
- ANN search:
- Prefer HNSW for better speed/recall, higher build time/memory.
- Use IVFFlat when you need faster builds/lower memory.
- Create one index per distance function/operator class you plan to use.
Critical Prohibitions / Gotchas
- Approximate indexes can change results (recall vs speed).
- Index usage typically requires
ORDER BY <distance-op> ... LIMIT .... <#>returns negative inner product; multiply by-1to get the actual value.NULLvectors are not indexed; for cosine distance, zero vectors are not indexed.
Links
- Docs / repo: https://github.com/pgvector/pgvector
- Client libs:
- Python: https://github.com/pgvector/pgvector-python
- Go: https://github.com/pgvector/pgvector-go
- Node: https://github.com/pgvector/pgvector-node
- Java: https://github.com/pgvector/pgvector-java
- Swift: https://github.com/pgvector/pgvector-swift
- Releases/tags: https://github.com/pgvector/pgvector/tags
pgvector core (SQL)
Install + enable
- pgvector is a PostgreSQL extension (README states Postgres 13+).
- Enable per-database:
CREATE EXTENSION vector;
Getting started (minimal SQL)
CREATE EXTENSION vector;
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding vector(3)
);
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;Note: <#> returns the negative inner product.
Data model
- Fixed dimension column:
embedding vector(1536)- Variable dimensions (no
(n)): embedding vector- Indexing then typically uses expression + partial indexes per dimension.
Insert / update
- Insert:
INSERT INTO items (embedding) VALUES ('[1,2,3]'); - Upsert: normal
ON CONFLICT ... DO UPDATEworks. - Bulk load:
- Prefer
COPYfor initial loads; add ANN indexes after.
Querying nearest neighbors
- Basic pattern (index-friendly):
ORDER BY <distance-op> ... LIMIT ...
Common query patterns
-- nearest neighbors to a query vector
SELECT * FROM items
ORDER BY embedding <-> '[3,1,2]'
LIMIT 5;
-- nearest neighbors to an existing row
SELECT * FROM items
WHERE id != 1
ORDER BY embedding <-> (SELECT embedding FROM items WHERE id = 1)
LIMIT 5;
-- within a distance threshold
SELECT * FROM items
WHERE embedding <-> '[3,1,2]' < 5;Tip: combine distance filtering with ORDER BY + LIMIT when you want index usage.
Distance operators
- L2 (Euclidean):
<-> - Inner product:
<#>(returns negative inner product) - Cosine distance:
<=> - L1 (taxicab):
<+> - Binary: Hamming
<~>, Jaccard<%>
Distance vs similarity
- Inner product value:
-(embedding <#> query_vec) - Cosine similarity:
1 - (embedding <=> query_vec)
Aggregates
- Average vector:
SELECT AVG(embedding) FROM items; - Grouped average:
SELECT category_id, AVG(embedding) FROM items GROUP BY category_id;
Practical tips
NULLvectors are not indexed; for cosine distance, zero vectors are not indexed (important for recall).
Go (pgvector-go)
Upstream: https://github.com/pgvector/pgvector-go
Install
go get github.com/pgvector/pgvector-go
Supported libraries (README)
- pgx, pg, Bun, Ent, GORM, sqlx
pgx (important detail: type registration)
- Import and register types per connection/pool:
pgxvec.RegisterTypes(ctx, conn)- or in
config.AfterConnectfor pools
This is typically required so pgx can encode/decode vector, halfvec, sparsevec, etc.
Data model
- Use
pgvector.Vectorin structs and tag the DB type in your ORM/driver metadata (examples showvector(3)). - Create vectors with:
pgvector.NewVector([]float32{...})
Querying
- Use raw SQL with distance operators:
SELECT id FROM items ORDER BY embedding <-> $1 LIMIT 5- In ORMs, use
OrderExpr("embedding <-> ?", vec)/ selector expressions.
Indexing
- Create HNSW or IVFFlat indexes with the right operator class:
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);- Use
vector_ip_opsfor inner product andvector_cosine_opsfor cosine distance.
Reference: other vector types
- Half vectors:
pgvector.NewHalfVector([]float32{...})- Sparse vectors:
pgvector.NewSparseVector([]float32{...})- or
pgvector.NewSparseVectorFromMap(elements, dimensions) - Note: indices start at 0 in the Go API (different from SQL
sparsevecformat).
Indexing (HNSW / IVFFlat)
pgvector supports exact search by default. Add an index for approximate nearest-neighbor (ANN) search when you need better latency at some recall cost.
Core rule
- Create a separate ANN index for each distance function/operator class you plan to query with.
- Queries typically need
ORDER BY <distance-op> ... LIMIT ...to use the ANN index.
HNSW
When to choose
- Better speed/recall tradeoff than IVFFlat, but slower builds and higher memory usage.
- Can create the index before loading data (no training step).
Create index (examples)
-- L2
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
-- inner product
CREATE INDEX ON items USING hnsw (embedding vector_ip_ops);
-- cosine
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
-- L1
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);
-- binary
CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);
CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);Operator classes for other types
- Use
halfvec_*_opsforhalfvec - Use
sparsevec_*_opsforsparsevec
Index options
m(default 16): max connections per layeref_construction(default 64): candidate list size during build
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)
WITH (m = 16, ef_construction = 64);Query option
hnsw.ef_search(default 40): candidate list size during search
SET hnsw.ef_search = 100;
-- or per query
BEGIN;
SET LOCAL hnsw.ef_search = 100;
SELECT ...;
COMMIT;Build performance
- Index build is much faster when the graph fits in
maintenance_work_mem. - If you see a notice that the HNSW graph no longer fits, builds will slow down; increase
maintenance_work_membut avoid exhausting server memory. - Create ANN indexes after bulk loading initial data when possible.
- Parallelism: tune
max_parallel_maintenance_workers(and possiblymax_parallel_workers).
Build progress
- Check
pg_stat_progress_create_indexphases for HNSW: initializingloading tuples
IVFFlat
When to choose
- Faster builds / lower memory, but typically worse speed/recall than HNSW.
- Works best when created after the table has enough data (it performs a k-means step).
Create index (example)
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);Choosing parameters (rule of thumb from README)
- Build after you have data.
lists:- up to 1M rows:
rows / 1000 - over 1M rows:
sqrt(rows) - Query: set
ivfflat.probes(higher = better recall, slower).
Note: setting ivfflat.probes to the number of lists makes the search exact, and the planner may stop using the index.
SET ivfflat.probes = 10;
BEGIN;
SET LOCAL ivfflat.probes = 10;
SELECT ...;
COMMIT;Build progress
- Check
pg_stat_progress_create_indexphases for IVFFlat: initializingperforming k-meansassigning tuplesloading tuples(the%field is only populated here)
Progress monitoring
- Use
pg_stat_progress_create_indexto see phases and progress.
Installation (pgvector)
pgvector is a PostgreSQL extension. The upstream README indicates support for Postgres 13+.
Build from source (Linux/macOS)
cd /tmp
git clone --branch v0.8.2 https://github.com/pgvector/pgvector.git
cd pgvector
make
make install # may need sudoMultiple Postgres installs
- Point the build to the correct
pg_config:
export PG_CONFIG=/path/to/pg_config
make clean
make
sudo --preserve-env=PG_CONFIG make installBuild on Windows
- Use the x64 Native Tools Command Prompt as Administrator.
set "PGROOT=C:\Program Files\PostgreSQL\18"
cd %TEMP%
git clone --branch v0.8.2 https://github.com/pgvector/pgvector.git
cd pgvector
nmake /F Makefile.win
nmake /F Makefile.win installCommon build issues
- Missing
postgres.h: - Linux: install server dev headers (
postgresql-server-dev-<major>) - Windows: verify
PGROOTpoints to the correct install - Portability:
-march=nativecan break when moving binaries between CPUs. - Build with
make OPTFLAGS=""for portable binaries.
Packaging / alternative install methods
- Docker image (based on official Postgres image)
- Homebrew:
brew install pgvector(for specific Postgres formulas) - PGXN:
pgxn install vector - Debian/Ubuntu: install
postgresql-<major>-pgvectorfrom PostgreSQL APT repo - RHEL/Fedora:
dnf install pgvector_<major>from PostgreSQL Yum repo - FreeBSD:
pkg install postgresql17-pgvector(example) - Alpine:
apk add postgresql-pgvector - Conda:
conda install -c conda-forge pgvector
Enable
- Per database:
CREATE EXTENSION vector;
Java / Kotlin (pgvector-java)
Upstream: https://github.com/pgvector/pgvector-java
JDBC (Java)
Key pattern:
- Enable extension:
CREATE EXTENSION IF NOT EXISTS vector - Register types on the
Connection: PGvector.registerTypes(conn)- Use
PreparedStatement#setObject(..., new PGvector(float[]))for parameters.
Nearest neighbors:
ORDER BY embedding <-> ? LIMIT 5
Spring JDBC
- Same idea as raw JDBC: pass
PGvectorobjects as parameters.
Hibernate / R2DBC
- Hibernate 6.4+ includes a dedicated vector module (
hibernate-vector) and recommends using it instead of the oldercom.pgvector.pgvectorintegration. - R2DBC PostgreSQL 1.0.3+ has built-in support for the vector type.
Kotlin
PGvector(floatArrayOf(...))works similarly for JDBC.
Indexing
- Create ANN indexes with SQL:
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);- Use
vector_ip_opsfor inner product andvector_cosine_opsfor cosine distance.
Reference: helper types
PGvector(vector)PGhalfvec(halfvec)PGbit(bit)PGsparsevec(sparsevec)
Sparse vector note:
- The Java API examples note indices start at 0 (different from SQL
sparsevectext format where indices start at 1).
Node.js / TypeScript (pgvector-node)
Upstream: https://github.com/pgvector/pgvector-node
Install
npm install pgvector
Common core: SQL literal conversion
- Many integrations use
pgvector.toSql([1, 2, 3])to pass vectors to SQL safely.
node-postgres (pg)
Enable extension
CREATE EXTENSION IF NOT EXISTS vector
Register types (important)
- Register per client or on pool connections:
pgvector.registerTypes(client)
This enables automatic parsing/serialization of pgvector types.
Query patterns
- Nearest neighbor:
ORDER BY embedding <-> $1 LIMIT 5(pass$1aspgvector.toSql([...]))
Indexing
- Create HNSW/IVFFlat indexes with operator classes:
vector_l2_ops,vector_ip_ops,vector_cosine_ops
Supported query-builder / ORM integrations shown in README
- Knex.js
- Objection.js
- Kysely
- Sequelize
- pg-promise
- Prisma
- Postgres.js
- Slonik
- TypeORM (0.3.27+ has built-in pgvector support)
- MikroORM
Notable gotchas
- Prisma: README notes
prisma migrate devdoes not support pgvector indexes; use SQL migrations / raw SQL for index creation.
Filtering, iterative scans, performance
Filtering with WHERE
When you combine ANN search with filters, pgvector/PG will often apply the filter after scanning the ANN index, which can reduce the number of returned rows.
Start with exact indexes on filters
- Add a normal index on the filter column(s):
CREATE INDEX ON items (category_id);
CREATE INDEX ON items (location_id, category_id);- Exact indexes work well when the filter matches a small fraction of rows.
ANN + filtering gotcha
- If ~10% of rows match a filter and
hnsw.ef_searchis 40, you may get ~4 matches on average unless you scan more candidates.
Iterative index scans (0.8.0+)
Iterative scans automatically scan more of the ANN index when filtering prevents enough rows from being found.
Ordering modes
strict_order: results strictly ordered by distancerelaxed_order: may be slightly out of order, but improves recall
SET hnsw.iterative_scan = strict_order;
SET hnsw.iterative_scan = relaxed_order;
SET ivfflat.iterative_scan = relaxed_order;Make relaxed results strictly ordered
- Use a materialized CTE to re-sort:
WITH relaxed_results AS MATERIALIZED (
SELECT id, embedding <-> '[1,2,3]' AS distance
FROM items
WHERE category_id = 123
ORDER BY distance
LIMIT 5
)
SELECT * FROM relaxed_results
ORDER BY distance + 0;Note: the README mentions + 0 is needed for Postgres 17+.
Filter by distance efficiently
- Put the distance filter outside a materialized 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;Tuning knobs for “scan more”
HNSW
-- scan more candidates (recall ↑, latency ↑)
SET hnsw.ef_search = 200;
-- stop conditions
SET hnsw.max_scan_tuples = 20000;
SET hnsw.scan_mem_multiplier = 2;Notes:
hnsw.max_scan_tuplesis approximate and does not affect the initial scan.- If increasing
hnsw.max_scan_tuplesdoesn’t help recall, try increasinghnsw.scan_mem_multiplier.
IVFFlat
SET ivfflat.max_probes = 100;Note: if ivfflat.max_probes is lower than ivfflat.probes, ivfflat.probes is used.
Production performance workflow
- Bulk load with
COPY, add indexes after loading. - Use
EXPLAIN (ANALYZE, BUFFERS)to validate index usage and latency. - Prefer
SET LOCALinside transactions to tune per-query.
General performance notes
Postgres tuning
- The README suggests using tools like PgTune for initial server settings.
- Useful inspection queries:
SHOW config_file;
SHOW shared_buffers;Loading and indexing
- Bulk load with
COPYwhen possible; create ANN indexes after the initial load. - In production, consider concurrent index creation to avoid blocking writes:
CREATE INDEX CONCURRENTLY ...Query performance debugging
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM items
ORDER BY embedding <-> '[3,1,2]'
LIMIT 5;Exact search (no ANN index)
- Increase parallelism for large scans:
SET max_parallel_workers_per_gather = 4;- If vectors are normalized to length 1, the README suggests inner product for best performance:
SELECT * FROM items ORDER BY embedding <#> '[3,1,2]' LIMIT 5;IVFFlat speed vs recall
- Increasing
listscan improve speed (and can reduce recall):
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000);Vacuuming with HNSW
- Vacuum can be slow; the README suggests reindexing first:
REINDEX INDEX CONCURRENTLY index_name;
VACUUM table_name;Monitoring
- Use
pg_stat_statements(requiresshared_preload_libraries):
CREATE EXTENSION pg_stat_statements;- Compare ANN results with exact results to monitor recall:
BEGIN;
SET LOCAL enable_indexscan = off;
SELECT ...;
COMMIT;Scaling
- Scale the same way you scale Postgres: vertically (bigger instance) or horizontally (replicas / sharding approaches like Citus).
Python (pgvector-python)
Upstream: https://github.com/pgvector/pgvector-python
Install
pip install pgvector
Django integration
Enable extension via migrations
- Use
VectorExtension()migration operation.
Fields
VectorField(dimensions=n)- Also:
HalfVectorField,BitField,SparseVectorField
Query patterns
- Order by distance:
L2Distance,MaxInnerProduct,CosineDistance,L1Distance,HammingDistance,JaccardDistance- Filter by distance:
- compute/alias distance and filter (e.g.
< 5)
Approximate indexes (Django)
- Use
HnswIndex(...)/IvfflatIndex(...)inMeta.indexes. - Specify
opclasses(examples in README usevector_l2_opsand mentionvector_ip_ops,vector_cosine_ops).
Half-precision indexing (Django)
- Use expression + opclass:
- cast field to
HalfVectorField(dimensions=n)and usehalfvec_*_ops.
SQLAlchemy integration
Enable extension
CREATE EXTENSION IF NOT EXISTS vector
Column types
VECTOR(n)- Also:
HALFVEC,BIT,SPARSEVEC
Query patterns
- Column methods for distance:
.l2_distance(...),.max_inner_product(...),.cosine_distance(...),.l1_distance(...),.hamming_distance(...),.jaccard_distance(...)
Approximate indexes (SQLAlchemy)
- Create an
Index(..., postgresql_using='hnsw'|'ivfflat'). - Provide:
postgresql_with(e.g.{'m': 16, 'ef_construction': 64}or{'lists': 100})postgresql_opsmapping column name to operator class (e.g.'vector_l2_ops').
Advanced: expression indexing
- Half precision: index
cast(column, HALFVEC(n))withhalfvec_*_ops. - Binary quantization: index
cast(binary_quantize(column), BIT(n))withbit_hamming_ops, then re-rank with original vectors.
Other supported DB libraries (README list)
- SQLModel, Psycopg 3, Psycopg 2, asyncpg, pg8000, Peewee
Examples directory (what to look for)
The upstream repo has examples/ covering common patterns and integrations, including:
loading(bulk loading patterns)hybrid_search(hybrid retrieval patterns)sparse_search(sparse vectors)rag(RAG-style workflows)openai,cohere,sentence_transformers(embedding generation + storage)image_search,imagehash(image similarity / hashing)citus(distributed/sharded Postgres example)
Swift (pgvector-swift)
Upstream: https://github.com/pgvector/pgvector-swift
Supported clients (README)
- PostgresNIO
- PostgresClientKit
PostgresNIO
- Add
pgvector-swifttoPackage.swiftand include products: PgvectorPgvectorNIO
Key steps:
- Enable extension:
CREATE EXTENSION IF NOT EXISTS vector - Register types:
PgvectorNIO.registerTypes(client)- Use
Vector([Float])values for interpolation in query strings.
PostgresClientKit
- Add products:
PgvectorPgvectorClientKit
- Use prepared statements and pass
Vector([..])as parameter values. - Decode vectors using
columns[i].vector().
Indexing
- HNSW / IVFFlat examples match the core extension:
CREATE INDEX ... USING hnsw (embedding vector_l2_ops)CREATE INDEX ... USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)
Reference: vector types
Vector,HalfVector,SparseVector- Sparse vector note: indices start at 0 in the Swift API.
Troubleshooting
Query isn’t using an ANN index
Checklist:
- Query must have both
ORDER BYandLIMIT. ORDER BYmust be the _distance operator result_ (not a derived expression) in ascending order.
Example:
-- index-friendly
SELECT *
FROM items
ORDER BY embedding <=> '[3,1,2]'
LIMIT 5;
-- not index-friendly
SELECT *
FROM items
ORDER BY 1 - (embedding <=> '[3,1,2]') DESC
LIMIT 5;Planner nudge (use per-query):
BEGIN;
SET LOCAL enable_seqscan = off;
SELECT ...;
COMMIT;Also: on small tables, a sequential scan can be legitimately faster.
Query isn’t using a parallel table scan
The README notes the planner may underestimate the cost of out-of-line storage (TOAST). Options:
- Adjust cost thresholds per-query:
BEGIN;
SET LOCAL min_parallel_table_scan_size = 1;
SET LOCAL parallel_setup_cost = 1;
SELECT ...;
COMMIT;- Or store vectors inline:
ALTER TABLE items ALTER COLUMN embedding SET STORAGE PLAIN;Fewer results after adding an HNSW index
- Often limited by
hnsw.ef_search(default 40) and/or filtering and dead tuples. - Enable iterative scans to scan more when filters prevent enough matches.
- Remember:
NULLvectors are not indexed; for cosine distance, zero vectors are not indexed.
Fewer results after adding an IVFFlat index
- Common cause: created with too little data for the chosen
lists. - Drop and recreate later:
DROP INDEX index_name;- Can also be limited by
ivfflat.probes; iterative scans can help. - Remember:
NULLvectors are not indexed; for cosine distance, zero vectors are not indexed.
Types, operators, functions
This reference focuses on practical type choices, indexing implications, and a minimal operator/function checklist.
Types at a glance
vector: float32 elementshalfvec: float16 elements (smaller storage, and can be indexed at half precision)bit: binary vectors (useful for hashes/quantization)sparsevec: sparse vectors (index/value map + dimensions)
Indexing dimension limits called out in the README:
vector: up to 2,000 dims for ANN indexeshalfvec: up to 4,000 dims for ANN indexesbit: up to 64,000 dims for ANN indexessparsevec: up to 1,000 non-zero elements for ANN indexes
vector basics
- Fixed dims:
vector(n) - Variable dims:
vector(indexes usually require expression + partial indexes per dimension)
Storage + validity
- README notes: roughly
4 * dimensions + 8bytes. - Elements must be finite (no NaN/Infinity).
Common operators
- Element-wise:
+,-,*, concatenation|| - Distances:
<->,<#>,<=>,<+>
Common functions
vector_dims(v)vector_norm(v)l2_normalize(v)subvector(v, start, count)l2_distance(a, b),inner_product(a, b),cosine_distance(a, b),l1_distance(a, b)
Aggregates
avg(v)sum(v)
Half precision (halfvec)
Store half-precision vectors
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding halfvec(3)
);Index at half precision
- Smaller indexes by indexing via a cast:
CREATE INDEX ON items
USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);Query
SELECT *
FROM items
ORDER BY embedding::halfvec(3) <-> '[1,2,3]'
LIMIT 5;Storage + validity
- README notes: roughly
2 * dimensions + 8bytes. - Elements must be finite (no NaN/Infinity).
Operator class reminder
- Use
halfvec_*_opsforhalfvec - Use
sparsevec_*_opsforsparsevec - Use
bit_*_opsforbit
Binary vectors (bit) and quantization
Store binary vectors
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding bit(3)
);
INSERT INTO items (embedding) VALUES ('000'), ('111');Query
- Hamming distance:
<~> - Jaccard distance:
<%>
SELECT * FROM items ORDER BY embedding <~> '101' LIMIT 5;Binary quantization workflow
- Use expression indexing on
binary_quantize(...)and then re-rank with original vectors:
CREATE INDEX ON items
USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);
SELECT * FROM (
SELECT *
FROM items
ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]')
LIMIT 20
) t
ORDER BY embedding <=> '[1,-2,3]'
LIMIT 5;Sparse vectors (sparsevec)
Store sparse vectors
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding sparsevec(5)
);Insert format
- Format:
{index:value,...}/dimensions - Indices start at 1 (like SQL arrays)
INSERT INTO items (embedding)
VALUES ('{1:1,3:2,5:3}/5'), ('{1:4,3:5,5:6}/5');Query
SELECT *
FROM items
ORDER BY embedding <-> '{1:3,3:1,5:2}/5'
LIMIT 5;Storage + validity
- README notes: roughly
8 * non-zero elements + 16bytes. - Elements must be finite (no NaN/Infinity).