pgvector
What it is
pgvector adds vector columns, distance operators, and approximate-nearest-neighbor indexes to PostgreSQL. It lets relational records and machine-learning embeddings live and transact together.
What it provides
- Vector, half-vector, sparse-vector, and binary-vector storage
- Exact L2, inner-product, cosine, L1, Hamming, and Jaccard search
- HNSW and IVFFlat indexes for approximate nearest-neighbor queries
Use it for semantic search, recommendation systems, duplicate detection, image similarity, retrieval-augmented generation, or hybrid search that combines embeddings with ordinary filters.
Use it with Rent
extension vector {
name = "vector"
schema = "rent_native_codec"
}
model Post {
id BigInt @id
title String
embedding pgvector.Vector? @db.Vector(1536)
}Run rent generate, then review and apply the migration with rent migrate dev --name add_embeddings.
Add rent-ext-pgvector to your application's dependencies and enable Rent's postgres and extensions
features. The generated model holds Option<Vector>, not a string or JSON proxy.
use rent_ext_pgvector::Vector;
let query = Vector::new(embedding_service_output)?;
let posts = client
.post()
.order_by_embedding_cosine(&query)?
.limit(10)
.all()
.await?;Generated setters, filters, and distance ordering use native binary values and qualify extension operators
with the installation schema. The field's dimensions are checked before a write or filter reaches PostgreSQL.
Use .embedding(query) to set a value, .clear_embedding() to clear a nullable value, and
.select_embedding() to retrieve typed vectors. Vector distance ordering composes with ordinary filters,
eager loading, limits, and multi-field projections. A cosine query must have nonzero norm to define a useful
distance; choose L2 when zero vectors are meaningful in your application.
Query filters and distance ordering accept owned or borrowed vectors. Borrowing avoids copying the original component buffer; Rent encodes owned query parameters before returning the query builder, so the builder does not borrow your embedding across an await. Collection filters accept borrowed slices as well:
let candidates = [first_embedding, second_embedding];
let posts = client
.post()
.embedding_in(&candidates)?
.all()
.await?;Model field setters accept an owned or borrowed vector and store an owned copy. The generated native field type represents
pgvector's full-precision vector; other storage formats require explicit SQL and their own codecs.
Run the application
cargo run -p rent --example extension_04_pgvectorSet DATABASE_URL to a disposable PostgreSQL server with the vector, PostGIS, and citext packages available.
The example creates and removes its own database, so its role needs database-creation and extension-installation
privileges. It stores two posts, ranks them by cosine distance, and checks a typed title/vector projection.
The shared schema and readable workflow are in crates/rent/examples/native_extension_fields/.
The extension matrix runs the same workflow under nextest.