pgvector stores and searches vector data inside PostgreSQL. Its distance operators are what turn a table of embeddings into a nearest neighbor search, and each operator uses a different definition of "close" in vector space.
pgvector supports four distance functions:
Each one maps to a single operator: <->, <#>, <=>, and <+>.
<-> for Euclidean (L2) distanceEuclidean distance is the straight-line distance between two points in vector space. In pgvector, the <-> distance operator returns it.
Here's an example query:
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;This query will return the 5 nearest neighbors to the vector [3,1,2] based on Euclidean distance.
<#> returns the negative inner productInner product measures similarity by multiplying the corresponding elements of two vectors and summing the results. In pgvector, you use the <#> operator for inner product. It gives you the negative inner product, because Postgres only supports ASC order index scans on operators.
Here's how you'd use it:
SELECT * FROM items ORDER BY embedding <#>Occasional notes on software, tools, and things I learn. No spam.
Unsubscribe anytime.
To get the actual inner product, you'd need to multiply the result by -1:
SELECT (embedding <#> '[3,1,2]') * -1 AS inner_product FROM items;Cosine distance compares the direction of two vectors and ignores their magnitude, which is what you want for text embeddings. In pgvector, you use the <=> operator for cosine distance.
Here's an example:
SELECT * FROM items ORDER BY embedding <=> '[3,1,2]' LIMIT 5;Cosine distance and cosine similarity are not the same number. Distance is 1 minus similarity, so a distance of 0 means the two vectors point in the same direction. For cosine similarity, subtract the distance from 1:
SELECT 1 - (embedding <=> '[3,1,2]') AS cosine_similarity FROM items;Taxicab distance, also known as Manhattan distance or L1 distance, is the sum of the absolute differences of the coordinates. The name comes from travel on a street grid, where you can only move along the axes. In pgvector, you use the <+> operator for taxicab distance.
Here's how to use it:
SELECT * FROM items ORDER BY embedding <+> '[3,1,2]' LIMIT 5;Indexing: To speed up your queries, you can create indexes for each distance function you want to use. For example:
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
CREATE INDEX ON items USING hnsw (embedding vector_ip_ops);
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);Normalization: If your vectors are normalized to length 1 (like OpenAI embeddings), use inner product for best performance.
Dimensions: pgvector supports up to 2,000 dimensions by default. If you need more, you can use half-precision indexing for up to 4,000 dimensions or binary quantization for up to 64,000 dimensions.
Approximate vs Exact Search: By default, pgvector performs exact nearest neighbor search. You can add an HNSW or IVFFlat index to use approximate nearest neighbor search, which trades some recall for speed.
Query Performance: Use EXPLAIN ANALYZE to debug performance issues. You can also increase max_parallel_workers_per_gather to speed up queries without an index.
Filtering: You can combine vector search with other conditions. For example:
SELECT * FROM items WHERE category_id = 123 ORDER BY embedding <-> '[3,1,2]' LIMIT 5;pgvector provides four main similarity search functions:
<->)<#>)<=>)<+>)Each has its use case and can be optimized with appropriate indexing. Remember to consider normalization, dimensions, and performance when working with vector embeddings in Postgres. For the full TypeScript and Drizzle ORM setup, see how to store vector embeddings in Postgres with Drizzle ORM.