vectra-client 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f6f5038173b0fbcde02bcfe548c2a7b34bcef702233151f811cdceaff451cb0
4
- data.tar.gz: 3b5557a28941df944df56379e6f657da4f027942bd34ee94fd8adea9ec93ce03
3
+ metadata.gz: aada098f943da3a1b81288f46e32382e775492efc96e87a175b1e9ba002ea7c3
4
+ data.tar.gz: 24f2efb3bdf110541c60ce4710f61e616fc824bf4874d8205ff5d1ebdaa8d9bc
5
5
  SHA512:
6
- metadata.gz: d4ff59a890b201c2877d496a77106f926fb641e126c6e68501d7e516e2d28d433685ca9cba490e8fee29e7d4846ca36cf962564ea9be7db75b925d344d957864
7
- data.tar.gz: 42f0b9c6be55e599ed3342254f379df8065ce2a0e13f235d0678a10c309b924a59705c8c7e7b3d50682b116891d1ca25edc36e9f0ee5cddb258c0d2c98404af4
6
+ metadata.gz: 00df0da772c575e7933c97b640e0e6c1f31a5cdd7671b9f41106ccc54eda2b0f7e90871dd7ddbfe654ff9007fb5cc0b3a4897a0955284e0b4625c3dd1ddf9d3c
7
+ data.tar.gz: d16d6c0425c8ba8ecad38021f416220c91a01bd4f58d8b0d397324dead8e9e3d32bc46b90583ac7678ecc2e0b2dcb0084dfad3b99fd021577c5c469ab07e8588
data/CHANGELOG.md CHANGED
@@ -1,10 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## [v1.0.4](https://github.com/stokry/vectra/tree/v1.0.4) (2026-01-13)
3
+ ## [v1.0.6](https://github.com/stokry/vectra/tree/v1.0.6) (2026-01-13)
4
+
5
+ [Full Changelog](https://github.com/stokry/vectra/compare/v1.0.5...v1.0.6)
6
+
7
+ ### Improvements
8
+ - Refined test suite and documentation
9
+ - Performance optimizations
10
+ - Enhanced stability and reliability
4
11
 
5
- ### Added
6
- - Comprehensive Rails generator test coverage
7
- - Support for all vector database providers in generator
12
+ ## [v1.0.5](https://github.com/stokry/vectra/tree/v1.0.5) (2026-01-13)
13
+
14
+ [Full Changelog](https://github.com/stokry/vectra/compare/v1.0.4...v1.0.5)
15
+
16
+ ## [v1.0.4](https://github.com/stokry/vectra/tree/v1.0.4) (2026-01-13)
8
17
 
9
18
  [Full Changelog](https://github.com/stokry/vectra/compare/v1.0.3...v1.0.4)
10
19
 
data/README.md CHANGED
@@ -184,6 +184,8 @@ graph TB
184
184
 
185
185
  ## Rails Integration
186
186
 
187
+ ### Quick Start
188
+
187
189
  ```ruby
188
190
  class Document < ApplicationRecord
189
191
  include Vectra::ActiveRecord
@@ -216,6 +218,17 @@ This will:
216
218
  - **Update the model** to include `ProductVector`
217
219
  - **Append to `config/vectra.yml`** with index metadata (no API keys)
218
220
 
221
+ ### Complete Rails Guide
222
+
223
+ For a complete step-by-step guide including:
224
+ - Setting up embeddings (OpenAI, Cohere)
225
+ - Processing 1000+ products
226
+ - Background jobs
227
+ - Hybrid search
228
+ - Performance optimization
229
+
230
+ 👉 **[Read the complete Rails Integration Guide](https://vectra-docs.netlify.app/guides/rails-integration/)**
231
+
219
232
  ## Production Patterns
220
233
 
221
234
  Vectra includes 7 production-ready patterns out of the box:
@@ -197,6 +197,18 @@ This will:
197
197
  - Update the **model** to include `ProductVector`
198
198
  - Append an entry to **`config/vectra.yml`** with index metadata (no API keys)
199
199
 
200
+ ### Complete Rails Integration Guide
201
+
202
+ For a comprehensive Rails guide including:
203
+ - Step-by-step setup
204
+ - Embedding generation (OpenAI, Cohere)
205
+ - Processing 1000+ products with batch operations
206
+ - Background jobs for async processing
207
+ - Hybrid search examples
208
+ - Performance optimization tips
209
+
210
+ 👉 **[Read the complete Rails Integration Guide]({{ site.baseurl }}/guides/rails-integration/)**
211
+
200
212
  ## Configuration
201
213
 
202
214
  Create a configuration file (Rails: `config/initializers/vectra.rb`):
@@ -214,6 +226,7 @@ client = Vectra::Client.new
214
226
 
215
227
  ## Next Steps
216
228
 
229
+ - **[Rails Integration Guide]({{ site.baseurl }}/guides/rails-integration/)** - Complete step-by-step guide for Rails apps
217
230
  - [API Reference]({{ site.baseurl }}/api/overview)
218
231
  - [Provider Guides]({{ site.baseurl }}/providers)
219
232
  - [Examples]({{ site.baseurl }}/examples/basic-usage)
@@ -0,0 +1,690 @@
1
+ ---
2
+ layout: page
3
+ title: Rails Integration Guide
4
+ permalink: /guides/rails-integration/
5
+ ---
6
+
7
+ # Rails Integration Guide
8
+
9
+ Complete step-by-step guide to integrate Vectra into your Rails application with vector search capabilities.
10
+
11
+ ## Overview
12
+
13
+ This guide will walk you through:
14
+ 1. Installing Vectra in a Rails app
15
+ 2. Setting up a Product model with vector search
16
+ 3. Generating embeddings for 1000 products
17
+ 4. Performing vector searches
18
+ 5. Using all Vectra features
19
+
20
+ ## Step 1: Installation
21
+
22
+ ### Add Vectra to your Gemfile
23
+
24
+ ```ruby
25
+ # Gemfile
26
+ gem 'vectra-client'
27
+ ```
28
+
29
+ ```bash
30
+ bundle install
31
+ ```
32
+
33
+ ### Run the Install Generator
34
+
35
+ ```bash
36
+ rails generate vectra:install
37
+ ```
38
+
39
+ This creates:
40
+ - `config/initializers/vectra.rb` - Configuration file
41
+ - `db/migrate/XXXXXX_enable_pgvector_extension.rb` - pgvector extension (if using pgvector)
42
+
43
+ ## Step 2: Configure Vectra
44
+
45
+ **Important:** Always add `require 'vectra'` at the top of your initializer to ensure all Providers are loaded in Rails autoloading context.
46
+
47
+ ### Option A: Using pgvector (PostgreSQL)
48
+
49
+ ```ruby
50
+ # config/initializers/vectra.rb
51
+ require 'vectra' # Ensure all Providers are loaded
52
+
53
+ Vectra.configure do |config|
54
+ config.provider = :pgvector
55
+ config.host = Rails.application.config.database_configuration[Rails.env]['database']
56
+ # Or use connection URL:
57
+ # config.host = ENV['DATABASE_URL']
58
+ end
59
+ ```
60
+
61
+ ### Option B: Using Qdrant
62
+
63
+ ```ruby
64
+ # config/initializers/vectra.rb
65
+ require 'vectra' # Ensure all Providers are loaded
66
+
67
+ Vectra.configure do |config|
68
+ config.provider = :qdrant
69
+ config.host = ENV.fetch('QDRANT_HOST', 'http://localhost:6333')
70
+ config.api_key = ENV['QDRANT_API_KEY'] # Optional for local instances
71
+ end
72
+ ```
73
+
74
+ ### Option C: Using Pinecone
75
+
76
+ ```ruby
77
+ # config/initializers/vectra.rb
78
+ require 'vectra' # Ensure all Providers are loaded
79
+
80
+ Vectra.configure do |config|
81
+ config.provider = :pinecone
82
+ config.api_key = ENV['PINECONE_API_KEY']
83
+ config.environment = ENV['PINECONE_ENVIRONMENT'] # e.g., 'us-west-4'
84
+ end
85
+ ```
86
+
87
+ ## Step 3: Create Product Model with Vector Search
88
+
89
+ ### Generate the Model and Migration
90
+
91
+ ```bash
92
+ rails generate model Product name:string description:text price:decimal category:string
93
+ rails generate vectra:index Product embedding dimension:1536 provider:qdrant
94
+ ```
95
+
96
+ This will:
97
+ - Create a migration for the `embedding` column (if `provider=pgvector`)
98
+ - Generate `app/models/concerns/product_vector.rb` with `has_vector` configuration
99
+ - Update `app/models/product.rb` to include the concern
100
+ - Add configuration to `config/vectra.yml`
101
+
102
+ ### Run Migrations
103
+
104
+ ```bash
105
+ rails db:migrate
106
+ ```
107
+
108
+ ### Manual Setup (Alternative)
109
+
110
+ If you prefer manual setup:
111
+
112
+ ```ruby
113
+ # db/migrate/XXXXXX_create_products.rb
114
+ class CreateProducts < ActiveRecord::Migration[7.0]
115
+ def change
116
+ create_table :products do |t|
117
+ t.string :name
118
+ t.text :description
119
+ t.decimal :price
120
+ t.string :category
121
+ # For pgvector, add vector column:
122
+ # t.column :embedding, :vector, limit: 1536
123
+ t.timestamps
124
+ end
125
+ end
126
+ end
127
+ ```
128
+
129
+ ```ruby
130
+ # app/models/concerns/product_vector.rb
131
+ module ProductVector
132
+ extend ActiveSupport::Concern
133
+
134
+ included do
135
+ include Vectra::ActiveRecord
136
+
137
+ has_vector :embedding,
138
+ provider: :qdrant,
139
+ index: 'products',
140
+ dimension: 1536,
141
+ auto_index: true,
142
+ metadata_fields: [:name, :category, :price]
143
+ end
144
+ end
145
+ ```
146
+
147
+ ```ruby
148
+ # app/models/product.rb
149
+ class Product < ApplicationRecord
150
+ include ProductVector
151
+
152
+ # Generate embedding before validation
153
+ before_validation :generate_embedding, if: -> { description.present? && embedding.nil? }
154
+
155
+ private
156
+
157
+ def generate_embedding
158
+ # Use OpenAI, Cohere, or any embedding service
159
+ self.embedding = generate_embedding_from_text(description)
160
+ end
161
+
162
+ def generate_embedding_from_text(text)
163
+ # Example with OpenAI (install 'ruby-openai' gem)
164
+ # client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])
165
+ # response = client.embeddings(
166
+ # parameters: {
167
+ # model: 'text-embedding-3-small',
168
+ # input: text
169
+ # }
170
+ # )
171
+ # response.dig('data', 0, 'embedding')
172
+
173
+ # For demo purposes, use a simple hash-based embedding
174
+ # In production, use a real embedding service!
175
+ hash = text.hash.abs
176
+ Array.new(1536) { |i| ((hash * (i + 1)) % 1000) / 1000.0 }
177
+ end
178
+ end
179
+ ```
180
+
181
+ ## Step 4: Generate Embeddings for Products
182
+
183
+ ### Using OpenAI (Recommended)
184
+
185
+ First, add the OpenAI gem:
186
+
187
+ ```ruby
188
+ # Gemfile
189
+ gem 'ruby-openai'
190
+ ```
191
+
192
+ ```bash
193
+ bundle install
194
+ ```
195
+
196
+ Then create a service:
197
+
198
+ ```ruby
199
+ # app/services/embedding_service.rb
200
+ class EmbeddingService
201
+ def self.generate(text)
202
+ client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])
203
+ response = client.embeddings(
204
+ parameters: {
205
+ model: 'text-embedding-3-small', # 1536 dimensions
206
+ input: text
207
+ }
208
+ )
209
+
210
+ embedding = response.dig('data', 0, 'embedding')
211
+ Vectra::Vector.normalize(embedding) # Normalize for better cosine similarity
212
+ end
213
+ end
214
+ ```
215
+
216
+ Update your Product model:
217
+
218
+ ```ruby
219
+ # app/models/product.rb
220
+ class Product < ApplicationRecord
221
+ include ProductVector
222
+
223
+ before_validation :generate_embedding, if: -> { description.present? && embedding.nil? }
224
+
225
+ private
226
+
227
+ def generate_embedding
228
+ self.embedding = EmbeddingService.generate(description)
229
+ end
230
+ end
231
+ ```
232
+
233
+ ### Using Cohere
234
+
235
+ ```ruby
236
+ # Gemfile
237
+ gem 'cohere-rb'
238
+ ```
239
+
240
+ ```ruby
241
+ # app/services/embedding_service.rb
242
+ class EmbeddingService
243
+ def self.generate(text)
244
+ client = Cohere::Client.new(api_key: ENV['COHERE_API_KEY'])
245
+ response = client.embed(
246
+ texts: [text],
247
+ model: 'embed-english-v3.0',
248
+ input_type: 'search_document'
249
+ )
250
+
251
+ embedding = response.dig('embeddings', 0)
252
+ Vectra::Vector.normalize(embedding)
253
+ end
254
+ end
255
+ ```
256
+
257
+ ### Batch Processing for 1000 Products
258
+
259
+ Create a rake task to process all products:
260
+
261
+ ```ruby
262
+ # lib/tasks/vectra.rake
263
+ namespace :vectra do
264
+ desc "Generate embeddings for all products without embeddings"
265
+ task generate_embeddings: :environment do
266
+ products = Product.where(embedding: nil).where.not(description: nil)
267
+ total = products.count
268
+
269
+ puts "Generating embeddings for #{total} products..."
270
+
271
+ products.find_each.with_index do |product, index|
272
+ begin
273
+ product.generate_embedding!
274
+ product.save!
275
+
276
+ if (index + 1) % 100 == 0
277
+ puts "Processed #{index + 1}/#{total} products..."
278
+ end
279
+ rescue => e
280
+ puts "Error processing product #{product.id}: #{e.message}"
281
+ end
282
+ end
283
+
284
+ puts "✅ Completed! Generated embeddings for #{Product.where.not(embedding: nil).count} products"
285
+ end
286
+
287
+ desc "Re-index all products in vector database"
288
+ task reindex: :environment do
289
+ products = Product.where.not(embedding: nil)
290
+ total = products.count
291
+
292
+ puts "Re-indexing #{total} products..."
293
+
294
+ products.find_each.with_index do |product, index|
295
+ begin
296
+ product.index_vector!
297
+
298
+ if (index + 1) % 100 == 0
299
+ puts "Indexed #{index + 1}/#{total} products..."
300
+ end
301
+ rescue => e
302
+ puts "Error indexing product #{product.id}: #{e.message}"
303
+ end
304
+ end
305
+
306
+ puts "✅ Completed! Re-indexed #{total} products"
307
+ end
308
+ end
309
+ ```
310
+
311
+ Run the task:
312
+
313
+ ```bash
314
+ rails vectra:generate_embeddings
315
+ ```
316
+
317
+ ## Step 5: Create the Vector Index
318
+
319
+ ### For Qdrant/Pinecone/Weaviate
320
+
321
+ ```ruby
322
+ # rails console
323
+ client = Vectra::Client.new
324
+ client.provider.create_index(
325
+ name: 'products',
326
+ dimension: 1536,
327
+ metric: 'cosine'
328
+ )
329
+ ```
330
+
331
+ ### For pgvector
332
+
333
+ The index is created automatically via migration, but you can also create it manually:
334
+
335
+ ```ruby
336
+ # db/migrate/XXXXXX_add_vector_index_to_products.rb
337
+ class AddVectorIndexToProducts < ActiveRecord::Migration[7.0]
338
+ def change
339
+ add_index :products, :embedding,
340
+ using: :ivfflat,
341
+ with: { lists: 100 },
342
+ opclass: :vector_cosine_ops
343
+ end
344
+ end
345
+ ```
346
+
347
+ ## Step 6: Using Vector Search
348
+
349
+ ### Basic Search
350
+
351
+ ```ruby
352
+ # In your controller or service
353
+ class ProductsController < ApplicationController
354
+ def search
355
+ query_text = params[:query]
356
+
357
+ # Generate embedding for search query
358
+ query_embedding = EmbeddingService.generate(query_text)
359
+
360
+ # Search for similar products
361
+ results = Product.vector_search(query_embedding, limit: 10)
362
+
363
+ render json: results.map { |p|
364
+ {
365
+ id: p.id,
366
+ name: p.name,
367
+ score: p.vector_score,
368
+ price: p.price
369
+ }
370
+ }
371
+ end
372
+ end
373
+ ```
374
+
375
+ ### Search with Metadata Filters
376
+
377
+ ```ruby
378
+ # Search only in specific category
379
+ results = Product.vector_search(
380
+ query_embedding,
381
+ limit: 10,
382
+ filter: { category: 'electronics' }
383
+ )
384
+
385
+ # Search with price range (if using Qdrant/Weaviate)
386
+ results = Product.vector_search(
387
+ query_embedding,
388
+ limit: 10,
389
+ filter: {
390
+ category: 'electronics',
391
+ price: { gte: 100, lte: 500 }
392
+ }
393
+ )
394
+ ```
395
+
396
+ ### Hybrid Search (Semantic + Keyword)
397
+
398
+ ```ruby
399
+ # Search combining semantic similarity and keyword matching
400
+ query_embedding = EmbeddingService.generate(params[:query])
401
+
402
+ results = Product.vectra_client.hybrid_search(
403
+ index: 'products',
404
+ vector: query_embedding,
405
+ text: params[:query],
406
+ alpha: 0.7, # 70% semantic, 30% keyword
407
+ top_k: 10
408
+ )
409
+
410
+ # Convert results to Product objects
411
+ product_ids = results.map(&:id)
412
+ products = Product.where(id: product_ids).index_by(&:id)
413
+ results_with_products = results.map do |result|
414
+ product = products[result.id]
415
+ product&.vector_score = result.score
416
+ product
417
+ end.compact
418
+ ```
419
+
420
+ ### Find Similar Products
421
+
422
+ ```ruby
423
+ # Find products similar to a specific product
424
+ product = Product.find(params[:id])
425
+ similar = Product.similar_to(product, limit: 5)
426
+
427
+ similar.each do |p|
428
+ puts "#{p.name} (similarity: #{p.vector_score})"
429
+ end
430
+ ```
431
+
432
+ ## Step 7: Advanced Features
433
+
434
+ ### Background Job for Embedding Generation
435
+
436
+ ```ruby
437
+ # app/jobs/generate_embedding_job.rb
438
+ class GenerateEmbeddingJob < ApplicationJob
439
+ queue_as :default
440
+
441
+ def perform(product_id)
442
+ product = Product.find(product_id)
443
+ return if product.embedding.present?
444
+
445
+ product.generate_embedding!
446
+ product.save!
447
+ end
448
+ end
449
+ ```
450
+
451
+ ```ruby
452
+ # app/models/product.rb
453
+ class Product < ApplicationRecord
454
+ include ProductVector
455
+
456
+ after_create :enqueue_embedding_generation, if: -> { description.present? }
457
+
458
+ private
459
+
460
+ def enqueue_embedding_generation
461
+ GenerateEmbeddingJob.perform_later(id)
462
+ end
463
+ end
464
+ ```
465
+
466
+ ### Batch Upsert for Performance
467
+
468
+ ```ruby
469
+ # app/services/product_indexer.rb
470
+ class ProductIndexer
471
+ def self.batch_index(products)
472
+ vectors = products.map do |product|
473
+ {
474
+ id: product.id.to_s,
475
+ values: product.embedding,
476
+ metadata: {
477
+ name: product.name,
478
+ category: product.category,
479
+ price: product.price.to_f
480
+ }
481
+ }
482
+ end
483
+
484
+ client = Vectra::Client.new
485
+ client.upsert(
486
+ index: 'products',
487
+ vectors: vectors
488
+ )
489
+ end
490
+ end
491
+
492
+ # Usage
493
+ products = Product.where.not(embedding: nil).limit(100)
494
+ ProductIndexer.batch_index(products)
495
+ ```
496
+
497
+ ### Using Callbacks for Progress Tracking
498
+
499
+ ```ruby
500
+ # Batch upsert with progress callback
501
+ client = Vectra::Client.new
502
+
503
+ client.upsert(
504
+ index: 'products',
505
+ vectors: vectors,
506
+ on_progress: ->(progress) {
507
+ puts "Progress: #{progress[:processed]}/#{progress[:total]} (#{progress[:percentage]}%)"
508
+ }
509
+ )
510
+ ```
511
+
512
+ ## Step 8: Complete Example: Products Controller
513
+
514
+ ```ruby
515
+ # app/controllers/products_controller.rb
516
+ class ProductsController < ApplicationController
517
+ def index
518
+ @products = Product.all.page(params[:page])
519
+ end
520
+
521
+ def show
522
+ @product = Product.find(params[:id])
523
+ @similar = Product.similar_to(@product, limit: 4)
524
+ end
525
+
526
+ def search
527
+ query = params[:q]
528
+ return render json: [] if query.blank?
529
+
530
+ # Generate embedding for query
531
+ query_embedding = EmbeddingService.generate(query)
532
+
533
+ # Vector search
534
+ results = Product.vector_search(
535
+ query_embedding,
536
+ limit: 20,
537
+ filter: params[:category].present? ? { category: params[:category] } : nil
538
+ )
539
+
540
+ render json: {
541
+ query: query,
542
+ results: results.map { |p|
543
+ {
544
+ id: p.id,
545
+ name: p.name,
546
+ description: p.description,
547
+ price: p.price,
548
+ category: p.category,
549
+ similarity_score: p.vector_score.round(4)
550
+ }
551
+ }
552
+ }
553
+ end
554
+
555
+ def create
556
+ @product = Product.new(product_params)
557
+
558
+ if @product.save
559
+ # Embedding is generated automatically via before_validation
560
+ # and indexed automatically via after_save (auto_index: true)
561
+ render json: @product, status: :created
562
+ else
563
+ render json: @product.errors, status: :unprocessable_entity
564
+ end
565
+ end
566
+
567
+ private
568
+
569
+ def product_params
570
+ params.require(:product).permit(:name, :description, :price, :category)
571
+ end
572
+ end
573
+ ```
574
+
575
+ ## Step 9: Testing
576
+
577
+ ```ruby
578
+ # spec/models/product_spec.rb
579
+ RSpec.describe Product, type: :model do
580
+ describe 'vector search' do
581
+ let!(:product1) do
582
+ Product.create!(
583
+ name: 'Laptop',
584
+ description: 'High-performance laptop for developers',
585
+ price: 1299.99,
586
+ category: 'electronics',
587
+ embedding: Array.new(1536) { rand }
588
+ )
589
+ end
590
+
591
+ let!(:product2) do
592
+ Product.create!(
593
+ name: 'Desktop PC',
594
+ description: 'Powerful desktop computer for gaming',
595
+ price: 1999.99,
596
+ category: 'electronics',
597
+ embedding: Array.new(1536) { rand }
598
+ )
599
+ end
600
+
601
+ it 'finds similar products' do
602
+ query_vector = product1.embedding
603
+ results = Product.vector_search(query_vector, limit: 5)
604
+
605
+ expect(results).to include(product1)
606
+ expect(results.first.vector_score).to be > 0
607
+ end
608
+
609
+ it 'filters by category' do
610
+ query_vector = product1.embedding
611
+ results = Product.vector_search(
612
+ query_vector,
613
+ limit: 10,
614
+ filter: { category: 'electronics' }
615
+ )
616
+
617
+ expect(results.all? { |p| p.category == 'electronics' }).to be true
618
+ end
619
+ end
620
+ end
621
+ ```
622
+
623
+ ## Step 10: Performance Tips
624
+
625
+ ### 1. Use Connection Pooling (pgvector)
626
+
627
+ ```ruby
628
+ # config/initializers/vectra.rb
629
+ Vectra.configure do |config|
630
+ config.provider = :pgvector
631
+ config.pool_size = 10 # Connection pool size
632
+ end
633
+ ```
634
+
635
+ ### 2. Enable Caching
636
+
637
+ ```ruby
638
+ # config/initializers/vectra.rb
639
+ Vectra.configure do |config|
640
+ config.cache_enabled = true
641
+ config.cache_ttl = 3600 # 1 hour
642
+ end
643
+ ```
644
+
645
+ ### 3. Batch Operations
646
+
647
+ Always use batch operations when processing multiple products:
648
+
649
+ ```ruby
650
+ # Good: Batch upsert
651
+ Product.where.not(embedding: nil).find_in_batches(batch_size: 100) do |batch|
652
+ vectors = batch.map { |p| { id: p.id.to_s, values: p.embedding } }
653
+ Vectra::Client.new.upsert(index: 'products', vectors: vectors)
654
+ end
655
+
656
+ # Bad: Individual upserts
657
+ Product.where.not(embedding: nil).each do |product|
658
+ Vectra::Client.new.upsert(
659
+ index: 'products',
660
+ vectors: [{ id: product.id.to_s, values: product.embedding }]
661
+ )
662
+ end
663
+ ```
664
+
665
+ ## Summary
666
+
667
+ You now have a complete Rails application with:
668
+ - ✅ Vector search for products
669
+ - ✅ Automatic embedding generation
670
+ - ✅ Automatic indexing on save
671
+ - ✅ Search with metadata filters
672
+ - ✅ Hybrid search (semantic + keyword)
673
+ - ✅ Batch processing for 1000+ products
674
+ - ✅ Background jobs for async processing
675
+
676
+ ## Troubleshooting
677
+
678
+ If you encounter issues like `uninitialized constant Vectra::Providers::Qdrant`, see the [Rails Troubleshooting Guide]({{ site.baseurl }}/guides/rails-troubleshooting/) for solutions.
679
+
680
+ Common fixes:
681
+ - Add `require 'vectra'` at the top of your initializer
682
+ - Restart Rails server after configuration changes
683
+ - Check that all Providers are loaded: `Vectra::Providers.constants`
684
+
685
+ ## Next Steps
686
+
687
+ - [Rails Troubleshooting Guide]({{ site.baseurl }}/guides/rails-troubleshooting/) - Common issues and solutions
688
+ - [API Reference]({{ site.baseurl }}/api/overview)
689
+ - [Provider Guides]({{ site.baseurl }}/providers)
690
+ - [Performance Optimization]({{ site.baseurl }}/guides/performance)
@@ -0,0 +1,377 @@
1
+ ---
2
+ layout: page
3
+ title: Rails Troubleshooting
4
+ permalink: /guides/rails-troubleshooting/
5
+ ---
6
+
7
+ # Rails Troubleshooting Guide
8
+
9
+ ## Common Issues and Solutions
10
+
11
+ ### Issue: `uninitialized constant Vectra::Providers::Qdrant`
12
+
13
+ **Error Message:**
14
+ ```
15
+ NameError: uninitialized constant Vectra::Providers::Qdrant
16
+ ```
17
+
18
+ **Cause:**
19
+ In Rails, when using autoloading (Zeitwerk), modules are loaded on-demand. If `Vectra::Client` is instantiated before all Providers are loaded, you'll get this error.
20
+
21
+ **Solution 1: Explicit require in initializer (Recommended)**
22
+
23
+ Add `require 'vectra'` at the top of your initializer:
24
+
25
+ ```ruby
26
+ # config/initializers/vectra.rb
27
+ require 'vectra' # Ensure all Providers are loaded
28
+
29
+ Vectra.configure do |config|
30
+ config.provider = :qdrant
31
+ # ... rest of config
32
+ end
33
+ ```
34
+
35
+ **Solution 2: Lazy loading in ActiveRecord**
36
+
37
+ If you're using `has_vector` in your models, the client is created lazily, which should work. However, if you're creating clients directly, ensure Providers are loaded:
38
+
39
+ ```ruby
40
+ # This will work because vectra.rb loads all Providers
41
+ client = Vectra::Client.new(provider: :qdrant)
42
+
43
+ # But if you're in a context where autoloading hasn't kicked in:
44
+ require 'vectra' # Load everything first
45
+ client = Vectra::Client.new(provider: :qdrant)
46
+ ```
47
+
48
+ **Solution 3: Check autoload paths**
49
+
50
+ If you're using Zeitwerk, ensure Vectra is in your autoload paths:
51
+
52
+ ```ruby
53
+ # config/application.rb
54
+ config.autoload_paths << Rails.root.join('lib')
55
+ ```
56
+
57
+ However, since Vectra is a gem, this shouldn't be necessary. The gem's `lib` directory should be in the load path automatically.
58
+
59
+ ### Issue: `uninitialized constant Vectra::Client::HealthCheck`
60
+
61
+ **Error Message:**
62
+ ```
63
+ NameError: uninitialized constant Vectra::Client::HealthCheck
64
+ ```
65
+
66
+ **Cause:**
67
+ Module loading order issue. `Client` tries to include `HealthCheck` before it's loaded.
68
+
69
+ **Solution:**
70
+
71
+ This is already fixed in the gem with defensive requires. If you still see this, ensure you're using the latest version:
72
+
73
+ ```ruby
74
+ # In your Gemfile
75
+ gem 'vectra-client', '>= 1.0.1'
76
+ ```
77
+
78
+ Then run:
79
+ ```bash
80
+ bundle update vectra-client
81
+ ```
82
+
83
+ ### Issue: Providers not found in Rails console
84
+
85
+ **Symptom:**
86
+ ```ruby
87
+ # In rails console
88
+ Vectra::Providers::Qdrant
89
+ # => NameError: uninitialized constant Vectra::Providers::Qdrant
90
+ ```
91
+
92
+ **Solution:**
93
+
94
+ Load Vectra explicitly:
95
+ ```ruby
96
+ require 'vectra'
97
+ Vectra::Providers::Qdrant
98
+ # => Vectra::Providers::Qdrant
99
+ ```
100
+
101
+ Or use the client directly:
102
+ ```ruby
103
+ client = Vectra::Client.new(provider: :qdrant)
104
+ # This will load all necessary Providers
105
+ ```
106
+
107
+ ### Issue: Autoloading conflicts with Zeitwerk
108
+
109
+ **Symptom:**
110
+ In Rails 7+ with Zeitwerk, you might see warnings or errors about constant loading.
111
+
112
+ **Solution:**
113
+
114
+ Vectra uses explicit `require_relative` statements, which work with both classic autoloading and Zeitwerk. However, if you're seeing issues:
115
+
116
+ 1. **Ensure initializer loads Vectra:**
117
+ ```ruby
118
+ # config/initializers/vectra.rb
119
+ require 'vectra' # Explicit require
120
+ ```
121
+
122
+ 2. **Disable Zeitwerk for Vectra (not recommended, but works):**
123
+ ```ruby
124
+ # config/application.rb
125
+ config.autoloader = :classic # Only if absolutely necessary
126
+ ```
127
+
128
+ 3. **Use eager loading in production:**
129
+ ```ruby
130
+ # config/environments/production.rb
131
+ config.eager_load = true # Already default in production
132
+ ```
133
+
134
+ ### Issue: `has_vector` not working in models
135
+
136
+ **Symptom:**
137
+ ```ruby
138
+ class Product < ApplicationRecord
139
+ include Vectra::ActiveRecord
140
+ has_vector :embedding, dimension: 1536
141
+ end
142
+
143
+ # Error: undefined method `has_vector`
144
+ ```
145
+
146
+ **Cause:**
147
+ `Vectra::ActiveRecord` module not loaded or included incorrectly.
148
+
149
+ **Solution:**
150
+
151
+ 1. **Ensure Vectra is required:**
152
+ ```ruby
153
+ # config/initializers/vectra.rb
154
+ require 'vectra'
155
+ ```
156
+
157
+ 2. **Check include order:**
158
+ ```ruby
159
+ class Product < ApplicationRecord
160
+ include Vectra::ActiveRecord # Must be included first
161
+
162
+ has_vector :embedding, dimension: 1536, provider: :qdrant
163
+ end
164
+ ```
165
+
166
+ 3. **Use the generator (recommended):**
167
+ ```bash
168
+ rails generate vectra:index Product embedding dimension:1536 provider:qdrant
169
+ ```
170
+ This will create the concern correctly.
171
+
172
+ ### Issue: Client created but provider methods fail
173
+
174
+ **Symptom:**
175
+ ```ruby
176
+ client = Vectra::Client.new(provider: :qdrant)
177
+ client.upsert(...) # Works
178
+ client.query(...) # NameError: uninitialized constant Vectra::Providers::Qdrant
179
+ ```
180
+
181
+ **Cause:**
182
+ Provider was loaded initially, but then unloaded by autoloader, or there's a circular dependency.
183
+
184
+ **Solution:**
185
+
186
+ 1. **Use explicit require:**
187
+ ```ruby
188
+ require 'vectra'
189
+ client = Vectra::Client.new(provider: :qdrant)
190
+ ```
191
+
192
+ 2. **Check for circular dependencies:**
193
+ If you have custom code that requires Vectra modules, ensure they're loaded in the correct order.
194
+
195
+ 3. **Disable autoloading for Vectra (last resort):**
196
+ ```ruby
197
+ # config/application.rb
198
+ # Add Vectra to eager load paths
199
+ config.eager_load_paths << Gem.find_files('vectra').first.split('/lib/').first + '/lib'
200
+ ```
201
+
202
+ ### Issue: Generator creates files but model doesn't work
203
+
204
+ **Symptom:**
205
+ ```bash
206
+ rails generate vectra:index Product embedding dimension:1536 provider:qdrant
207
+ # Files created, but model errors
208
+ ```
209
+
210
+ **Solution:**
211
+
212
+ 1. **Restart Rails server/console:**
213
+ ```bash
214
+ # After running generator
215
+ rails restart
216
+ ```
217
+
218
+ 2. **Check generated concern:**
219
+ ```ruby
220
+ # app/models/concerns/product_vector.rb
221
+ module ProductVector
222
+ extend ActiveSupport::Concern
223
+
224
+ included do
225
+ include Vectra::ActiveRecord
226
+ has_vector :embedding, ...
227
+ end
228
+ end
229
+ ```
230
+
231
+ 3. **Check model includes concern:**
232
+ ```ruby
233
+ # app/models/product.rb
234
+ class Product < ApplicationRecord
235
+ include ProductVector # Must be present
236
+ end
237
+ ```
238
+
239
+ 4. **Verify initializer:**
240
+ ```ruby
241
+ # config/initializers/vectra.rb
242
+ require 'vectra' # Should be at the top
243
+ Vectra.configure do |config|
244
+ config.provider = :qdrant
245
+ # ...
246
+ end
247
+ ```
248
+
249
+ ## Debugging Tips
250
+
251
+ ### 1. Check what's loaded
252
+
253
+ ```ruby
254
+ # In Rails console
255
+ Vectra.constants
256
+ # => [:Configuration, :Client, :Vector, ...]
257
+
258
+ Vectra::Providers.constants
259
+ # => Should show: [:Base, :Pinecone, :Qdrant, :Weaviate, :Pgvector, :Memory]
260
+
261
+ # Check if specific provider is loaded
262
+ defined?(Vectra::Providers::Qdrant)
263
+ # => "constant" if loaded, nil if not
264
+ ```
265
+
266
+ ### 2. Force load everything
267
+
268
+ ```ruby
269
+ # In Rails console or initializer
270
+ require 'vectra'
271
+ require 'vectra/providers/base'
272
+ require 'vectra/providers/qdrant'
273
+ require 'vectra/providers/pinecone'
274
+ require 'vectra/providers/weaviate'
275
+ require 'vectra/providers/pgvector'
276
+ require 'vectra/providers/memory'
277
+ ```
278
+
279
+ ### 3. Check load order
280
+
281
+ ```ruby
282
+ # In Rails console
283
+ $LOADED_FEATURES.grep(/vectra/)
284
+ # Shows what Vectra files are loaded and in what order
285
+ ```
286
+
287
+ ### 4. Test client creation
288
+
289
+ ```ruby
290
+ # In Rails console
291
+ begin
292
+ client = Vectra::Client.new(provider: :qdrant, host: 'http://localhost:6333')
293
+ puts "✅ Client created successfully"
294
+ puts "Provider: #{client.provider.class}"
295
+ rescue => e
296
+ puts "❌ Error: #{e.class}: #{e.message}"
297
+ puts e.backtrace.first(5)
298
+ end
299
+ ```
300
+
301
+ ## Best Practices for Rails
302
+
303
+ ### 1. Always require Vectra in initializer
304
+
305
+ ```ruby
306
+ # config/initializers/vectra.rb
307
+ require 'vectra' # Always at the top
308
+
309
+ Vectra.configure do |config|
310
+ # Configuration
311
+ end
312
+ ```
313
+
314
+ ### 2. Use the generator for new models
315
+
316
+ ```bash
317
+ rails generate vectra:index ModelName column_name dimension:1536 provider:qdrant
318
+ ```
319
+
320
+ This ensures correct setup.
321
+
322
+ ### 3. Test in Rails console
323
+
324
+ After setup, test in console:
325
+ ```ruby
326
+ rails console
327
+
328
+ # Test configuration
329
+ Vectra.configuration.provider
330
+ # => :qdrant
331
+
332
+ # Test client
333
+ client = Vectra::Client.new
334
+ client.healthy?
335
+ # => true
336
+
337
+ # Test model (if using ActiveRecord)
338
+ Product.vector_search([0.1, 0.2, ...], limit: 5)
339
+ ```
340
+
341
+ ### 4. Check logs
342
+
343
+ Enable logging to see what's happening:
344
+ ```ruby
345
+ # config/initializers/vectra.rb
346
+ Vectra.configure do |config|
347
+ config.logger = Rails.logger
348
+ config.logger.level = :debug # In development
349
+ end
350
+ ```
351
+
352
+ ## Still Having Issues?
353
+
354
+ 1. **Check Vectra version:**
355
+ ```bash
356
+ bundle show vectra-client
357
+ ```
358
+
359
+ 2. **Update to latest:**
360
+ ```bash
361
+ bundle update vectra-client
362
+ ```
363
+
364
+ 3. **Check Rails version:**
365
+ ```bash
366
+ rails --version
367
+ ```
368
+
369
+ 4. **Create minimal reproduction:**
370
+ - New Rails app
371
+ - Add Vectra
372
+ - Reproduce issue
373
+ - Share steps
374
+
375
+ 5. **Open an issue:**
376
+ - [GitHub Issues](https://github.com/stokry/vectra/issues)
377
+ - Include Rails version, Vectra version, error message, and stack trace
@@ -4,6 +4,9 @@
4
4
  #
5
5
  # For more information see: https://github.com/stokry/vectra
6
6
 
7
+ # Ensure Vectra and all Providers are loaded (important for Rails autoloading)
8
+ require 'vectra'
9
+
7
10
  Vectra.configure do |config|
8
11
  # Provider configuration
9
12
  config.provider = :<%= options[:provider] %>
@@ -2,6 +2,9 @@
2
2
 
3
3
  require "active_support/concern"
4
4
 
5
+ # Ensure Client and Providers are loaded (for Rails autoloading compatibility)
6
+ require_relative "client" unless defined?(Vectra::Client)
7
+
5
8
  module Vectra
6
9
  # ActiveRecord integration for vector embeddings
7
10
  #
data/lib/vectra/client.rb CHANGED
@@ -4,6 +4,14 @@
4
4
  require_relative "health_check" unless defined?(Vectra::HealthCheck)
5
5
  require_relative "configuration" unless defined?(Vectra::Configuration)
6
6
 
7
+ # Ensure Providers are loaded before Client (for Rails autoloading compatibility)
8
+ require_relative "providers/base" unless defined?(Vectra::Providers::Base)
9
+ require_relative "providers/pinecone" unless defined?(Vectra::Providers::Pinecone)
10
+ require_relative "providers/qdrant" unless defined?(Vectra::Providers::Qdrant)
11
+ require_relative "providers/weaviate" unless defined?(Vectra::Providers::Weaviate)
12
+ require_relative "providers/pgvector" unless defined?(Vectra::Providers::Pgvector)
13
+ require_relative "providers/memory" unless defined?(Vectra::Providers::Memory)
14
+
7
15
  module Vectra
8
16
  # Unified client for vector database operations
9
17
  #
@@ -517,15 +525,15 @@ module Vectra
517
525
  def build_provider
518
526
  case config.provider
519
527
  when :pinecone
520
- Providers::Pinecone.new(config)
528
+ Vectra::Providers::Pinecone.new(config)
521
529
  when :qdrant
522
- Providers::Qdrant.new(config)
530
+ Vectra::Providers::Qdrant.new(config)
523
531
  when :weaviate
524
- Providers::Weaviate.new(config)
532
+ Vectra::Providers::Weaviate.new(config)
525
533
  when :pgvector
526
- Providers::Pgvector.new(config)
534
+ Vectra::Providers::Pgvector.new(config)
527
535
  when :memory
528
- Providers::Memory.new(config)
536
+ Vectra::Providers::Memory.new(config)
529
537
  else
530
538
  raise UnsupportedProviderError, "Provider '#{config.provider}' is not supported"
531
539
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vectra
4
- VERSION = "1.0.4"
4
+ VERSION = "1.0.6"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vectra-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mijo Kristo
@@ -267,6 +267,8 @@ files:
267
267
  - docs/guides/installation.md
268
268
  - docs/guides/monitoring.md
269
269
  - docs/guides/performance.md
270
+ - docs/guides/rails-integration.md
271
+ - docs/guides/rails-troubleshooting.md
270
272
  - docs/guides/runbooks/cache-issues.md
271
273
  - docs/guides/runbooks/high-error-rate.md
272
274
  - docs/guides/runbooks/high-latency.md