API Methods Reference

Complete reference for all Vectra API methods.

For a quick reference, see the API Cheatsheet.
For an overview, see the API Overview.
All methods link to source code on GitHub.

Client Methods

Vectra::Client.new(options)

Source

Initialize a new Vectra client.

Parameters:

Returns: Vectra::Client

Example:

client = Vectra::Client.new(
  provider: :qdrant,
  host: 'http://localhost:6333',
  api_key: ENV['QDRANT_API_KEY']
)

In a Rails app that uses the vectra:index generator, if config/vectra.yml contains exactly one entry, Vectra::Client.new will automatically use that entry’s index (and namespace if present) as its defaults. This allows you to omit index: in most calls (upsert, query, text_search, etc.).


client.upsert(index:, vectors:, namespace: nil)

Source

Upsert vectors into an index. If a vector with the same ID exists, it will be updated.

Parameters:

Vector Hash Format:

{
  id: 'unique-id',              # Required
  values: [0.1, 0.2, 0.3],     # Required: Array of floats
  metadata: { key: 'value' }    # Optional: Hash of metadata
}

Returns: Hash with :upserted_count

Example:

result = client.upsert(
  index: 'documents',
  vectors: [
    { id: 'doc-1', values: [0.1, 0.2, 0.3], metadata: { title: 'Hello' } },
    { id: 'doc-2', values: [0.4, 0.5, 0.6], metadata: { title: 'World' } }
  ]
)
# => { upserted_count: 2 }

client.query(index:, vector:, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true)

Source

Search for similar vectors using cosine similarity.

Parameters:

Returns: Vectra::QueryResult

Example:

results = client.query(
  index: 'documents',
  vector: [0.1, 0.2, 0.3],
  top_k: 5,
  filter: { category: 'docs' },
  include_metadata: true
)

results.each do |match|
  puts "#{match.id}: #{match.score} - #{match.metadata['title']}"
end

QueryResult Methods:


client.hybrid_search(index:, vector:, text:, alpha: 0.7, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true)

Combine semantic (vector) and keyword (text) search.

Parameters:

Returns: Vectra::QueryResult

Provider Support:

Example:

results = client.hybrid_search(
  index: 'documents',
  vector: query_embedding,
  text: 'ruby vector search',
  alpha: 0.7,  # 70% semantic, 30% keyword
  top_k: 10
)

client.text_search(index:, text:, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true)

Source

Text-only search (keyword search without requiring embeddings).

Parameters:

Returns: Vectra::QueryResult

Provider Support:

Example:

# Keyword search for exact matches
results = client.text_search(
  index: 'products',
  text: 'iPhone 15 Pro',
  top_k: 10,
  filter: { category: 'electronics' }
)

results.each do |match|
  puts "#{match.id}: #{match.score} - #{match.metadata['title']}"
end

Use Cases:


client.fetch(index:, ids:, namespace: nil)

Fetch vectors by their IDs.

Parameters:

Returns: Hash<String, Vectra::Vector> - Hash mapping ID to Vector object

Example:

vectors = client.fetch(
  index: 'documents',
  ids: ['doc-1', 'doc-2']
)

vectors['doc-1'].values   # => [0.1, 0.2, 0.3]
vectors['doc-1'].metadata # => { 'title' => 'Hello' }

client.update(index:, id:, metadata: nil, values: nil, namespace: nil)

Update a vector’s metadata or values.

Parameters:

Returns: Hash with :updated

Note: Must provide either metadata or values (or both).

Example:

client.update(
  index: 'documents',
  id: 'doc-1',
  metadata: { category: 'updated', status: 'published' }
)

client.delete(index:, ids: nil, namespace: nil, filter: nil, delete_all: false)

Delete vectors.

Parameters:

Returns: Hash with :deleted

Note: Must provide ids, filter, or delete_all: true.

Example:

# Delete by IDs
client.delete(index: 'documents', ids: ['doc-1', 'doc-2'])

# Delete by filter
client.delete(index: 'documents', filter: { category: 'old' })

