pgvector-go

repository·master·Indexed 43 days ago

https://github.com/pgvector/pgvector-go

A Go client library for the pgvector extension in PostgreSQL. It provides support for dense, half, and sparse vectors, enabling vector similarity searches. The library includes integrations for pgx, pg, Bun, Ent, GORM, and sqlx, and supports various distance metrics including L2, Inner Product, Cosine, Manhattan, Hamming, and Jaccard distances.

Tokens
6.7K
Snippets
26
Records
30
Agent score
56%

What's inside pgvector-go

  1. Configure pgvector index operators

    master

    When creating approximate indexes (HNSW or IVFFlat) in PostgreSQL, you must specify the correct operator class based on the distance metric you intend to use:

    • L2 Distance (Euclidean): Use vector_l2_ops.
    • Inner Product: Use vector_ip_ops.
    • Cosine Distance: Use vector_cosine_ops.

    Example SQL for HNSW index: CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)

  2. Use pgvector-go with sqlx

    master

    To use pgvector-go with sqlx:

    1. Install: go get github.com/pgvector/pgvector-go
    2. Define Model: Use the pgvector.Vector type in your struct.
    3. Operations: Use standard NamedExec or Select methods. Pass pgvector.NewVector as a parameter in your SQL queries.
    type Item struct {
        Embedding pgvector.Vector
    }
    
    // Insert
    item := Item{
        Embedding: pgvector.NewVector([]float32{1, 2, 3}),
    }
    _, err := db.NamedExec(`INSERT INTO items (embedding) VALUES (:embedding)`, item)
    
    // Query
    var items []Item
    db.Select(&items, "SELECT * FROM items ORDER BY embedding <-> $1 LIMIT 5", pgvector.NewVector([]float32{1, 1, 1}))
  3. Use pgvector-go with GORM

    master

    To use pgvector-go with GORM:

    1. Install: go get github.com/pgvector/pgvector-go
    2. Define Model: Use the pgvector.Vector type with the gorm:"type:vector(N)" tag.
    3. Querying: Use db.Clauses with clause.OrderBy to pass the distance operator and the pgvector.NewVector value as a parameter.
    type Item struct {
        Embedding pgvector.Vector `gorm:"type:vector(3)"`
    }
    
    // Query nearest neighbors
    var items []Item
    db.Clauses(clause.OrderBy{
        Expression: clause.Expr{SQL: "embedding <-> ?", Vars: []interface{}{pgvector.NewVector([]float32{1, 1, 1})}},
    }).Limit(5).Find(&items)
  4. Use pgvector-go with Bun

    master

    To use pgvector-go with the Bun ORM:

    1. Install: go get github.com/pgvector/pgvector-go
    2. Define Model: Use the pgvector.Vector type with the bun:"type:vector(N)" tag.
    3. Querying: Use OrderExpr with the distance operator (e.g., <->) to find nearest neighbors.
    4. Indexing: You can implement the bun.AfterCreateTableHook to automatically create HNSW or IVFFlat indexes during table creation.
    type Item struct {
        Embedding pgvector.Vector `bun:"type:vector(3)"`
    }
    
    // Insert
    item := Item{
        Embedding: pgvector.NewVector([]float32{1, 2, 3}),
    }
    _, err := db.NewInsert().Model(&item).Exec(ctx)
    
    // Query
    var items []Item
    err := db.NewSelect().
        Model(&items).
        OrderExpr("embedding <-> ?", pgvector.NewVector([]float32{1, 2, 3})).
        Limit(5).
        Scan(ctx)
  5. Setup pgvector-go with pgx

    master

    To use pgvector-go with the pgx driver, install both the core package and the pgx sub-package. You must register the vector types with your connection or connection pool to enable seamless type mapping between Go and PostgreSQL.

    1. Install dependencies:
      go get github.com/pgvector/pgvector-go
      go get github.com/pgvector/pgvector-go/pgx
    2. Import packages:
      import (
          "github.com/pgvector/pgvector-go"
          pgxvec "github.com/pgvector/pgvector-go/pgx"
      )
    3. Enable the extension and register types: Execute CREATE EXTENSION IF NOT EXISTS vector and call pgxvec.RegisterTypes(ctx, conn) on your connection. For connection pools, use the AfterConnect configuration hook.
    // Register with a single connection
    err := pgxvec.RegisterTypes(ctx, conn)
    
    // Register with a connection pool
    config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
        return pgxvec.RegisterTypes(ctx, conn)
    }
  6. Use pgvector-go with pg

    master

    To use pgvector-go with the pg driver:

    1. Install: go get github.com/pgvector/pgvector-go
    2. Enable extension: Execute CREATE EXTENSION IF NOT EXISTS vector.
    3. Define Model: Use the pgvector.Vector type in your struct with the pg:"type:vector(N)" tag, where N is the dimension.
    4. Operations: Use pgvector.NewVector([]float32{...}) for insertions and OrderExpr for distance-based queries.
    type Item struct {
        Embedding pgvector.Vector `pg:"type:vector(3)"`
    }
    
    // Insert
    item := Item{
        Embedding: pgvector.NewVector([]float32{1, 2, 3}),
    }
    _, err := db.Model(&item).Insert()
    
    // Query nearest neighbors
    var items []Item
    err := db.Model(&items).
        OrderExpr("embedding <-> ?", pgvector.NewVector([]float32{1, 2, 3})).
        Limit(5).
        Select()
  7. Use pgvector-go with Ent

    master

    To use pgvector-go with the Ent framework:

    1. Install: go get github.com/pgvector/pgvector-go and go get github.com/pgvector/pgvector-go/ent.
    2. Enable extension: Requires the sql/execquery feature flag in Ent. Execute CREATE EXTENSION IF NOT EXISTS vector via client.ExecContext.
    3. Define Schema: Use field.Other("name", pgvector.Vector{}) and set the SchemaType to dialect.Postgres: "vector(N)".
    4. Querying: Use the entvec package helper functions like entvec.L2Distance within an Order clause.
    5. Indexing: Use entsql.IndexType("hnsw") and entsql.OpClass("vector_l2_ops") in your Indexes() definition.
    // Schema definition
    func (Item) Fields() []ent.Field {
        return []ent.Field{
            field.Other("embedding", pgvector.Vector{}). 
                SchemaType(map[string]string{
                    dialect.Postgres: "vector(3)",
                }),
        }
    }
    
    // Querying
    items, err := client.Item.Query().
        Order(func(s *sql.Selector) {
            s.OrderExpr(entvec.L2Distance("embedding", pgvector.NewVector([]float32{1, 2, 3})))
        }).
        Limit(5).
        All(ctx)
  8. Reference: Vector types and creation

    master

    The pgvector package provides types for different vector formats supported by PostgreSQL.

    Standard Vectors

    Create a standard vector from a []float32: vec := pgvector.NewVector([]float32{1, 2, 3})

    Half Vectors

    Create a half vector (16-bit floats) from a []float32: vec := pgvector.NewHalfVector([]float32{1, 2, 3})

    Sparse Vectors

    Create a sparse vector using a slice or a map of non-zero elements. Note that indices start at 0.

    • From slice: pgvector.NewSparseVector([]float32{1, 0, 2, 0, 3, 0})
    • From map: pgvector.NewSparseVectorFromMap(map[int32]float32{0: 1, 2: 2, 4: 3}, 6)

    Common Methods

    • vec.Slice(): Returns the underlying slice.
    • vec.Dimensions(): Returns the number of dimensions.
    • vec.Indices(): Returns the indices of non-zero elements (for sparse vectors).
    • vec.Values(): Returns the values of non-zero elements (for sparse vectors).
    // Standard Vector
    vec := pgvector.NewVector([]float32{1, 2, 3})
    slice := vec.Slice()
    
    // Half Vector
    hvec := pgvector.NewHalfVector([]float32{1, 2, 3})
    
    // Sparse Vector
    elements := map[int32]float32{0: 1, 2: 2, 4: 3}
    svec := pgvector.NewSparseVectorFromMap(elements, 6)
    indices := svec.Indices()
    values := svec.Values()
  9. Register pgvector types with pgx

    master

    To use pgvector types (vector, halfvec, and sparsevec) with the pgx/v5 driver, you must call RegisterTypes. This function queries the database for the OIDs of the vector types and registers them in the connection's TypeMap. This enables seamless scanning and passing of vector types between Go and PostgreSQL.

    Note that vector is treated as a required type; if it is not found in the database, RegisterTypes will return an error. halfvec and sparsevec are registered only if they exist in the database.

    import (
    	"context"
    	"github.com/jackc/pgx/v5"
    	"github.com/pgvector/pgvector-go/pgx"
    )
    
    // ... setup connection ...
    
    err := pgx.RegisterTypes(ctx, conn)
    if err != nil {
    	// handle error
    }
  10. Create a SparseVector

    master

    Use NewSparseVector or NewSparseVectorFromMap to initialize a SparseVector. SparseVector is a wrapper that implements sql.Scanner and driver.Valuer, making it compatible with standard Go database interfaces.

    • NewSparseVector(vec []float32): Creates a sparse vector from a dense slice. It automatically filters out zero values and stores only non-zero elements.
    • NewSparseVectorFromMap(elements map[int32]float32, dim int32): Creates a sparse vector from a map where keys are indices and values are the non-zero elements. The indices are automatically sorted.
    import "github.com/pgvector/pgvector-go"
    
    // From a dense slice
    dense := []float32{1.0, 0.0, 2.0, 0.0}
    vec := pgvector.NewSparseVector(dense)
    
    // From a map of non-zero elements
    elements := map[int32]float32{
        1: 0.5,
        3: 1.2,
    }
    vecFromMap := pgvector.NewSparseVectorFromMap(elements, 4)
  11. Use the Vector type for dense vectors

    master

    The Vector type is a wrapper around []float32 designed to work seamlessly with SQL databases, JSON, and binary formats. It implements sql.Scanner and driver.Valuer, allowing you to use it directly in database queries using standard Go database/sql or compatible drivers like pgx.

    To create a vector, use NewVector. To access the underlying data, use Slice().

    import "github.com/pgvector/pgvector-go"
    
    vec := pgvector.NewVector([]float32{1.0, 2.0, 3.0})
    // Access underlying slice
    raw := vec.Slice()
  12. Use VectorCodec with pgx/v5

    master

    The VectorCodec struct provides a custom codec implementation for the pgx/v5 driver, allowing you to seamlessly scan and encode pgvector.Vector types. It supports both Binary and Text formats, preferring Binary for performance.

    To use it, you typically register it with your pgx type map so that the driver knows how to handle the vector OID from PostgreSQL.

    import (
    	"github.com/jackc/pgx/v5"
    	"github.com/jackc/pgx/v5/pgtype"
    	"github.com/pgvector/pgvector-go"
    	"github.com/pgvector/pgvector-go/pgx"
    )
    
    // Example of how you might register the codec in a pgx configuration
    func setupPgx(conn *pgx.Conn) {
    	ctx := context.Background()
    	typemap := conn.TypeMap()
    
    	// Note: You need the OID for the vector type from your database
    	// This is a conceptual example of registration
    	vectorOID := uint32(12345) 
    	typemap.RegisterType[pgvector.Vector](ctx, vectorOID, pgx.VectorCodec{})
    }