API Cheatsheet
Quick reference for the most important Vectra APIs.
For full details, see the API Overview and provider guides.
Core Client
Initialize Client
require 'vectra'
client = Vectra::Client.new(
provider: :qdrant, # :pinecone, :qdrant, :weaviate, :pgvector, :memory
api_key: ENV['QDRANT_API_KEY'],
host: 'http://localhost:6333',
environment: 'us-west-4' # For Pinecone
)
Or use shortcuts:
client = Vectra.qdrant(host: 'http://localhost:6333')
client = Vectra.pinecone(api_key: ENV['PINECONE_API_KEY'], environment: 'us-west-4')
client = Vectra.pgvector(connection_url: ENV['DATABASE_URL'])
client = Vectra.memory # In-memory (testing only)
You can also set a default index and namespace:
client = Vectra::Client.new(
provider: :qdrant,
host: 'http://localhost:6333',
index: 'products',
namespace: 'tenant-1'
)
# Now index and namespace can be omitted
client.upsert(vectors: [...])
client.query(vector: query_embedding, top_k: 10)
In a Rails app with config/vectra.yml generated by rails generate vectra:index, if that YAML file contains only one entry, Vectra::Client.new will automatically use that entry’s index (and namespace, if present) as defaults.
Temporary defaults (block-scoped)
client.with_defaults(index: "products", namespace: "tenant-2") do |c|
c.upsert(vectors: [...])
c.query(vector: query_embedding, top_k: 10)
end
Validate client config & capabilities
client.validate!
client.validate!(require_default_index: true)
client.validate!(features: [:text_search])
# Non-raising: returns true/false
client.valid?
client.valid?(require_default_index: true)
Multi-tenant (for_tenant)
client.for_tenant("acme", namespace_prefix: "tenant_") do |c|
c.upsert(vectors: [...])
c.query(vector: query_embedding, top_k: 10)
end
Upsert
client.upsert(
index: 'documents',
vectors: [
{
id: 'doc-1',
values: embedding_array,
metadata: { title: 'Hello World', category: 'docs' }
}
],
namespace: 'default' # optional
)
Query (similarity search)
results = client.query(
index: 'documents',
vector: query_embedding,
top_k: 10,
filter: { category: 'docs' },
namespace: 'default',
include_values: false,
include_metadata: true
)
results.each do |match|
puts "#{match.id} (score=#{match.score.round(3)}): #{match.metadata['title']}"
end
Hybrid Search (semantic + keyword)
results = client.hybrid_search(
index: 'documents',
vector: query_embedding,
text: 'ruby vector search',
alpha: 0.7, # 70% semantic, 30% keyword
top_k: 10,
filter: { category: 'blog' }
)
Supported providers: Qdrant ✅, Weaviate ✅, pgvector ✅, Pinecone ⚠️
Text Search (keyword-only, no embeddings)
results = client.text_search(
index: 'products',
text: 'iPhone 15 Pro',
top_k: 10,
filter: { category: 'electronics' }
)
results.each do |match|
puts "#{match.id} (score=#{match.score.round(3)}): #{match.metadata['title']}"
end
Supported providers: Qdrant ✅ (BM25), Weaviate ✅ (BM25), pgvector ✅ (PostgreSQL full-text)
Fetch
vectors = client.fetch(
index: 'documents',
ids: ['doc-1', 'doc-2'],
namespace: 'default'
)
vectors['doc-1'].values # => [0.1, 0.2, ...]
vectors['doc-1'].metadata # => { 'title' => 'Hello World' }
Update
client.update(
index: 'documents',
id: 'doc-1',
metadata: { category: 'guides' }
)
Delete
# By IDs
client.delete(index: 'documents', ids: ['doc-1', 'doc-2'])
# By filter
client.delete(index: 'documents', filter: { category: 'old' })
# Delete all in namespace
client.delete(index: 'documents', delete_all: true)
Index Management
# Create index
client.create_index(name: 'documents', dimension: 384, metric: 'cosine')
# List indexes
indexes = client.list_indexes
# => [{ name: 'documents', dimension: 384, ... }]
# Describe index
info = client.describe_index(index: 'documents')
# => { name: 'documents', dimension: 384, metric: 'cosine', status: 'ready' }
# Get stats
stats = client.stats(index: 'documents')
# => { total_vector_count: 1000, dimension: 384, namespaces: { ... } }
# List namespaces
namespaces = client.list_namespaces(index: 'documents')
# => ['tenant-1', 'tenant-2']
# Delete index
client.delete_index(name: 'old-index')
Note: create_index and delete_index are supported by Pinecone, Qdrant, and pgvector. Memory and Weaviate providers don’t support these operations.
Health & Monitoring
Health Check
if client.healthy?
puts 'Vectra provider is healthy'
else
puts 'Vectra provider is NOT healthy'
end
For fast health checks you can temporarily lower the timeout:
status = client.with_timeout(0.5) do |c|
c.ping
end
Ping (with latency)
status = client.ping
# => { healthy: true, provider: :qdrant, latency_ms: 23.4 }
puts "Provider: #{status[:provider]}, latency: #{status[:latency_ms]}ms"
Rails Health Endpoint (Example)
# config/routes.rb
Rails.application.routes.draw do
get '/health/vectra', to: 'health#vectra'
end
# app/controllers/health_controller.rb
class HealthController < ApplicationController
def vectra
client = Vectra::Client.new
status = client.ping
if status[:healthy]
render json: { status: 'ok', provider: status[:provider], latency_ms: status[:latency_ms] }
else
render json: { status: 'unhealthy' }, status: :service_unavailable
end
rescue => e
render json: { status: 'error', error: e.message }, status: :service_unavailable
end
end
Use this endpoint for Kubernetes / load balancer health checks.
Vector Helpers
Normalize Embeddings
embedding = openai_response['data'][0]['embedding']
normalized = Vectra::Vector.normalize(embedding, type: :l2) # or :l1
client.upsert(
index: 'documents',
vectors: [
{ id: 'doc-1', values: normalized, metadata: { title: 'Hello' } }
]
)
In-Place Normalization
vector = Vectra::Vector.new(id: 'doc-1', values: embedding)
vector.normalize! # Mutates values
client.upsert(index: 'documents', vectors: [vector])
Embedding Cache Helper
cache = Vectra::Cache.new(ttl: 600, max_size: 1000)
embedding = Vectra::Embeddings.fetch(
cache: cache,
model_name: "Product",
id: product.id,
input: product.description,
field: :description
) do
EmbeddingService.generate(product.description)
end
Batch Operations
Simple Batch Upsert
vectors = products.map do |product|
{
id: product.id.to_s,
values: product.embedding,
metadata: { name: product.name, price: product.price }
}
end
client.upsert(index: 'products', vectors: vectors)
Batch with Progress Callback
Vectra::Batch.upsert(
client: client,
index: 'products',
vectors: 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 (Multiple Vectors)
batch = Vectra::Batch.new(client, concurrency: 4)
# Find similar items for multiple products at once
product_embeddings = products.map(&:embedding)
results = batch.query_async(
index: 'products',
vectors: product_embeddings,
top_k: 5
)
# Each result corresponds to one product
results.each_with_index do |result, i|
puts "Similar to product #{i}: #{result.ids}"
end
ActiveRecord + has_vector DSL
Basic Setup
class Document < ApplicationRecord
include Vectra::ActiveRecord
has_vector :embedding,
provider: :qdrant,
index: 'documents',
dimension: 1536,
metadata_fields: [:title, :category]
end
Auto-Index on Save
doc = Document.create!(
title: 'Hello World',
category: 'docs',
embedding: EmbeddingService.generate('Hello World')
)
Vector Search from Model
results = Document.vector_search(
embedding: EmbeddingService.generate('vector search in ruby'),
limit: 10,
filter: { category: 'docs' }
)
results.each do |doc|
puts doc.title
end
Reindex All Records
# Reindex all documents that already have embeddings
processed = Document.reindex_vectors(
scope: Document.where.not(embedding: nil),
batch_size: 500
)
puts "Reindexed #{processed} documents"
Migration Tool
Migrate Vectors Between Providers
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)
# Migrate with progress tracking
result = migration.migrate(
source_index: "old-index",
target_index: "new-index",
on_progress: ->(stats) {
puts "#{stats[:percentage]}% (#{stats[:migrated]}/#{stats[:total]})"
}
)
# Verify migration
verification = migration.verify(
source_index: "old-index",
target_index: "new-index"
)
puts "Match: #{verification[:match]}" # => true
Middleware
Request ID Tracking
Vectra::Client.use Vectra::Middleware::RequestId
# Request ID is automatically added to request.metadata[:request_id]
# and propagated to response.metadata[:request_id]
Dry Run Mode
client = Vectra::Client.new(
provider: :qdrant,
middleware: [Vectra::Middleware::DryRun]
)
# Write operations are logged but not executed
client.upsert(index: "test", vectors: [...])
# Read operations pass through normally
results = client.query(index: "test", vector: [...], top_k: 10)
Error Handling
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