vectra-client 1.0.6 → 1.0.8
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 +8 -5
- data/README.md +13 -0
- data/docs/_layouts/default.html +1 -0
- data/docs/_layouts/home.html +45 -0
- data/docs/_layouts/page.html +1 -0
- data/docs/api/cheatsheet.md +387 -0
- data/docs/api/methods.md +632 -0
- data/docs/api/overview.md +1 -1
- data/docs/assets/style.css +50 -1
- data/docs/examples/index.md +0 -1
- data/docs/guides/faq.md +215 -0
- data/docs/guides/recipes.md +612 -0
- data/docs/guides/testing.md +211 -0
- data/docs/providers/selection.md +215 -0
- data/lib/vectra/active_record.rb +52 -1
- data/lib/vectra/batch.rb +69 -0
- data/lib/vectra/cache.rb +49 -0
- data/lib/vectra/client.rb +144 -12
- data/lib/vectra/version.rb +1 -1
- metadata +7 -1
data/docs/api/methods.md
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: page
|
|
3
|
+
title: API Methods
|
|
4
|
+
permalink: /api/methods/
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# API Methods Reference
|
|
8
|
+
|
|
9
|
+
Complete reference for all Vectra API methods.
|
|
10
|
+
|
|
11
|
+
> For a quick reference, see the [API Cheatsheet](/api/cheatsheet/).
|
|
12
|
+
> For an overview, see the [API Overview](/api/overview/).
|
|
13
|
+
|
|
14
|
+
## Client Methods
|
|
15
|
+
|
|
16
|
+
### `Vectra::Client.new(options)`
|
|
17
|
+
|
|
18
|
+
Initialize a new Vectra client.
|
|
19
|
+
|
|
20
|
+
**Parameters:**
|
|
21
|
+
- `provider` (Symbol) - Provider name: `:pinecone`, `:qdrant`, `:weaviate`, `:pgvector`, `:memory`
|
|
22
|
+
- `api_key` (String, optional) - API key for cloud providers
|
|
23
|
+
- `host` (String, optional) - Host URL for self-hosted providers
|
|
24
|
+
- `environment` (String, optional) - Environment/region (Pinecone)
|
|
25
|
+
- `index` (String, optional) - Default index name
|
|
26
|
+
- `namespace` (String, optional) - Default namespace
|
|
27
|
+
|
|
28
|
+
**Returns:** `Vectra::Client`
|
|
29
|
+
|
|
30
|
+
**Example:**
|
|
31
|
+
```ruby
|
|
32
|
+
client = Vectra::Client.new(
|
|
33
|
+
provider: :qdrant,
|
|
34
|
+
host: 'http://localhost:6333',
|
|
35
|
+
api_key: ENV['QDRANT_API_KEY']
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### `client.upsert(index:, vectors:, namespace: nil)`
|
|
42
|
+
|
|
43
|
+
Upsert vectors into an index. If a vector with the same ID exists, it will be updated.
|
|
44
|
+
|
|
45
|
+
**Parameters:**
|
|
46
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
47
|
+
- `vectors` (Array<Hash, Vector>) - Array of vector hashes or Vector objects
|
|
48
|
+
- `namespace` (String, optional) - Namespace
|
|
49
|
+
|
|
50
|
+
**Vector Hash Format:**
|
|
51
|
+
```ruby
|
|
52
|
+
{
|
|
53
|
+
id: 'unique-id', # Required
|
|
54
|
+
values: [0.1, 0.2, 0.3], # Required: Array of floats
|
|
55
|
+
metadata: { key: 'value' } # Optional: Hash of metadata
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Returns:** `Hash` with `:upserted_count`
|
|
60
|
+
|
|
61
|
+
**Example:**
|
|
62
|
+
```ruby
|
|
63
|
+
result = client.upsert(
|
|
64
|
+
index: 'documents',
|
|
65
|
+
vectors: [
|
|
66
|
+
{ id: 'doc-1', values: [0.1, 0.2, 0.3], metadata: { title: 'Hello' } },
|
|
67
|
+
{ id: 'doc-2', values: [0.4, 0.5, 0.6], metadata: { title: 'World' } }
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
# => { upserted_count: 2 }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### `client.query(index:, vector:, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true)`
|
|
76
|
+
|
|
77
|
+
Search for similar vectors using cosine similarity.
|
|
78
|
+
|
|
79
|
+
**Parameters:**
|
|
80
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
81
|
+
- `vector` (Array<Float>) - Query vector
|
|
82
|
+
- `top_k` (Integer) - Number of results (default: 10)
|
|
83
|
+
- `namespace` (String, optional) - Namespace
|
|
84
|
+
- `filter` (Hash, optional) - Metadata filter
|
|
85
|
+
- `include_values` (Boolean) - Include vector values in results (default: false)
|
|
86
|
+
- `include_metadata` (Boolean) - Include metadata in results (default: true)
|
|
87
|
+
|
|
88
|
+
**Returns:** `Vectra::QueryResult`
|
|
89
|
+
|
|
90
|
+
**Example:**
|
|
91
|
+
```ruby
|
|
92
|
+
results = client.query(
|
|
93
|
+
index: 'documents',
|
|
94
|
+
vector: [0.1, 0.2, 0.3],
|
|
95
|
+
top_k: 5,
|
|
96
|
+
filter: { category: 'docs' },
|
|
97
|
+
include_metadata: true
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
results.each do |match|
|
|
101
|
+
puts "#{match.id}: #{match.score} - #{match.metadata['title']}"
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**QueryResult Methods:**
|
|
106
|
+
- `results.each` - Iterate over matches
|
|
107
|
+
- `results.size` - Number of matches
|
|
108
|
+
- `results.ids` - Array of match IDs
|
|
109
|
+
- `results.scores` - Array of similarity scores
|
|
110
|
+
- `results.first` - First match (highest score)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### `client.hybrid_search(index:, vector:, text:, alpha: 0.7, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true)`
|
|
115
|
+
|
|
116
|
+
Combine semantic (vector) and keyword (text) search.
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
- `index` (String) - Index/collection name
|
|
120
|
+
- `vector` (Array<Float>) - Query vector for semantic search
|
|
121
|
+
- `text` (String) - Text query for keyword search
|
|
122
|
+
- `alpha` (Float) - Balance between semantic and keyword (0.0 = pure keyword, 1.0 = pure semantic, default: 0.7)
|
|
123
|
+
- `top_k` (Integer) - Number of results (default: 10)
|
|
124
|
+
- `namespace` (String, optional) - Namespace
|
|
125
|
+
- `filter` (Hash, optional) - Metadata filter
|
|
126
|
+
- `include_values` (Boolean) - Include vector values (default: false)
|
|
127
|
+
- `include_metadata` (Boolean) - Include metadata (default: true)
|
|
128
|
+
|
|
129
|
+
**Returns:** `Vectra::QueryResult`
|
|
130
|
+
|
|
131
|
+
**Provider Support:**
|
|
132
|
+
- ✅ Qdrant
|
|
133
|
+
- ✅ Weaviate
|
|
134
|
+
- ✅ pgvector
|
|
135
|
+
- ⚠️ Pinecone (limited support)
|
|
136
|
+
|
|
137
|
+
**Example:**
|
|
138
|
+
```ruby
|
|
139
|
+
results = client.hybrid_search(
|
|
140
|
+
index: 'documents',
|
|
141
|
+
vector: query_embedding,
|
|
142
|
+
text: 'ruby vector search',
|
|
143
|
+
alpha: 0.7, # 70% semantic, 30% keyword
|
|
144
|
+
top_k: 10
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### `client.fetch(index:, ids:, namespace: nil)`
|
|
151
|
+
|
|
152
|
+
Fetch vectors by their IDs.
|
|
153
|
+
|
|
154
|
+
**Parameters:**
|
|
155
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
156
|
+
- `ids` (Array<String>) - Array of vector IDs
|
|
157
|
+
- `namespace` (String, optional) - Namespace
|
|
158
|
+
|
|
159
|
+
**Returns:** `Hash<String, Vectra::Vector>` - Hash mapping ID to Vector object
|
|
160
|
+
|
|
161
|
+
**Example:**
|
|
162
|
+
```ruby
|
|
163
|
+
vectors = client.fetch(
|
|
164
|
+
index: 'documents',
|
|
165
|
+
ids: ['doc-1', 'doc-2']
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
vectors['doc-1'].values # => [0.1, 0.2, 0.3]
|
|
169
|
+
vectors['doc-1'].metadata # => { 'title' => 'Hello' }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### `client.update(index:, id:, metadata: nil, values: nil, namespace: nil)`
|
|
175
|
+
|
|
176
|
+
Update a vector's metadata or values.
|
|
177
|
+
|
|
178
|
+
**Parameters:**
|
|
179
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
180
|
+
- `id` (String) - Vector ID
|
|
181
|
+
- `metadata` (Hash, optional) - New metadata (merged with existing)
|
|
182
|
+
- `values` (Array<Float>, optional) - New vector values
|
|
183
|
+
- `namespace` (String, optional) - Namespace
|
|
184
|
+
|
|
185
|
+
**Returns:** `Hash` with `:updated`
|
|
186
|
+
|
|
187
|
+
**Note:** Must provide either `metadata` or `values` (or both).
|
|
188
|
+
|
|
189
|
+
**Example:**
|
|
190
|
+
```ruby
|
|
191
|
+
client.update(
|
|
192
|
+
index: 'documents',
|
|
193
|
+
id: 'doc-1',
|
|
194
|
+
metadata: { category: 'updated', status: 'published' }
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### `client.delete(index:, ids: nil, namespace: nil, filter: nil, delete_all: false)`
|
|
201
|
+
|
|
202
|
+
Delete vectors.
|
|
203
|
+
|
|
204
|
+
**Parameters:**
|
|
205
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
206
|
+
- `ids` (Array<String>, optional) - Vector IDs to delete
|
|
207
|
+
- `namespace` (String, optional) - Namespace
|
|
208
|
+
- `filter` (Hash, optional) - Delete by metadata filter
|
|
209
|
+
- `delete_all` (Boolean) - Delete all vectors in namespace (default: false)
|
|
210
|
+
|
|
211
|
+
**Returns:** `Hash` with `:deleted`
|
|
212
|
+
|
|
213
|
+
**Note:** Must provide `ids`, `filter`, or `delete_all: true`.
|
|
214
|
+
|
|
215
|
+
**Example:**
|
|
216
|
+
```ruby
|
|
217
|
+
# Delete by IDs
|
|
218
|
+
client.delete(index: 'documents', ids: ['doc-1', 'doc-2'])
|
|
219
|
+
|
|
220
|
+
# Delete by filter
|
|
221
|
+
client.delete(index: 'documents', filter: { category: 'old' })
|
|
222
|
+
|
|
223
|
+
# Delete all
|
|
224
|
+
client.delete(index: 'documents', delete_all: true)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### `client.stats(index:, namespace: nil)`
|
|
230
|
+
|
|
231
|
+
Get index statistics.
|
|
232
|
+
|
|
233
|
+
**Parameters:**
|
|
234
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
235
|
+
- `namespace` (String, optional) - Namespace
|
|
236
|
+
|
|
237
|
+
**Returns:** `Hash` with statistics:
|
|
238
|
+
- `dimension` (Integer) - Vector dimension
|
|
239
|
+
- `total_vector_count` (Integer) - Total number of vectors
|
|
240
|
+
- `namespaces` (Hash, optional) - Namespace breakdown with vector counts
|
|
241
|
+
|
|
242
|
+
**Example:**
|
|
243
|
+
```ruby
|
|
244
|
+
stats = client.stats(index: 'documents')
|
|
245
|
+
# => { dimension: 1536, total_vector_count: 1000, namespaces: { "default" => { vector_count: 1000 } } }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
### `client.create_index(name:, dimension:, metric: "cosine", **options)`
|
|
251
|
+
|
|
252
|
+
Create a new index/collection.
|
|
253
|
+
|
|
254
|
+
**Parameters:**
|
|
255
|
+
- `name` (String) - Index name
|
|
256
|
+
- `dimension` (Integer) - Vector dimension
|
|
257
|
+
- `metric` (String) - Distance metric: `"cosine"` (default), `"euclidean"`, `"dot_product"`
|
|
258
|
+
- `options` (Hash) - Provider-specific options (e.g., `on_disk: true` for Qdrant)
|
|
259
|
+
|
|
260
|
+
**Returns:** `Hash` with index information
|
|
261
|
+
|
|
262
|
+
**Note:** Not all providers support index creation. Raises `NotImplementedError` if provider doesn't support it (e.g., Memory, Weaviate).
|
|
263
|
+
|
|
264
|
+
**Example:**
|
|
265
|
+
```ruby
|
|
266
|
+
# Create index with default cosine metric
|
|
267
|
+
result = client.create_index(name: 'documents', dimension: 384)
|
|
268
|
+
# => { name: 'documents', dimension: 384, metric: 'cosine', status: 'ready' }
|
|
269
|
+
|
|
270
|
+
# Create with custom metric (Qdrant)
|
|
271
|
+
result = client.create_index(
|
|
272
|
+
name: 'products',
|
|
273
|
+
dimension: 1536,
|
|
274
|
+
metric: 'euclidean',
|
|
275
|
+
on_disk: true
|
|
276
|
+
)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### `client.delete_index(name:)`
|
|
282
|
+
|
|
283
|
+
Delete an index/collection.
|
|
284
|
+
|
|
285
|
+
**Parameters:**
|
|
286
|
+
- `name` (String) - Index name
|
|
287
|
+
|
|
288
|
+
**Returns:** `Hash` with `:deleted => true`
|
|
289
|
+
|
|
290
|
+
**Note:** Not all providers support index deletion. Raises `NotImplementedError` if provider doesn't support it (e.g., Memory, Weaviate).
|
|
291
|
+
|
|
292
|
+
**Example:**
|
|
293
|
+
```ruby
|
|
294
|
+
result = client.delete_index(name: 'old-index')
|
|
295
|
+
# => { deleted: true }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### `client.list_namespaces(index:)`
|
|
301
|
+
|
|
302
|
+
List all namespaces in an index.
|
|
303
|
+
|
|
304
|
+
**Parameters:**
|
|
305
|
+
- `index` (String) - Index/collection name
|
|
306
|
+
|
|
307
|
+
**Returns:** `Array<String>` - List of namespace names (excludes empty/default namespace)
|
|
308
|
+
|
|
309
|
+
**Example:**
|
|
310
|
+
```ruby
|
|
311
|
+
namespaces = client.list_namespaces(index: 'documents')
|
|
312
|
+
# => ["tenant-1", "tenant-2", "tenant-3"]
|
|
313
|
+
|
|
314
|
+
namespaces.each do |ns|
|
|
315
|
+
stats = client.stats(index: 'documents', namespace: ns)
|
|
316
|
+
puts "Namespace #{ns}: #{stats[:total_vector_count]} vectors"
|
|
317
|
+
end
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Health & Monitoring Methods
|
|
323
|
+
|
|
324
|
+
### `client.healthy?`
|
|
325
|
+
|
|
326
|
+
Quick health check - returns true if provider connection is healthy.
|
|
327
|
+
|
|
328
|
+
**Returns:** `Boolean`
|
|
329
|
+
|
|
330
|
+
**Example:**
|
|
331
|
+
```ruby
|
|
332
|
+
if client.healthy?
|
|
333
|
+
client.upsert(...)
|
|
334
|
+
else
|
|
335
|
+
Rails.logger.warn('Vectra provider is unhealthy')
|
|
336
|
+
end
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
### `client.ping`
|
|
342
|
+
|
|
343
|
+
Ping provider and get connection health status with latency.
|
|
344
|
+
|
|
345
|
+
**Returns:** `Hash` with:
|
|
346
|
+
- `healthy` (Boolean) - Health status
|
|
347
|
+
- `provider` (Symbol) - Provider name
|
|
348
|
+
- `latency_ms` (Float) - Latency in milliseconds
|
|
349
|
+
|
|
350
|
+
**Example:**
|
|
351
|
+
```ruby
|
|
352
|
+
status = client.ping
|
|
353
|
+
# => { healthy: true, provider: :qdrant, latency_ms: 23.4 }
|
|
354
|
+
|
|
355
|
+
puts "Latency: #{status[:latency_ms]}ms"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### `client.health_check`
|
|
361
|
+
|
|
362
|
+
Detailed health check with provider-specific information.
|
|
363
|
+
|
|
364
|
+
**Returns:** `Hash` with detailed health information
|
|
365
|
+
|
|
366
|
+
**Example:**
|
|
367
|
+
```ruby
|
|
368
|
+
health = client.health_check
|
|
369
|
+
# => { healthy: true, provider: :qdrant, version: '1.7.0', ... }
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Vector Helper Methods
|
|
375
|
+
|
|
376
|
+
### `Vectra::Vector.normalize(vector, type: :l2)`
|
|
377
|
+
|
|
378
|
+
Normalize a vector array (non-mutating).
|
|
379
|
+
|
|
380
|
+
**Parameters:**
|
|
381
|
+
- `vector` (Array<Float>) - Vector values to normalize
|
|
382
|
+
- `type` (Symbol) - Normalization type: `:l2` (default) or `:l1`
|
|
383
|
+
|
|
384
|
+
**Returns:** `Array<Float>` - Normalized vector
|
|
385
|
+
|
|
386
|
+
**Example:**
|
|
387
|
+
```ruby
|
|
388
|
+
embedding = openai_response['data'][0]['embedding']
|
|
389
|
+
normalized = Vectra::Vector.normalize(embedding, type: :l2)
|
|
390
|
+
client.upsert(vectors: [{ id: 'doc-1', values: normalized }])
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
### `vector.normalize!(type: :l2)`
|
|
396
|
+
|
|
397
|
+
Normalize vector in-place (mutates the vector).
|
|
398
|
+
|
|
399
|
+
**Parameters:**
|
|
400
|
+
- `type` (Symbol) - Normalization type: `:l2` (default) or `:l1`
|
|
401
|
+
|
|
402
|
+
**Returns:** `Self` (for method chaining)
|
|
403
|
+
|
|
404
|
+
**Example:**
|
|
405
|
+
```ruby
|
|
406
|
+
vector = Vectra::Vector.new(id: 'doc-1', values: embedding)
|
|
407
|
+
vector.normalize! # L2 normalization
|
|
408
|
+
client.upsert(vectors: [vector])
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Batch Operations
|
|
414
|
+
|
|
415
|
+
### `Vectra::Batch.upsert(client:, index:, vectors:, batch_size: 100, namespace: nil, on_progress: nil)`
|
|
416
|
+
|
|
417
|
+
Upsert vectors in batches with progress callbacks.
|
|
418
|
+
|
|
419
|
+
**Parameters:**
|
|
420
|
+
- `client` (Vectra::Client) - Vectra client
|
|
421
|
+
- `index` (String) - Index/collection name
|
|
422
|
+
- `vectors` (Array<Hash, Vector>) - Vectors to upsert
|
|
423
|
+
- `batch_size` (Integer) - Batch size (default: 100)
|
|
424
|
+
- `namespace` (String, optional) - Namespace
|
|
425
|
+
- `on_progress` (Proc, optional) - Progress callback: `->(batch_index, total_batches, batch_count) { ... }`
|
|
426
|
+
|
|
427
|
+
**Returns:** `Hash` with `:upserted_count`
|
|
428
|
+
|
|
429
|
+
**Example:**
|
|
430
|
+
```ruby
|
|
431
|
+
Vectra::Batch.upsert(
|
|
432
|
+
client: client,
|
|
433
|
+
index: 'products',
|
|
434
|
+
vectors: product_vectors,
|
|
435
|
+
batch_size: 100,
|
|
436
|
+
on_progress: ->(batch_index, total_batches, batch_count) do
|
|
437
|
+
puts "Batch #{batch_index + 1}/#{total_batches} (#{batch_count} vectors)"
|
|
438
|
+
end
|
|
439
|
+
)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
### `batch.query_async(index:, vectors:, top_k: 10, namespace: nil, filter: nil, include_values: false, include_metadata: true, chunk_size: 10, on_progress: nil)`
|
|
445
|
+
|
|
446
|
+
Query multiple vectors concurrently (useful for recommendation engines).
|
|
447
|
+
|
|
448
|
+
**Parameters:**
|
|
449
|
+
- `index` (String) - Index/collection name
|
|
450
|
+
- `vectors` (Array<Array<Float>>) - Array of query vectors
|
|
451
|
+
- `top_k` (Integer) - Number of results per query (default: 10)
|
|
452
|
+
- `namespace` (String, optional) - Namespace
|
|
453
|
+
- `filter` (Hash, optional) - Metadata filter
|
|
454
|
+
- `include_values` (Boolean) - Include vector values in results (default: false)
|
|
455
|
+
- `include_metadata` (Boolean) - Include metadata in results (default: true)
|
|
456
|
+
- `chunk_size` (Integer) - Queries per chunk for progress tracking (default: 10)
|
|
457
|
+
- `on_progress` (Proc, optional) - Progress callback: `->(stats) { ... }`
|
|
458
|
+
|
|
459
|
+
**Returns:** `Array<QueryResult>` - One QueryResult per input vector
|
|
460
|
+
|
|
461
|
+
**Example:**
|
|
462
|
+
```ruby
|
|
463
|
+
batch = Vectra::Batch.new(client, concurrency: 4)
|
|
464
|
+
|
|
465
|
+
# Find similar items for multiple products
|
|
466
|
+
product_embeddings = products.map(&:embedding)
|
|
467
|
+
results = batch.query_async(
|
|
468
|
+
index: 'products',
|
|
469
|
+
vectors: product_embeddings,
|
|
470
|
+
top_k: 5,
|
|
471
|
+
on_progress: ->(stats) do
|
|
472
|
+
puts "Processed #{stats[:processed]}/#{stats[:total]} queries (#{stats[:percentage]}%)"
|
|
473
|
+
end
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# Each result corresponds to one product
|
|
477
|
+
results.each_with_index do |result, i|
|
|
478
|
+
puts "Similar to product #{i}: #{result.ids}"
|
|
479
|
+
end
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Query Builder (Chainable API)
|
|
485
|
+
|
|
486
|
+
### `client.query(index)`
|
|
487
|
+
|
|
488
|
+
Start a chainable query builder.
|
|
489
|
+
|
|
490
|
+
**Returns:** `Vectra::QueryBuilder`
|
|
491
|
+
|
|
492
|
+
**Example:**
|
|
493
|
+
```ruby
|
|
494
|
+
results = client
|
|
495
|
+
.query('documents')
|
|
496
|
+
.vector([0.1, 0.2, 0.3])
|
|
497
|
+
.top_k(10)
|
|
498
|
+
.filter(category: 'docs')
|
|
499
|
+
.with_metadata
|
|
500
|
+
.execute
|
|
501
|
+
|
|
502
|
+
results.each do |match|
|
|
503
|
+
puts "#{match.id}: #{match.score}"
|
|
504
|
+
end
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**QueryBuilder Methods:**
|
|
508
|
+
- `.vector(array)` - Set query vector
|
|
509
|
+
- `.top_k(integer)` - Set number of results
|
|
510
|
+
- `.filter(hash)` - Set metadata filter
|
|
511
|
+
- `.namespace(string)` - Set namespace
|
|
512
|
+
- `.with_metadata` - Include metadata in results
|
|
513
|
+
- `.with_values` - Include vector values in results
|
|
514
|
+
- `.execute` - Execute query and return QueryResult
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## ActiveRecord Methods
|
|
519
|
+
|
|
520
|
+
### `has_vector(column_name, options)`
|
|
521
|
+
|
|
522
|
+
Define vector search on an ActiveRecord model.
|
|
523
|
+
|
|
524
|
+
**Parameters:**
|
|
525
|
+
- `column_name` (Symbol) - Column name (e.g., `:embedding`)
|
|
526
|
+
- `provider` (Symbol) - Provider name (default: from global config)
|
|
527
|
+
- `index` (String) - Index/collection name
|
|
528
|
+
- `dimension` (Integer) - Vector dimension
|
|
529
|
+
- `auto_index` (Boolean) - Auto-index on save (default: true)
|
|
530
|
+
- `metadata_fields` (Array<Symbol>) - Fields to include in metadata
|
|
531
|
+
|
|
532
|
+
**Example:**
|
|
533
|
+
```ruby
|
|
534
|
+
class Document < ApplicationRecord
|
|
535
|
+
include Vectra::ActiveRecord
|
|
536
|
+
|
|
537
|
+
has_vector :embedding,
|
|
538
|
+
provider: :qdrant,
|
|
539
|
+
index: 'documents',
|
|
540
|
+
dimension: 1536,
|
|
541
|
+
auto_index: true,
|
|
542
|
+
metadata_fields: [:title, :category]
|
|
543
|
+
end
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
### `Model.vector_search(embedding:, limit: 10, filter: nil)`
|
|
549
|
+
|
|
550
|
+
Search for similar records using vector similarity.
|
|
551
|
+
|
|
552
|
+
**Parameters:**
|
|
553
|
+
- `embedding` (Array<Float>) - Query vector
|
|
554
|
+
- `limit` (Integer) - Number of results (default: 10)
|
|
555
|
+
- `filter` (Hash, optional) - Metadata filter
|
|
556
|
+
|
|
557
|
+
**Returns:** `ActiveRecord::Relation`
|
|
558
|
+
|
|
559
|
+
**Example:**
|
|
560
|
+
```ruby
|
|
561
|
+
results = Document.vector_search(
|
|
562
|
+
embedding: query_embedding,
|
|
563
|
+
limit: 10,
|
|
564
|
+
filter: { category: 'docs' }
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
results.each do |doc|
|
|
568
|
+
puts doc.title
|
|
569
|
+
end
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
### `Model.reindex_vectors(scope: all, batch_size: 1000, on_progress: nil)`
|
|
575
|
+
|
|
576
|
+
Reindex all records for a model into the configured vector index.
|
|
577
|
+
|
|
578
|
+
**Parameters:**
|
|
579
|
+
- `scope` (ActiveRecord::Relation) - Records to reindex (default: `Model.all`)
|
|
580
|
+
- `batch_size` (Integer) - Number of records per batch (default: 1000)
|
|
581
|
+
- `on_progress` (Proc, optional) - Progress callback, receives a hash with `:processed` and `:total`
|
|
582
|
+
|
|
583
|
+
**Returns:** `Integer` - Number of records processed
|
|
584
|
+
|
|
585
|
+
**Example:**
|
|
586
|
+
```ruby
|
|
587
|
+
# Reindex all products with embeddings
|
|
588
|
+
processed = Product.reindex_vectors(
|
|
589
|
+
scope: Product.where.not(embedding: nil),
|
|
590
|
+
batch_size: 500
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
puts "Reindexed #{processed} products"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
## Error Handling
|
|
599
|
+
|
|
600
|
+
Vectra defines specific error types:
|
|
601
|
+
|
|
602
|
+
- `Vectra::Error` - Base error class
|
|
603
|
+
- `Vectra::NotFoundError` - Index or vector not found
|
|
604
|
+
- `Vectra::ValidationError` - Invalid parameters
|
|
605
|
+
- `Vectra::RateLimitError` - Rate limit exceeded
|
|
606
|
+
- `Vectra::ConnectionError` - Connection failed
|
|
607
|
+
- `Vectra::TimeoutError` - Request timeout
|
|
608
|
+
- `Vectra::AuthenticationError` - Authentication failed
|
|
609
|
+
- `Vectra::ConfigurationError` - Configuration error
|
|
610
|
+
- `Vectra::ProviderError` - Provider-specific error
|
|
611
|
+
|
|
612
|
+
**Example:**
|
|
613
|
+
```ruby
|
|
614
|
+
begin
|
|
615
|
+
client.query(index: 'missing', vector: [0.1, 0.2, 0.3])
|
|
616
|
+
rescue Vectra::NotFoundError => e
|
|
617
|
+
Rails.logger.warn("Index not found: #{e.message}")
|
|
618
|
+
rescue Vectra::RateLimitError => e
|
|
619
|
+
Rails.logger.error("Rate limited: #{e.message}")
|
|
620
|
+
rescue Vectra::Error => e
|
|
621
|
+
Rails.logger.error("Vectra error: #{e.message}")
|
|
622
|
+
end
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## See Also
|
|
628
|
+
|
|
629
|
+
- [API Cheatsheet](/api/cheatsheet/) - Quick reference
|
|
630
|
+
- [API Overview](/api/overview/) - Overview and examples
|
|
631
|
+
- [Recipes & Patterns](/guides/recipes/) - Real-world examples
|
|
632
|
+
- [Rails Integration Guide](/guides/rails-integration/) - ActiveRecord integration
|
data/docs/api/overview.md
CHANGED
data/docs/assets/style.css
CHANGED
|
@@ -1033,6 +1033,54 @@ code {
|
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
|
+
/* Recipes Section */
|
|
1037
|
+
.tma-recipes {
|
|
1038
|
+
padding: var(--tma-spacing-3xl) var(--tma-spacing-xl);
|
|
1039
|
+
max-width: var(--tma-max-width);
|
|
1040
|
+
margin: 0 auto;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.tma-recipes__grid {
|
|
1044
|
+
display: grid;
|
|
1045
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
1046
|
+
gap: var(--tma-spacing-lg);
|
|
1047
|
+
margin-top: var(--tma-spacing-xl);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.tma-recipe-card {
|
|
1051
|
+
display: flex;
|
|
1052
|
+
flex-direction: column;
|
|
1053
|
+
padding: var(--tma-spacing-xl);
|
|
1054
|
+
background: var(--tma-color-bg-secondary);
|
|
1055
|
+
border: 1px solid var(--tma-color-border);
|
|
1056
|
+
border-radius: var(--tma-radius-lg);
|
|
1057
|
+
text-decoration: none;
|
|
1058
|
+
color: inherit;
|
|
1059
|
+
transition: all var(--tma-transition-normal);
|
|
1060
|
+
border-left: 3px solid var(--tma-color-accent-primary);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.tma-recipe-card:hover {
|
|
1064
|
+
border-color: var(--tma-color-border-hover);
|
|
1065
|
+
transform: translateY(-4px);
|
|
1066
|
+
box-shadow: var(--tma-shadow-glow);
|
|
1067
|
+
background: var(--tma-color-bg-tertiary);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
.tma-recipe-card__title {
|
|
1071
|
+
font-size: 1.2rem;
|
|
1072
|
+
font-weight: 700;
|
|
1073
|
+
margin-bottom: var(--tma-spacing-sm);
|
|
1074
|
+
color: var(--tma-color-text-primary);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
.tma-recipe-card__description {
|
|
1078
|
+
color: var(--tma-color-text-secondary);
|
|
1079
|
+
font-size: 0.9rem;
|
|
1080
|
+
line-height: 1.6;
|
|
1081
|
+
flex-grow: 1;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1036
1084
|
/* Responsive Comparison Table */
|
|
1037
1085
|
@media (max-width: 1024px) {
|
|
1038
1086
|
.tma-comparison-table {
|
|
@@ -1056,7 +1104,8 @@ code {
|
|
|
1056
1104
|
}
|
|
1057
1105
|
|
|
1058
1106
|
.tma-advantages__grid,
|
|
1059
|
-
.tma-choose__grid
|
|
1107
|
+
.tma-choose__grid,
|
|
1108
|
+
.tma-recipes__grid {
|
|
1060
1109
|
grid-template-columns: 1fr;
|
|
1061
1110
|
}
|
|
1062
1111
|
}
|
data/docs/examples/index.md
CHANGED