# Delete all
client.delete(index: 'documents', delete_all: true)

client.stats(index:, namespace: nil)

Get index statistics.

Parameters:

Returns: Hash with statistics:

Example:

stats = client.stats(index: 'documents')
# => { dimension: 1536, total_vector_count: 1000, namespaces: { "default" => { vector_count: 1000 } } }

client.create_index(name:, dimension:, metric: "cosine", **options)

Create a new index/collection.

Parameters:

Returns: Hash with index information

Note: Not all providers support index creation. Raises NotImplementedError if provider doesn’t support it (e.g., Memory, Weaviate).

Example:

# Create index with default cosine metric
result = client.create_index(name: 'documents', dimension: 384)
# => { name: 'documents', dimension: 384, metric: 'cosine', status: 'ready' }

# Create with custom metric (Qdrant)
result = client.create_index(
  name: 'products',
  dimension: 1536,
  metric: 'euclidean',
  on_disk: true
)

client.delete_index(name:)

Delete an index/collection.

Parameters:

Returns: Hash with :deleted => true

Note: Not all providers support index deletion. Raises NotImplementedError if provider doesn’t support it (e.g., Memory, Weaviate).

Example:

result = client.delete_index(name: 'old-index')
# => { deleted: true }

client.list_namespaces(index:)

List all namespaces in an index.

Parameters:

Returns: Array<String> - List of namespace names (excludes empty/default namespace)

Example:

namespaces = client.list_namespaces(index: 'documents')
# => ["tenant-1", "tenant-2", "tenant-3"]

namespaces.each do |ns|
  stats = client.stats(index: 'documents', namespace: ns)
  puts "Namespace #{ns}: #{stats[:total_vector_count]} vectors"
end

Health & Monitoring Methods

client.healthy?

Quick health check - returns true if provider connection is healthy.

Returns: Boolean

Example:

if client.healthy?
  client.upsert(...)
else
  Rails.logger.warn('Vectra provider is unhealthy')
end

client.ping

Ping provider and get connection health status with latency.

Returns: Hash with:

Example:

status = client.ping
# => { healthy: true, provider: :qdrant, latency_ms: 23.4 }

puts "Latency: #{status[:latency_ms]}ms"

client.with_timeout(seconds) { ... }

Temporarily override the client’s request timeout inside a block.

Parameters:

Returns: Block result

Example (fast health check in Rails controller):

status = client.with_timeout(0.5) do |c|
  c.ping
end

render json: status, status: status[:healthy] ? :ok : :service_unavailable

After the block finishes (even if it raises), the previous config.timeout value is restored.


client.validate!(require_default_index: false, require_default_namespace: false, features: [])

Source

Validate the client configuration and (optionally) your defaults and provider feature support.

This is useful in boot-time checks (Rails initializers), health endpoints, and CI.

Parameters:

Returns: Vectra::Client (self)

Raises: Vectra::ConfigurationError when validation fails

Example:

# Ensure client is configured
client.validate!

# Ensure calls can omit index:
client.validate!(require_default_index: true)

# Ensure provider supports text_search:
client.validate!(features: [:text_search])

client.with_defaults(index: ..., namespace: ...) { ... }

Source

Temporarily override the client’s default index and/or namespace inside a block.

Unlike with_index_and_namespace, this helper accepts keyword arguments and only overrides what you pass.

Returns: Block result

Example:

client.with_defaults(index: "products", namespace: "tenant-2") do |c|
  c.upsert(vectors: [...]) # uses products/tenant-2
  c.query(vector: embedding, top_k: 10)
end

# Previous defaults are restored after the block.

client.valid?(require_default_index: false, require_default_namespace: false, features: [])

Non-raising validation. Returns true if the client passes validate!, false otherwise. Accepts the same options as validate!.

Returns: Boolean

Example:

next unless client.valid?
client.upsert(vectors: [...])

# With same options as validate!
client.valid?(require_default_index: true)
client.valid?(features: [:text_search])

client.for_tenant(tenant_id, namespace_prefix: "tenant_") { ... }

Source

