Class: Phronomy::VectorStore::Pgvector

Inherits:
Base
  • Object
show all
Defined in:
lib/phronomy/vector_store/pgvector.rb

Overview

PostgreSQL-backed vector store using the pgvector extension.

Requires:

  • The +pgvector+ gem (add to your Gemfile)
  • An ActiveRecord model class with the following columns: id (string / uuid) embedding (vector — from the pgvector column type) metadata (text or jsonb — stores arbitrary metadata as JSON)

Examples:

Usage

store = Phronomy::VectorStore::Pgvector.new(model_class: VectorDocument)
store.add(id: "doc1", embedding: [0.1, 0.9], metadata: {text: "hello"})
results = store.search(query_embedding: [0.1, 0.8], k: 5)

Instance Method Summary collapse

Constructor Details

#initialize(model_class:, dimension: nil) ⇒ Pgvector

Returns a new instance of Pgvector.

Parameters:

  • model_class (Class)

    ActiveRecord model with id/embedding/metadata columns

  • dimension (Integer, nil) (defaults to: nil)

    expected embedding dimension for Phronomy-side pre-validation. When nil, dimension enforcement is delegated to the database schema; no pre-validation is performed by Phronomy.



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/phronomy/vector_store/pgvector.rb', line 26

def initialize(model_class:, dimension: nil)
  begin
    require "pgvector"
  rescue LoadError
    raise LoadError,
      "pgvector gem is required for Phronomy::VectorStore::Pgvector. " \
      "Add `gem 'pgvector'` to your Gemfile."
  end
  @model_class = model_class
  @dimension = dimension
end

Instance Method Details

#add(id:, embedding:, metadata: {}, cancellation_token: nil) ⇒ Object

Parameters:

  • id (String)
  • embedding (Array<Float>)
  • metadata (Hash) (defaults to: {})
  • cancellation_token (Phronomy::CancellationToken, nil) (defaults to: nil)


43
44
45
46
47
48
49
50
51
# File 'lib/phronomy/vector_store/pgvector.rb', line 43

def add(id:, embedding:, metadata: {}, cancellation_token: nil)
  cancellation_token&.raise_if_cancelled!
  validate_embedding_dimension!(embedding, @dimension)
  @model_class.upsert(
    {id: id, embedding: safe_vector(embedding), metadata: .to_json},
    unique_by: :id
  )
  self
end

#clearObject



84
85
86
87
# File 'lib/phronomy/vector_store/pgvector.rb', line 84

def clear
  @model_class.delete_all
  self
end

#remove(id:) ⇒ Object



79
80
81
82
# File 'lib/phronomy/vector_store/pgvector.rb', line 79

def remove(id:)
  @model_class.where(id: id).delete_all
  self
end

#search(query_embedding:, k: 5, cancellation_token: nil) ⇒ Array<Hash>

Returns sorted by descending similarity score.

Parameters:

Returns:

  • (Array<Hash>)

    sorted by descending similarity score



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/phronomy/vector_store/pgvector.rb', line 58

def search(query_embedding:, k: 5, cancellation_token: nil)
  cancellation_token&.raise_if_cancelled!
  k_safe = validate_k!(k)
  validate_embedding_dimension!(query_embedding, @dimension)
  vec = safe_vector_literal(query_embedding)
  conn = @model_class.connection
  quoted_vec = "#{conn.quote(vec)}::vector"

  @model_class
    .select("id, metadata, 1 - (embedding <=> #{quoted_vec}) AS score")
    .order("embedding <=> #{quoted_vec}")
    .limit(k_safe)
    .map do |r|
      {
        id: r.id.to_s,
        score: r.score.to_f,
        metadata: (r.)
      }
    end
end

#sizeObject

Returns the number of documents in the backing table.



90
91
92
# File 'lib/phronomy/vector_store/pgvector.rb', line 90

def size
  @model_class.count
end