rent
PostgreSQL extensions

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_pgvector

Set 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.

On this page