Multi-tenant block helper. Temporarily sets the default namespace to "#{namespace_prefix}#{tenant_id}", yields the client, then restores the previous namespace. tenant_id can be a string, symbol, or anything responding to to_s.

Parameters:

Returns: Block result

Example:

client.for_tenant("acme", namespace_prefix: "tenant_") do |c|
  c.upsert(vectors: [...])
  c.query(vector: embedding, top_k: 10)
end
# All operations use namespace "tenant_acme"; previous default restored after block.

client.health_check

Detailed health check with provider-specific information.

Returns: Hash with detailed health information

Example:

health = client.health_check
# => { healthy: true, provider: :qdrant, version: '1.7.0', ... }

Vector Helper Methods

Vectra::Vector.normalize(vector, type: :l2)

Normalize a vector array (non-mutating).

Parameters:

Returns: Array<Float> - Normalized vector

Example:

embedding = openai_response['data'][0]['embedding']
normalized = Vectra::Vector.normalize(embedding, type: :l2)
client.upsert(vectors: [{ id: 'doc-1', values: normalized }])

vector.normalize!(type: :l2)

Normalize vector in-place (mutates the vector).

Parameters:

Returns: Self (for method chaining)

Example:

vector = Vectra::Vector.new(id: 'doc-1', values: embedding)
vector.normalize!  # L2 normalization
client.upsert(vectors: [vector])

Batch Operations

Vectra::Batch.upsert(client:, index:, vectors:, batch_size: 100, namespace: nil, on_progress: nil)

Upsert vectors in batches with progress callbacks.

Parameters:

Returns: Hash with :upserted_count

Example:

Vectra::Batch.upsert(
  client: client,
  index: 'products',
  vectors: product_vectors,
  batch_size: 100,
  on_progress: ->(batch_index, total_batches, batch_count) do
    puts "Batch #{batch_index + 1}/#{total_batches} (#{batch_count} vectors)"
  end
)

batch.query_async(index:, vectors:, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true, chunk_size: 10, on_progress: nil)

Query multiple vectors concurrently (useful for recommendation engines).

Parameters:

Returns: Array<QueryResult> - One QueryResult per input vector

Example:

batch = Vectra::Batch.new(client, concurrency: 4)

# Find similar items for multiple products
product_embeddings = products.map(&:embedding)
results = batch.query_async(
  index: 'products',
  vectors: product_embeddings,
  top_k: 5,
  on_progress: ->(stats) do
    puts "Processed #{stats[:processed]}/#{stats[:total]} queries (#{stats[:percentage]}%)"
  end
)

# Each result corresponds to one product
results.each_with_index do |result, i|
  puts "Similar to product #{i}: #{result.ids}"
end

Query Builder (Chainable API)

client.query(index)

Start a chainable query builder.

Returns: Vectra::QueryBuilder

Example:

results = client
  .query('documents')
  .vector([0.1, 0.2, 0.3])
  .top_k(10)
  .filter(category: 'docs')
  .with_metadata
  .execute

results.each do |match|
  puts "#{match.id}: #{match.score}"
end

QueryBuilder Methods:


ActiveRecord Methods

has_vector(column_name, options)

Define vector search on an ActiveRecord model.

Parameters:

Example:

class Document < ApplicationRecord
  include Vectra::ActiveRecord

  has_vector :embedding,
    provider: :qdrant,
    index: 'documents',
    dimension: 1536,
    auto_index: true,
    metadata_fields: [:title, :category]
end

Model.vector_search(embedding:, limit: 10, filter: nil)

Search for similar records using vector similarity.

Parameters:

Returns: ActiveRecord::Relation

Example:

results = Document.vector_search(
  embedding: query_embedding,
  limit: 10,
  filter: { category: 'docs' }
)

results.each do |doc|
  puts doc.title
end

Model.reindex_vectors(scope: all, batch_size: 1000, on_progress: nil)

Reindex all records for a model into the configured vector index.

Parameters:

Returns: Integer - Number of records processed

Example:

