vectra-client 0.2.2 → 0.3.0

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.
@@ -6,27 +6,49 @@ permalink: /examples/
6
6
 
7
7
  # Code Examples
8
8
 
9
- Explore practical examples to get started with Vectra:
10
-
11
- ## Basic Examples
12
-
13
- - [Basic Usage]({{ site.baseurl }}/examples/basic-usage/) - Simple searches and operations
14
- - [Batch Operations]({{ site.baseurl }}/examples/basic-usage/#batch-operations) - Working with multiple vectors
15
- - [Error Handling]({{ site.baseurl }}/examples/basic-usage/#error-handling) - Proper error handling patterns
16
-
17
- ## Provider-Specific Examples
18
-
19
- - [Pinecone Example]({{ site.baseurl }}/providers/pinecone/#example)
20
- - [Qdrant Example]({{ site.baseurl }}/providers/qdrant/#example)
21
- - [Weaviate Example]({{ site.baseurl }}/providers/weaviate/#example)
22
- - [pgvector Example]({{ site.baseurl }}/providers/pgvector/#example)
23
-
24
- ## Rails Integration
25
-
26
- Check out the [pgvector guide]({{ site.baseurl }}/providers/pgvector/) for Rails ActiveRecord integration examples.
27
-
28
- ## Need More Examples?
29
-
30
- - Check the [GitHub Repository](https://github.com/stokry/vectra/tree/main/examples)
31
- - See the [Implementation Guide](https://github.com/stokry/vectra/blob/main/IMPLEMENTATION_GUIDE.md)
32
- - Review [Integration Tests](https://github.com/stokry/vectra/tree/main/spec/integration)
9
+ Practical examples to get started with Vectra.
10
+
11
+ ## Quick Examples
12
+
13
+ <div class="tma-comparison-grid">
14
+ <div class="tma-comparison-card">
15
+ <h4>Basic Usage</h4>
16
+ <p>Simple searches and CRUD operations</p>
17
+ <a href="{{ site.baseurl }}/examples/basic-usage/">View Guide →</a>
18
+ </div>
19
+ <div class="tma-comparison-card">
20
+ <h4>Rails Integration</h4>
21
+ <p>ActiveRecord integration with has_vector</p>
22
+ <a href="{{ site.baseurl }}/providers/pgvector/">View Guide →</a>
23
+ </div>
24
+ </div>
25
+
26
+ ## Provider Examples
27
+
28
+ <div class="tma-comparison-grid">
29
+ <div class="tma-comparison-card">
30
+ <h4>Pinecone</h4>
31
+ <p>Managed cloud vector database</p>
32
+ <a href="{{ site.baseurl }}/providers/pinecone/">View Guide →</a>
33
+ </div>
34
+ <div class="tma-comparison-card">
35
+ <h4>Qdrant</h4>
36
+ <p>Open source, self-hosted</p>
37
+ <a href="{{ site.baseurl }}/providers/qdrant/">View Guide →</a>
38
+ </div>
39
+ <div class="tma-comparison-card">
40
+ <h4>Weaviate</h4>
41
+ <p>Semantic search with GraphQL</p>
42
+ <a href="{{ site.baseurl }}/providers/weaviate/">View Guide →</a>
43
+ </div>
44
+ <div class="tma-comparison-card">
45
+ <h4>pgvector</h4>
46
+ <p>PostgreSQL with vector support</p>
47
+ <a href="{{ site.baseurl }}/providers/pgvector/">View Guide →</a>
48
+ </div>
49
+ </div>
50
+
51
+ ## More Resources
52
+
53
+ - [GitHub Examples](https://github.com/stokry/vectra/tree/main/examples) - Full example files
54
+ - [Integration Tests](https://github.com/stokry/vectra/tree/main/spec/integration) - Real-world test cases
@@ -0,0 +1,200 @@
1
+ ---
2
+ layout: page
3
+ title: Performance & Optimization
4
+ permalink: /guides/performance/
5
+ ---
6
+
7
+ # Performance & Optimization
8
+
9
+ Vectra provides several performance optimization features for high-throughput applications.
10
+
11
+ ## Async Batch Operations
12
+
13
+ Process large vector sets concurrently with automatic chunking:
14
+
15
+ ```ruby
16
+ require 'vectra'
17
+
18
+ client = Vectra::Client.new(provider: :pinecone, api_key: ENV['PINECONE_API_KEY'])
19
+
20
+ # Create a batch processor with 4 concurrent workers
21
+ batch = Vectra::Batch.new(client, concurrency: 4)
22
+
23
+ # Async upsert with automatic chunking
24
+ vectors = 10_000.times.map { |i| { id: "vec_#{i}", values: Array.new(384) { rand } } }
25
+
26
+ result = batch.upsert_async(
27
+ index: 'my-index',
28
+ vectors: vectors,
29
+ chunk_size: 100
30
+ )
31
+
32
+ puts "Upserted: #{result[:upserted_count]} vectors in #{result[:chunks]} chunks"
33
+ puts "Errors: #{result[:errors].size}" if result[:errors].any?
34
+ ```
35
+
36
+ ### Batch Delete
37
+
38
+ ```ruby
39
+ ids = 1000.times.map { |i| "vec_#{i}" }
40
+
41
+ result = batch.delete_async(
42
+ index: 'my-index',
43
+ ids: ids,
44
+ chunk_size: 100
45
+ )
46
+ ```
47
+
48
+ ### Batch Fetch
49
+
50
+ ```ruby
51
+ ids = ['vec_1', 'vec_2', 'vec_3']
52
+
53
+ vectors = batch.fetch_async(
54
+ index: 'my-index',
55
+ ids: ids,
56
+ chunk_size: 50
57
+ )
58
+ ```
59
+
60
+ ## Streaming Results
61
+
62
+ For large query result sets, use streaming to reduce memory usage:
63
+
64
+ ```ruby
65
+ stream = Vectra::Streaming.new(client, page_size: 100)
66
+
67
+ # Stream with a block
68
+ stream.query_each(
69
+ index: 'my-index',
70
+ vector: query_vector,
71
+ total: 1000
72
+ ) do |match|
73
+ process_match(match)
74
+ end
75
+
76
+ # Or use lazy enumerator
77
+ results = stream.query_stream(
78
+ index: 'my-index',
79
+ vector: query_vector,
80
+ total: 1000
81
+ )
82
+
83
+ # Only fetches what you need
84
+ results.take(50).each { |m| puts m.id }
85
+ ```
86
+
87
+ ## Caching Layer
88
+
89
+ Cache frequently queried vectors to reduce database load:
90
+
91
+ ```ruby
92
+ # Create cache with 5-minute TTL
93
+ cache = Vectra::Cache.new(ttl: 300, max_size: 1000)
94
+
95
+ # Wrap client with caching
96
+ cached_client = Vectra::CachedClient.new(client, cache: cache)
97
+
98
+ # First query hits the database
99
+ result1 = cached_client.query(index: 'idx', vector: vec, top_k: 10)
100
+
101
+ # Second identical query returns cached result
102
+ result2 = cached_client.query(index: 'idx', vector: vec, top_k: 10)
103
+
104
+ # Invalidate cache when data changes
105
+ cached_client.invalidate_index('idx')
106
+
107
+ # Clear all cache
108
+ cached_client.clear_cache
109
+ ```
110
+
111
+ ### Cache Statistics
112
+
113
+ ```ruby
114
+ stats = cache.stats
115
+ puts "Cache size: #{stats[:size]}/#{stats[:max_size]}"
116
+ puts "TTL: #{stats[:ttl]} seconds"
117
+ ```
118
+
119
+ ## Connection Pooling (pgvector)
120
+
121
+ For pgvector, use connection pooling with warmup:
122
+
123
+ ```ruby
124
+ # Configure pool size
125
+ Vectra.configure do |config|
126
+ config.provider = :pgvector
127
+ config.host = ENV['DATABASE_URL']
128
+ config.pool_size = 10
129
+ config.pool_timeout = 5
130
+ end
131
+
132
+ client = Vectra::Client.new
133
+
134
+ # Warmup connections at startup
135
+ client.provider.warmup_pool(5)
136
+
137
+ # Check pool stats
138
+ stats = client.provider.pool_stats
139
+ puts "Available connections: #{stats[:available]}"
140
+ puts "Checked out: #{stats[:checked_out]}"
141
+
142
+ # Shutdown pool when done
143
+ client.provider.shutdown_pool
144
+ ```
145
+
146
+ ## Configuration Options
147
+
148
+ ```ruby
149
+ Vectra.configure do |config|
150
+ # Provider settings
151
+ config.provider = :pinecone
152
+ config.api_key = ENV['PINECONE_API_KEY']
153
+
154
+ # Timeouts
155
+ config.timeout = 30
156
+ config.open_timeout = 10
157
+
158
+ # Retry settings
159
+ config.max_retries = 3
160
+ config.retry_delay = 1
161
+
162
+ # Batch operations
163
+ config.batch_size = 100
164
+ config.async_concurrency = 4
165
+
166
+ # Connection pooling (pgvector)
167
+ config.pool_size = 10
168
+ config.pool_timeout = 5
169
+
170
+ # Caching
171
+ config.cache_enabled = true
172
+ config.cache_ttl = 300
173
+ config.cache_max_size = 1000
174
+ end
175
+ ```
176
+
177
+ ## Benchmarking
178
+
179
+ Run the included benchmarks:
180
+
181
+ ```bash
182
+ # Batch operations benchmark
183
+ bundle exec ruby benchmarks/batch_operations_benchmark.rb
184
+
185
+ # Connection pooling benchmark
186
+ bundle exec ruby benchmarks/connection_pooling_benchmark.rb
187
+ ```
188
+
189
+ ## Best Practices
190
+
191
+ 1. **Batch Size**: Use batch sizes of 100-500 for optimal throughput
192
+ 2. **Concurrency**: Set concurrency to 2-4x your CPU cores
193
+ 3. **Connection Pool**: Size pool to expected concurrent requests + 20%
194
+ 4. **Cache TTL**: Set TTL based on data freshness requirements
195
+ 5. **Warmup**: Always warmup connections in production
196
+
197
+ ## Next Steps
198
+
199
+ - [API Reference]({{ site.baseurl }}/api/overview)
200
+ - [Provider Guides]({{ site.baseurl }}/providers)
data/docs/index.md CHANGED
@@ -3,51 +3,35 @@ layout: home
3
3
  title: Vectra
4
4
  ---
5
5
 
6
- # Welcome to Vectra Documentation
7
-
8
- **Vectra** is a unified Ruby client for vector databases that allows you to write once and switch providers easily.
9
-
10
- ## Supported Vector Databases
11
-
12
- - **Pinecone** - Managed vector database in the cloud
13
- - **Qdrant** - Open-source vector database
14
- - **Weaviate** - Open-source vector search engine
15
- - **PostgreSQL with pgvector** - SQL database with vector support
16
-
17
- ## Quick Links
18
-
19
- - [Installation Guide]({{ site.baseurl }}/guides/installation)
20
- - [Getting Started]({{ site.baseurl }}/guides/getting-started)
21
- - [API Reference]({{ site.baseurl }}/api/overview)
22
- - [Examples]({{ site.baseurl }}/examples/basic-usage)
23
- - [Contributing]({{ site.baseurl }}/community/contributing)
24
-
25
- ## Key Features
26
-
27
- - 🔄 **Provider Agnostic** - Switch between different vector database providers with minimal code changes
28
- - 🚀 **Easy Integration** - Works seamlessly with Rails and other Ruby frameworks
29
- - 📊 **Vector Operations** - Create, search, update, and delete vectors
30
- - 🔌 **Multiple Providers** - Support for leading vector database platforms
31
- - 📈 **Instrumentation** - Built-in support for Datadog and New Relic monitoring
32
- - 🗄️ **ActiveRecord Integration** - Native support for Rails models
33
-
34
- ## Get Started
35
-
36
6
  ```ruby
37
7
  require 'vectra'
38
8
 
39
- # Initialize client
40
- client = Vectra::Client.new(provider: :pinecone, api_key: 'your-key')
9
+ # Initialize any provider with the same API
10
+ client = Vectra::Client.new(
11
+ provider: :pinecone, # or :qdrant, :weaviate, :pgvector
12
+ api_key: ENV['API_KEY'],
13
+ host: 'your-host.example.com'
14
+ )
41
15
 
42
- # Upsert vectors
16
+ # Store vectors with metadata
43
17
  client.upsert(
44
18
  vectors: [
45
- { id: '1', values: [0.1, 0.2, 0.3], metadata: { text: 'example' } }
19
+ {
20
+ id: 'doc-1',
21
+ values: [0.1, 0.2, 0.3, ...], # Your embedding
22
+ metadata: { title: 'Getting Started with AI' }
23
+ }
46
24
  ]
47
25
  )
48
26
 
49
- # Search
50
- results = client.query(vector: [0.1, 0.2, 0.3], top_k: 5)
51
- ```
27
+ # Search by similarity
28
+ results = client.query(
29
+ vector: [0.1, 0.2, 0.3, ...],
30
+ top_k: 10,
31
+ filter: { category: 'tutorials' }
32
+ )
52
33
 
53
- For more detailed examples, see [Basic Usage]({{ site.baseurl }}/examples/basic-usage).
34
+ results.each do |match|
35
+ puts "#{match['id']}: #{match['score']}"
36
+ end
37
+ ```
@@ -6,57 +6,76 @@ permalink: /providers/
6
6
 
7
7
  # Vector Database Providers
8
8
 
9
- Vectra supports multiple vector database providers. Choose the one that best fits your needs:
9
+ Vectra supports multiple vector database providers. Choose the one that best fits your needs.
10
10
 
11
11
  ## Supported Providers
12
12
 
13
- | Provider | Type | Best For | Documentation |
14
- |----------|------|----------|---|
15
- | **Pinecone** | Managed Cloud | Production, Fully managed | [Guide]({{ site.baseurl }}/providers/pinecone) |
16
- | **Qdrant** | Open Source | Self-hosted, High performance | [Guide]({{ site.baseurl }}/providers/qdrant) |
17
- | **Weaviate** | Open Source | Semantic search, GraphQL | [Guide]({{ site.baseurl }}/providers/weaviate) |
18
- | **PostgreSQL + pgvector** | SQL Database | SQL integration, ACID | [Guide]({{ site.baseurl }}/providers/pgvector) |
13
+ | Provider | Type | Best For |
14
+ |----------|------|----------|
15
+ | [**Pinecone**]({{ site.baseurl }}/providers/pinecone) | Managed Cloud | Production, Zero ops |
16
+ | [**Qdrant**]({{ site.baseurl }}/providers/qdrant) | Open Source | Self-hosted, Performance |
17
+ | [**Weaviate**]({{ site.baseurl }}/providers/weaviate) | Open Source | Semantic search, GraphQL |
18
+ | [**pgvector**]({{ site.baseurl }}/providers/pgvector) | PostgreSQL | SQL integration, ACID |
19
19
 
20
20
  ## Quick Comparison
21
21
 
22
- ### Pinecone
23
- - ✅ Fully managed service
24
- - ✅ Easy setup
25
- - ✅ Scalable
26
- - Cloud only
27
- - Paid service
28
-
29
- ### Qdrant
30
- - Open source
31
- - ✅ Self-hosted
32
- - ✅ High performance
33
- - ✅ Multiple deployment options
34
- - ❌ More configuration needed
35
-
36
- ### Weaviate
37
- - ✅ Open source
38
- - Semantic search
39
- - GraphQL API
40
- - Multi-model support
41
- - ❌ More complex
42
-
43
- ### PostgreSQL + pgvector
44
- - ✅ SQL database
45
- - ✅ ACID transactions
46
- - Existing infrastructure
47
- - Affordable
48
- - Not specialized for vectors
22
+ <div class="tma-comparison-grid">
23
+ <div class="tma-comparison-card">
24
+ <h4>Pinecone</h4>
25
+ <ul>
26
+ <li class="pro">Fully managed service</li>
27
+ <li class="pro">Easy setup</li>
28
+ <li class="pro">Highly scalable</li>
29
+ <li class="con">Cloud only</li>
30
+ <li class="con">Paid service</li>
31
+ </ul>
32
+ </div>
33
+ <div class="tma-comparison-card">
34
+ <h4>Qdrant</h4>
35
+ <ul>
36
+ <li class="pro">Open source</li>
37
+ <li class="pro">Self-hosted option</li>
38
+ <li class="pro">High performance</li>
39
+ <li class="pro">Cloud option available</li>
40
+ <li class="con">More configuration</li>
41
+ </ul>
42
+ </div>
43
+ <div class="tma-comparison-card">
44
+ <h4>Weaviate</h4>
45
+ <ul>
46
+ <li class="pro">Open source</li>
47
+ <li class="pro">Semantic search</li>
48
+ <li class="pro">GraphQL API</li>
49
+ <li class="pro">Multi-model support</li>
50
+ <li class="con">More complex setup</li>
51
+ </ul>
52
+ </div>
53
+ <div class="tma-comparison-card">
54
+ <h4>pgvector</h4>
55
+ <ul>
56
+ <li class="pro">SQL database</li>
57
+ <li class="pro">ACID transactions</li>
58
+ <li class="pro">Use existing Postgres</li>
59
+ <li class="pro">Very affordable</li>
60
+ <li class="con">Not vector-specialized</li>
61
+ </ul>
62
+ </div>
63
+ </div>
49
64
 
50
65
  ## Switching Providers
51
66
 
52
67
  One of Vectra's key features is easy provider switching:
53
68
 
54
69
  ```ruby
55
- # All it takes is changing one line!
56
- client = Vectra::Client.new(provider: :qdrant)
70
+ # Just change the provider - your code stays the same!
71
+ client = Vectra::Client.new(provider: :qdrant, host: 'localhost:6333')
57
72
 
58
- # All your code remains the same
59
- results = client.query(vector: [0.1, 0.2, 0.3])
73
+ # All operations work identically
74
+ client.upsert(vectors: [...])
75
+ results = client.query(vector: [...], top_k: 5)
60
76
  ```
61
77
 
62
- See the [Getting Started Guide]({{ site.baseurl }}/guides/getting-started) for more information.
78
+ ## Next Steps
79
+
80
+ - [Getting Started Guide]({{ site.baseurl }}/guides/getting-started)
81
+ - [API Reference]({{ site.baseurl }}/api/overview)
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent"
4
+
5
+ module Vectra
6
+ # Batch operations with concurrent processing
7
+ #
8
+ # Provides async batch upsert capabilities with configurable concurrency
9
+ # and automatic chunking of large vector sets.
10
+ #
11
+ # @example Async batch upsert
12
+ # batch = Vectra::Batch.new(client, concurrency: 4)
13
+ # result = batch.upsert_async(
14
+ # index: 'my-index',
15
+ # vectors: large_vector_array,
16
+ # chunk_size: 100
17
+ # )
18
+ # puts "Upserted: #{result[:upserted_count]}"
19
+ #
20
+ class Batch
21
+ DEFAULT_CONCURRENCY = 4
22
+ DEFAULT_CHUNK_SIZE = 100
23
+
24
+ attr_reader :client, :concurrency
25
+
26
+ # Initialize a new Batch processor
27
+ #
28
+ # @param client [Client] the Vectra client
29
+ # @param concurrency [Integer] max concurrent requests (default: 4)
30
+ def initialize(client, concurrency: DEFAULT_CONCURRENCY)
31
+ @client = client
32
+ @concurrency = [concurrency, 1].max
33
+ end
34
+
35
+ # Perform async batch upsert with concurrent requests
36
+ #
37
+ # @param index [String] the index name
38
+ # @param vectors [Array<Hash>] vectors to upsert
39
+ # @param namespace [String, nil] optional namespace
40
+ # @param chunk_size [Integer] vectors per chunk (default: 100)
41
+ # @return [Hash] aggregated result with :upserted_count, :chunks, :errors
42
+ def upsert_async(index:, vectors:, namespace: nil, chunk_size: DEFAULT_CHUNK_SIZE)
43
+ chunks = vectors.each_slice(chunk_size).to_a
44
+ return { upserted_count: 0, chunks: 0, errors: [] } if chunks.empty?
45
+
46
+ results = process_chunks_concurrently(chunks) do |chunk|
47
+ client.upsert(index: index, vectors: chunk, namespace: namespace)
48
+ end
49
+
50
+ aggregate_results(results, vectors.size)
51
+ end
52
+
53
+ # Perform async batch delete with concurrent requests
54
+ #
55
+ # @param index [String] the index name
56
+ # @param ids [Array<String>] IDs to delete
57
+ # @param namespace [String, nil] optional namespace
58
+ # @param chunk_size [Integer] IDs per chunk (default: 100)
59
+ # @return [Hash] aggregated result
60
+ def delete_async(index:, ids:, namespace: nil, chunk_size: DEFAULT_CHUNK_SIZE)
61
+ chunks = ids.each_slice(chunk_size).to_a
62
+ return { deleted_count: 0, chunks: 0, errors: [] } if chunks.empty?
63
+
64
+ results = process_chunks_concurrently(chunks) do |chunk|
65
+ client.delete(index: index, ids: chunk, namespace: namespace)
66
+ end
67
+
68
+ aggregate_delete_results(results, ids.size)
69
+ end
70
+
71
+ # Perform async batch fetch with concurrent requests
72
+ #
73
+ # @param index [String] the index name
74
+ # @param ids [Array<String>] IDs to fetch
75
+ # @param namespace [String, nil] optional namespace
76
+ # @param chunk_size [Integer] IDs per chunk (default: 100)
77
+ # @return [Hash<String, Vector>] merged results
78
+ def fetch_async(index:, ids:, namespace: nil, chunk_size: DEFAULT_CHUNK_SIZE)
79
+ chunks = ids.each_slice(chunk_size).to_a
80
+ return {} if chunks.empty?
81
+
82
+ results = process_chunks_concurrently(chunks) do |chunk|
83
+ client.fetch(index: index, ids: chunk, namespace: namespace)
84
+ end
85
+
86
+ merge_fetch_results(results)
87
+ end
88
+
89
+ private
90
+
91
+ def process_chunks_concurrently(chunks)
92
+ pool = Concurrent::FixedThreadPool.new(concurrency)
93
+ futures = []
94
+
95
+ chunks.each_with_index do |chunk, index|
96
+ futures << Concurrent::Future.execute(executor: pool) do
97
+ { index: index, result: yield(chunk), error: nil }
98
+ rescue StandardError => e
99
+ { index: index, result: nil, error: e }
100
+ end
101
+ end
102
+
103
+ # Wait for all futures and collect results
104
+ results = futures.map(&:value)
105
+ pool.shutdown
106
+ pool.wait_for_termination(30)
107
+
108
+ results.sort_by { |r| r[:index] }
109
+ end
110
+
111
+ def aggregate_results(results, total_vectors)
112
+ errors = results.select { |r| r[:error] }.map { |r| r[:error] }
113
+ successful = results.reject { |r| r[:error] }
114
+ upserted = successful.sum { |r| r.dig(:result, :upserted_count) || 0 }
115
+
116
+ {
117
+ upserted_count: upserted,
118
+ total_vectors: total_vectors,
119
+ chunks: results.size,
120
+ successful_chunks: successful.size,
121
+ errors: errors
122
+ }
123
+ end
124
+
125
+ def aggregate_delete_results(results, total_ids)
126
+ errors = results.select { |r| r[:error] }.map { |r| r[:error] }
127
+ successful = results.reject { |r| r[:error] }
128
+
129
+ {
130
+ deleted_count: total_ids - (errors.size * (total_ids / results.size.to_f).ceil),
131
+ total_ids: total_ids,
132
+ chunks: results.size,
133
+ successful_chunks: successful.size,
134
+ errors: errors
135
+ }
136
+ end
137
+
138
+ def merge_fetch_results(results)
139
+ merged = {}
140
+ results.each do |r|
141
+ next if r[:error] || r[:result].nil?
142
+
143
+ merged.merge!(r[:result])
144
+ end
145
+ merged
146
+ end
147
+ end
148
+ end