pgvector 0.7 separates vector storage from search representation
Half-precision vectors, sparse vectors and binary indexing expand pgvector's representation choices. Expression indexes let candidate search use a smaller representation while retaining the original vector.
An embedding's stored representation does not have to be the representation used to find search candidates. That is the most useful design choice in pgvector 0.7.0, released on April 29. The release adds half-precision and sparse vector types, indexing for binary vectors and a binary quantization function. PostgreSQL expression indexes connect those additions to data already stored in a table.
The result is a set of choices about precision and candidate retrieval. A table can retain its original vector while an index stores a half-precision conversion. Another index can search binary representations and return candidates for a second ordering using the original values. The new types are useful on their own, but the separation between stored data and indexed expressions makes them more flexible.
I like this use of PostgreSQL's extension interface. A vector-search change can use the database's existing expression-index machinery while the original relational record remains available to the query. The design makes the representation used for search visible in SQL, where its relationship to the final ordering can be inspected.
Half precision can belong to the index
The tagged 0.7.0 documentation introduces halfvec for half-precision vectors. An application can declare a column with that type, but it can also retain a vector column and cast it within an index expression. The documentation presents that second arrangement as a way to obtain smaller indexes and faster builds.
The following example uses a small dimension count to show the conversion. The table stores ordinary vectors, and the index definition chooses half precision.
CREATE EXTENSION vector;
CREATE TABLE items (
id bigserial PRIMARY KEY,
embedding vector(3)
);
CREATE INDEX items_half_l2 ON items
USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
SELECT id FROM items
ORDER BY embedding::halfvec(3) <-> '[1,2,3]'
LIMIT 5;
The query orders by the same cast expression that the index stores. That agreement matters because the index describes distances over the half-precision representation. Retaining the full-precision column does not make an ordering on the cast equivalent to an ordering on the original values. The conversion is part of the search definition.
This arrangement makes a useful operational distinction. Changing the representation in an index need not require replacing the source column with a less precise type. An application can preserve its stored vectors for other computations or for a later ranking step. Storage policy and candidate-search policy can therefore be expressed separately within the schema.
The indexed dimension limits also differ. Version 0.7.0 documents HNSW support for vector up to 2,000 dimensions and halfvec up to 4,000. The half-precision option is consequently relevant to which vectors can be indexed, as well as to index size. Those are index limits, and should not be confused with a claim that every supported type has the same storage or indexing capacity.
Binary search changes the distance being measured
Binary vectors use PostgreSQL's bit type. The release adds Hamming distance and Jaccard distance functions, along with corresponding HNSW operator classes. Hamming distance uses <~>, while Jaccard distance uses <%>. These operators give binary vectors their own search semantics instead of treating them as ordinary floating-point vectors with a different display format.
The new binary_quantize function lets an index derive a binary representation from an existing vector. The binary quantization example uses an expression index with an explicit bit cast and the Hamming operator class. An explicit cast gives the indexed expression its dimension, while the operator class states the distance supported by that index.
For the earlier table, a binary candidate index can be defined independently of the half-precision index. Its definition makes the transformation visible.
CREATE INDEX items_binary_hamming ON items
USING hnsw (
(binary_quantize(embedding)::bit(3)) bit_hamming_ops
);
The new HNSW support allows binary vectors up to 64,000 dimensions. IVFFlat also gains binary indexing with Hamming distance. That does not mean every combination of access method and new distance is available. In particular, the documentation lists Jaccard support under HNSW, while the IVFFlat examples add Hamming distance. The access method and operator class remain explicit parts of the index definition.
The search is over the binary expression, so its candidate order is a Hamming-distance order. A later comparison against original floating-point values is a distinct computation. Treating the binary distance as an interchangeable approximation of every original distance function would conceal the decision the SQL actually makes.
Reranking operates within the retrieved candidates
The documentation follows binary retrieval with an outer query that reorders candidates by their original vectors. Its example retrieves 20 candidates by Hamming distance and then returns five by cosine distance. Those counts demonstrate a query structure. They do not establish a recommended candidate multiplier for every dataset.
The same structure can be written with a named subquery so that each ordering has an explicit input. Both query vectors in this example represent the same requested point.
SELECT id FROM (
SELECT id, embedding FROM items
ORDER BY binary_quantize(embedding)::bit(3)
<~> binary_quantize('[1,-2,3]'::vector)
LIMIT 20
) AS candidates
ORDER BY embedding <=> '[1,-2,3]'
LIMIT 5;
The inner limit defines which records remain available to the outer query. Reordering those records by cosine distance can improve the ordering relative to using Hamming distance alone. A record absent from the candidate set cannot reappear during that outer ordering. This follows directly from the query's structure and explains why retrieval depth remains part of the design.
There are consequently two approximations to consider when using this pattern with HNSW. The candidate search uses a binary representation, and HNSW itself performs approximate nearest-neighbour retrieval. The outer query then evaluates the original distance for the rows it receives. Describing the last step as exact reranking is meaningful within that candidate set, but it does not establish exact nearest neighbours over the whole table.
I prefer keeping these operations explicit. The SQL shows which representation narrows the records and which distance decides the final order. That makes an application-level judgement about acceptable search results more precise than a general claim that a smaller index preserves the original search behavior.
Sparse vectors have a different size constraint
The sparse-vector documentation introduces sparsevec. Its text representation records nonzero entries as index and value pairs followed by the total dimension count. Indices start at one, consistent with the documented SQL-array convention. This lets the value express a vector without writing every zero explicitly.
For example, '{1:1,3:2,5:3}/5' describes a five-dimensional vector with entries at positions one, three and five. The documentation uses sparsevec(5) for the column and demonstrates L2 nearest-neighbour queries. The type describes sparsity in the stored value, while the selected distance operator still defines what proximity means for the query.
HNSW's documented sparse limit is 1,000 nonzero elements. It is expressed in a different unit from the dimension limits for dense and binary vectors. A schema review therefore needs to preserve the distinction between total vector dimensionality and the number of populated entries. Calling all of these limits a single maximum embedding size would lose information needed to select a representation.
Expression indexes also support subvectors
Version 0.7.0 adds subvector, and the documentation shows indexing a slice of a vector with an expression index. It follows the same structure as binary retrieval by reranking candidates against the full vectors. That gives another way to separate a candidate representation from the values used in the final comparison, without changing the stored source vector.
The release also adds L1 indexing with HNSW and normalization and concatenation operations. These additions extend what can be expressed around a vector, but they do not remove the distinction between exact and approximate search. pgvector still documents exact nearest-neighbour search as its default, with approximate indexes trading recall for speed. The new representations give that tradeoff more explicit forms in the index definition and in the query that consumes its candidates.
The tagged troubleshooting guidance also requires an indexable ordering to use the distance operator directly with a limit. Rewriting cosine distance as one minus distance and sorting descending may look equivalent as a ranking expression, but the documentation distinguishes it from the indexable operator form. This is especially relevant when an application exposes similarity scores while the index operates on distance. The score can be useful to the caller without becoming the expression used for indexed ordering.
Filtering adds another choice around these representations. The README describes ordinary indexes on filter columns for exact search and partial vector indexes for approximate search restricted to a particular category. It also describes partitioning for many filter values. A smaller vector representation does not decide which records belong in the searchable collection. That membership remains part of the relational query and the index definition, alongside the chosen transformation and distance function.