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 +4 -4
- data/CHANGELOG.md +13 -4
- data/README.md +13 -0
- data/docs/guides/getting-started.md +13 -0
- data/docs/guides/rails-integration.md +690 -0
- data/docs/guides/rails-troubleshooting.md +377 -0
- data/lib/generators/vectra/templates/vectra.rb +3 -0
- data/lib/vectra/active_record.rb +3 -0
- data/lib/vectra/client.rb +13 -5
- data/lib/vectra/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aada098f943da3a1b81288f46e32382e775492efc96e87a175b1e9ba002ea7c3
|
|
4
|
+
data.tar.gz: 24f2efb3bdf110541c60ce4710f61e616fc824bf4874d8205ff5d1ebdaa8d9bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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] %>
|
data/lib/vectra/active_record.rb
CHANGED
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
|
data/lib/vectra/version.rb
CHANGED
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
|
+
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
|