Vector Search¶
Vector search finds documents based on semantic similarity using embeddings. It's the foundation of modern retrieval systems and powers RAG applications.
Overview¶
Vector search works by:
- Converting text to high-dimensional vectors (embeddings)
- Storing vectors in an index
- Finding nearest neighbors using similarity metrics
Query: "machine learning frameworks"
│
▼ (Embedding)
[0.12, -0.45, 0.78, ...]
│
▼ (Similarity Search)
┌─────────────────────────────┐
│ doc1: 0.92 "PyTorch intro" │
│ doc2: 0.87 "TensorFlow..." │
│ doc3: 0.71 "Keras guide" │
└─────────────────────────────┘
Quick Start¶
import "github.com/plexusone/omniretrieve/vector"
// Create index
index := vector.NewIndex(vector.Config{
Dimensions: 384, // Must match embedder
})
// Add documents with embeddings
index.Add("doc1", embedding1)
index.Add("doc2", embedding2)
// Search
results := index.Search(queryEmbedding, 5)
for _, r := range results {
fmt.Printf("%.3f: %s\n", r.Score, r.ID)
}
Configuration¶
index := vector.NewIndex(vector.Config{
Dimensions: 384, // Required: embedding dimensions
Metric: vector.MetricCosine, // Similarity metric
Normalize: true, // Normalize vectors
})
Similarity Metrics¶
| Metric | Use Case | Range |
|---|---|---|
MetricCosine |
Most text embeddings | [-1, 1] |
MetricEuclidean |
Absolute distances | [0, ∞) |
MetricDotProduct |
Normalized embeddings | [-∞, ∞] |
Choosing a Metric¶
// Cosine similarity (default, recommended)
// Best for: text embeddings, sentence transformers
index := vector.NewIndex(vector.Config{
Metric: vector.MetricCosine,
})
// Euclidean distance
// Best for: image embeddings, when magnitude matters
index := vector.NewIndex(vector.Config{
Metric: vector.MetricEuclidean,
})
// Dot product
// Best for: pre-normalized embeddings (e.g., OpenAI)
index := vector.NewIndex(vector.Config{
Metric: vector.MetricDotProduct,
})
Operations¶
Adding Vectors¶
// Add single vector
index.Add("doc1", embedding)
// Add with metadata
index.AddWithMeta("doc1", embedding, map[string]any{
"title": "Document Title",
"date": "2026-06-10",
})
// Batch add
vectors := map[string][]float64{
"doc1": embedding1,
"doc2": embedding2,
"doc3": embedding3,
}
index.AddBatch(vectors)
Searching¶
// Basic search
results := index.Search(queryEmbedding, 10)
// Search with filter
results := index.SearchWithFilter(queryEmbedding, 10, func(id string, meta map[string]any) bool {
return meta["type"] == "article"
})
Updating Vectors¶
Removing Vectors¶
Embeddings¶
Embedding Dimensions¶
Common embedding model dimensions:
| Model | Dimensions |
|---|---|
| OpenAI text-embedding-3-small | 1536 |
| OpenAI text-embedding-3-large | 3072 |
| Cohere embed-v3 | 1024 |
| Sentence Transformers | 384-768 |
Using omnillm¶
import "github.com/plexusone/omnillm"
client := omnillm.NewClient()
embedder := omnillm.NewEmbedder(client, "text-embedding-3-small")
// Embed text
embeddings, err := embedder.Embed(ctx, []string{
"First document",
"Second document",
})
// Add to index
for i, emb := range embeddings {
index.Add(fmt.Sprintf("doc%d", i), emb)
}
Batch Embedding¶
// Embed in batches for efficiency
texts := []string{"doc1", "doc2", "doc3", ...}
batchSize := 100
for i := 0; i < len(texts); i += batchSize {
end := min(i+batchSize, len(texts))
batch := texts[i:end]
embeddings, err := embedder.Embed(ctx, batch)
if err != nil {
return err
}
for j, emb := range embeddings {
index.Add(fmt.Sprintf("doc%d", i+j), emb)
}
}
Similarity Functions¶
Cosine Similarity¶
Euclidean Distance¶
Dot Product¶
Normalization¶
// Normalize vector to unit length
normalized := vector.Normalize(embedding)
// Check if normalized
isUnit := vector.IsNormalized(embedding)
Performance¶
Complexity¶
| Operation | Brute Force | With HNSW* |
|---|---|---|
| Add | O(1) | O(log n) |
| Search | O(n × d) | O(log n × d) |
| Remove | O(1) | O(log n) |
*HNSW available via pgvector provider
Memory Usage¶
// Memory per vector:
// dimensions × 8 bytes (float64) + overhead
//
// 384 dimensions: ~3.1 KB per vector
// 1536 dimensions: ~12.3 KB per vector
Optimization Tips¶
- Batch operations - Add/embed in batches
- Dimension reduction - Use smaller embedding models when possible
- Filter early - Apply metadata filters before similarity search
- Use ANN - For large datasets, use pgvector with HNSW index
Production Considerations¶
Index Persistence¶
For production, use pgvector:
import "github.com/plexusone/omniretrieve/providers/pgvector"
manager, err := pgvector.NewManager(pgvector.Config{
ConnectionString: "postgres://...",
Dimensions: 384,
IndexType: "hnsw", // Approximate nearest neighbor
})
Scaling¶
| Documents | Recommendation |
|---|---|
| < 10,000 | In-memory index |
| 10,000 - 1M | pgvector with HNSW |
| > 1M | Dedicated vector DB (Pinecone, Weaviate) |
See Also¶
- Memory Manager - Collection-based storage
- Hybrid Search - Combine with BM25
- Reranking - Improve result quality
- pgvector Provider - PostgreSQL storage