Class: Zvec::RubyLLM::Store

Inherits:
Object
  • Object
show all
Defined in:
lib/zvec/ruby_llm.rb

Overview

A vector store backend for the ruby_llm gem.

Provides a simple add/search/delete interface on top of a Collection. Compatible with the ruby_llm vector store protocol.

Examples:

Basic usage

store = Zvec::RubyLLM::Store.new("/path/to/db", dimension: 1536)
store.add("doc-1", embedding: [...], content: "Hello world")
results = store.search([0.1, 0.2, ...], top_k: 5)
results.first  #=> { id: "doc-1", score: 0.98, content: "Hello world", metadata: {} }

With metadata

store.add("doc-2", embedding: [...], content: "Ruby", metadata: { category: "lang" })

Constant Summary collapse

DEFAULT_VECTOR_FIELD =

Returns default vector field name.

Returns:

  • (String)

    default vector field name

"embedding"
DEFAULT_CONTENT_FIELD =

Returns default content field name.

Returns:

  • (String)

    default content field name

"content"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, dimension:, metric: :cosine, vector_field: DEFAULT_VECTOR_FIELD, content_field: DEFAULT_CONTENT_FIELD) ⇒ Store

Create a new store, opening an existing collection or creating one.

Examples:

store = Zvec::RubyLLM::Store.new("/tmp/store", dimension: 384, metric: :l2)

Parameters:

  • path (String)

    directory path for the collection data

  • dimension (Integer)

    the vector dimension (must be > 0)

  • metric (Symbol) (defaults to: :cosine)

    similarity metric (+:cosine+, :l2, or :ip)

  • vector_field (String) (defaults to: DEFAULT_VECTOR_FIELD)

    name of the vector field (default: "embedding")

  • content_field (String) (defaults to: DEFAULT_CONTENT_FIELD)

    name of the content field (default: "content")

Raises:

  • (ArgumentError)

    if metric is not one of :cosine, :l2, :ip



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/zvec/ruby_llm.rb', line 41

def initialize(path, dimension:, metric: :cosine, vector_field: DEFAULT_VECTOR_FIELD,
               content_field: DEFAULT_CONTENT_FIELD)
  @vector_field = vector_field.to_s
  @content_field = content_field.to_s
  @dimension = dimension

  metric_type = case metric.to_sym
                when :cosine then Zvec::DataTypes::COSINE
                when :l2     then Zvec::DataTypes::L2
                when :ip     then Zvec::DataTypes::IP
                else raise ArgumentError, "Unknown metric: #{metric}"
                end

  cf = @content_field
  vf = @vector_field
  dim = dimension
  schema = Zvec::Schema.new("ruby_llm_store") do
    string cf, nullable: true
    vector vf, dimension: dim,
           index: Zvec::Ext::HnswIndexParams.new(metric_type)
  end

  @schema = schema

  if Dir.exist?(path)
    @collection = Zvec::Collection.open(path)
  else
    @collection = Zvec::Collection.create_and_open(path, schema)
  end
end

Instance Attribute Details

#collectionZvec::Collection (readonly)

Returns the underlying collection.

Returns:



26
27
28
# File 'lib/zvec/ruby_llm.rb', line 26

def collection
  @collection
end

#dimensionInteger (readonly)

Returns the vector dimension.

Returns:

  • (Integer)

    the vector dimension



28
29
30
# File 'lib/zvec/ruby_llm.rb', line 28

def dimension
  @dimension
end

Instance Method Details

#add(id, embedding:, content: nil, metadata: {}) ⇒ Array

Add a document with its embedding and optional metadata.

Examples:

store.add("doc-1", embedding: [0.1, 0.2, 0.3], content: "Hello")

Parameters:

  • id (String, Integer)

    the document's primary key

  • embedding (Array<Numeric>)

    the vector embedding

  • content (String, nil) (defaults to: nil)

    optional text content

  • metadata (Hash{String, Symbol => Object}) (defaults to: {})

    additional fields to store