# Reindex all products with embeddings
processed = Product.reindex_vectors(
  scope: Product.where.not(embedding: nil),
  batch_size: 500
)

puts "Reindexed #{processed} products"

Migration Tool

Vectra::Migration.new(source_client, target_client)

Source

Initialize a migration tool for copying vectors between providers.

Parameters:

Returns: Vectra::Migration

Example:

source_client = Vectra::Client.new(provider: :memory)
target_client = Vectra::Client.new(provider: :qdrant, host: "http://localhost:6333")

migration = Vectra::Migration.new(source_client, target_client)

migration.migrate(source_index:, target_index:, source_namespace: nil, target_namespace: nil, batch_size: 1000, chunk_size: 100, on_progress: nil)

Source

Migrate vectors from source to target index.

Parameters:

Returns: Hash - Migration result with :migrated_count, :total_vectors, :batches, :errors

Example:

result = migration.migrate(
  source_index: "old-index",
  target_index: "new-index",
  source_namespace: "ns1",
  target_namespace: "ns2",
  on_progress: ->(stats) {
    puts "Progress: #{stats[:percentage]}% (#{stats[:migrated]}/#{stats[:total]})"
  }
)

puts "Migrated #{result[:migrated_count]} vectors in #{result[:batches]} batches"
puts "Errors: #{result[:errors].size}" if result[:errors].any?

migration.verify(source_index:, target_index:, source_namespace: nil, target_namespace: nil)

Source

Verify migration by comparing vector counts between source and target.

Parameters:

Returns: Hash - Verification result with :source_count, :target_count, :match

Example:

verification = migration.verify(
  source_index: "old-index",
  target_index: "new-index"
)

if verification[:match]
  puts "✅ Migration verified: #{verification[:source_count]} vectors"
else
  puts "❌ Mismatch: source=#{verification[:source_count]}, target=#{verification[:target_count]}"
end

Middleware

Vectra::Middleware::RequestId

Source

Request ID tracking middleware. Generates a unique request ID for each operation and propagates it through logs and instrumentation.

Configuration:

# Global
Vectra::Client.use Vectra::Middleware::RequestId

# With custom prefix
Vectra::Client.use Vectra::Middleware::RequestId, prefix: "myapp"

# With custom generator
Vectra::Client.use Vectra::Middleware::RequestId,
  generator: ->(prefix) { "#{prefix}-#{Time.now.to_i}-#{SecureRandom.hex(8)}" }

# With callback
Vectra::Client.use Vectra::Middleware::RequestId,
  on_assign: ->(id) { Rails.logger.info("Request ID: #{id}") }

Access request ID:

# In custom middleware
def after(request, response)
  request_id = request.metadata[:request_id]
  # Use request_id for tracing
end

Vectra::Middleware::DryRun

Source

Dry run / explain mode middleware. Intercepts write operations and logs what would be executed instead of actually executing them.

Configuration:

# Enable dry run mode
client = Vectra::Client.new(
  provider: :qdrant,
  middleware: [Vectra::Middleware::DryRun]
)

# Operations will be logged but not executed
client.upsert(index: "test", vectors: [...])

# With custom logger
Vectra::Client.use Vectra::Middleware::DryRun, logger: custom_logger

# With custom formatter
Vectra::Client.use Vectra::Middleware::DryRun,
  formatter: ->(request) { "Would execute: #{request.operation}" }

# With callback
Vectra::Client.use Vectra::Middleware::DryRun,
  on_dry_run: ->(plan) { puts "Plan: #{plan.inspect}" }

Note: Read operations (query, fetch, stats) pass through normally and are not intercepted.


Error Handling

Vectra defines specific error types:

Example:

begin
  client.query(index: 'missing', vector: [0.1, 0.2, 0.3])
rescue Vectra::NotFoundError => e
  Rails.logger.warn("Index not found: #{e.message}")
rescue Vectra::RateLimitError => e
  Rails.logger.error("Rate limited: #{e.message}")
rescue Vectra::Error => e
  Rails.logger.error("Vectra error: #{e.message}")
end

See Also