Returns:

  • (Array)

    write results from the collection



82
83
84
85
86
87
88
# File 'lib/zvec/ruby_llm.rb', line 82

def add(id, embedding:, content: nil, metadata: {})
  doc = Zvec::Doc.new(pk: id, schema: @schema)
  doc[@vector_field] = embedding
  doc[@content_field] = content if content
  .each { |k, v| doc[k] = v }
  @collection.insert(doc)
end

#add_many(docs) ⇒ Array

Batch-add multiple documents at once.

Examples:

store.add_many([
  { id: "a", embedding: [0.1, 0.2], content: "Hello" },
  { id: "b", embedding: [0.3, 0.4], content: "World" },
])

Parameters:

  • docs (Array<Hash>)

    documents, each containing:

    • :id [String, Integer] -- primary key (required)
    • :embedding [Array] -- the vector (required)
    • :content [String, nil] -- optional text content
    • :metadata [Hash, nil] -- optional additional fields

Returns:

  • (Array)

    write results from the collection



104
105
106
107
108
109
110
111
112
113
# File 'lib/zvec/ruby_llm.rb', line 104

def add_many(docs)
  zvec_docs = docs.map do |d|
    doc = Zvec::Doc.new(pk: d[:id], schema: @schema)
    doc[@vector_field] = d[:embedding]
    doc[@content_field] = d[:content] if d[:content]
    (d[:metadata] || {}).each { |k, v| doc[k] = v }
    doc
  end
  @collection.insert(zvec_docs)
end

#countInteger

Return the number of documents in the store.

Returns:

  • (Integer)


175
176
177
# File 'lib/zvec/ruby_llm.rb', line 175

def count
  @collection.doc_count
end

#delete(*ids) ⇒ Array

Delete documents by primary key(s).

Parameters:

  • ids (Array<String, Integer>)

    one or more primary keys

Returns:

  • (Array)

    write results from the collection



153
154
155
# File 'lib/zvec/ruby_llm.rb', line 153

def delete(*ids)
  @collection.delete(*ids.flatten)
end

#fetch(*ids) ⇒ Hash{String => Zvec::Doc}

Fetch documents by primary key(s).

Parameters:

  • ids (Array<String, Integer>)

    one or more primary keys

Returns:

  • (Hash{String => Zvec::Doc})

    mapping of pk to document



161
162
163
# File 'lib/zvec/ruby_llm.rb', line 161

def fetch(*ids)
  @collection.fetch(*ids.flatten)
end

#flushself

Flush pending writes to disk.

Returns:

  • (self)


168
169
170
# File 'lib/zvec/ruby_llm.rb', line 168

def flush
  @collection.flush
end

#search(query_vector, top_k: 10, filter: nil) ⇒ Array<Hash>

Search for similar vectors.

Examples:

results = store.search([0.1, 0.2, 0.3], top_k: 5)
results.first[:id]      #=> "doc-1"
results.first[:score]   #=> 0.95
results.first[:content] #=> "Hello"

Parameters:

  • query_vector (Array<Numeric>)

    the query vector

  • top_k (Integer) (defaults to: 10)

    maximum number of results (default: 10)

  • filter (String, nil) (defaults to: nil)

    optional filter expression (see VectorQuery for filter syntax)

Returns:

  • (Array<Hash>)

    results, each containing:

    • :id [String] -- document primary key
    • :score [Float] -- similarity score
    • :content [String, nil] -- the content field value
    • :metadata [Hash] -- all other stored fields


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/zvec/ruby_llm.rb', line 132

def search(query_vector, top_k: 10, filter: nil)
  results = @collection.query(
    field_name: @vector_field,
    vector: query_vector,
    topk: top_k,
    filter: filter
  )
  results.map do |doc|
    {
      id: doc.pk,
      score: doc.score,
      content: doc[@content_field],
      metadata: doc.to_h.reject { |k, _| ["pk", "score", @vector_field, @content_field].include?(k) }
    }
  end
end