Examples¶
Let's explore some real-world examples of how to use Quiver in different scenarios. These examples will help you understand how to integrate Quiver into your applications and get the most out of its features. 🚀
Semantic Search Engine¶
This example shows how to build a simple semantic search engine for documents:
package main
import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"github.com/TFMV/quiver"
"github.com/sashabaranov/go-openai"
"go.uber.org/zap"
)
func main() {
// Create a logger
logger, _ := zap.NewDevelopment()
// Initialize Quiver
idx, _ := quiver.New(quiver.Config{
Dimension: 1536, // OpenAI embeddings dimension
StoragePath: "./data/documents.db",
Distance: quiver.Cosine,
}, logger)
defer idx.Close()
// Initialize OpenAI client
openaiClient := openai.NewClient("your-api-key")
// Index documents
docs, _ := loadDocuments("./documents")
for i, doc := range docs {
// Get embedding from OpenAI
resp, _ := openaiClient.CreateEmbeddings(
context.Background(),
openai.EmbeddingRequest{
Input: []string{doc.Content},
Model: openai.AdaEmbeddingV2,
},
)
// Convert to float32
vector := make([]float32, len(resp.Data[0].Embedding))
for i, v := range resp.Data[0].Embedding {
vector[i] = float32(v)
}
// Add to Quiver
idx.Add(uint64(i), vector, map[string]interface{}{
"category": "document",
"title": doc.Title,
"path": doc.Path,
"tags": doc.Tags,
})
}
// Search function
searchDocuments := func(query string) {
// Get embedding for query
resp, _ := openaiClient.CreateEmbeddings(
context.Background(),
openai.EmbeddingRequest{
Input: []string{query},
Model: openai.AdaEmbeddingV2,
},
)
// Convert to float32
queryVector := make([]float32, len(resp.Data[0].Embedding))
for i, v := range resp.Data[0].Embedding {
queryVector[i] = float32(v)
}
// Search
results, _ := idx.Search(queryVector, 5, 1, 5)
// Print results
fmt.Printf("Results for query: %s\n", query)
for i, result := range results {
fmt.Printf("%d. %s (%.4f)\n", i+1, result.Metadata["title"], result.Distance)
}
}
// Example searches
searchDocuments("How does quantum computing work?")
searchDocuments("Latest developments in AI")
}
type Document struct {
Title string
Content string
Path string
Tags []string
}
func loadDocuments(dir string) ([]Document, error) {
// Implementation omitted for brevity
// This would load documents from files in the specified directory
return []Document{}, nil
}
Product Recommendation System¶
This example demonstrates how to build a product recommendation system:
package main
import (
"fmt"
"github.com/TFMV/quiver"
"go.uber.org/zap"
)
func main() {
// Create a logger
logger, _ := zap.NewDevelopment()
// Initialize Quiver
idx, _ := quiver.New(quiver.Config{
Dimension: 128,
StoragePath: "./data/products.db",
Distance: quiver.Cosine,
}, logger)
defer idx.Close()
// Load product embeddings
// In a real system, these would come from a model trained on product features
products := loadProductEmbeddings("./data/product_embeddings.json")
// Add products to the index
for _, product := range products {
idx.Add(product.ID, product.Embedding, map[string]interface{}{
"category": "product",
"name": product.Name,
"description": product.Description,
"price": product.Price,
"brand": product.Brand,
"tags": product.Tags,
})
}
// Get recommendations for a user
userID := uint64(12345)
userProfile := getUserProfile(userID)
// Get products the user has viewed or purchased
viewedProducts := getViewedProducts(userID)
// Create a positive query from the user profile
positiveQuery := userProfile.Embedding
// Create negative queries from products the user has already seen
negativeQueries := make([][]float32, len(viewedProducts))
for i, product := range viewedProducts {
negativeQueries[i] = product.Embedding
}
// Search with negative examples to avoid recommending products the user has already seen
results, _ := idx.SearchWithNegatives(positiveQuery, negativeQueries, 10, 1, 10)
// Filter by price range
results, _ = idx.SearchWithFilter(positiveQuery, 10,
fmt.Sprintf("price >= %f AND price <= %f", userProfile.MinPrice, userProfile.MaxPrice))
// Print recommendations
fmt.Printf("Recommendations for user %d:\n", userID)
for i, result := range results {
fmt.Printf("%d. %s - $%.2f\n",
i+1,
result.Metadata["name"],
result.Metadata["price"])
}
}
// Placeholder functions and types
type Product struct {
ID uint64
Name string
Description string
Price float64
Brand string
Tags []string
Embedding []float32
}
type UserProfile struct {
Embedding []float32
MinPrice float64
MaxPrice float64
}
func loadProductEmbeddings(path string) []Product {
// Implementation omitted for brevity
return []Product{}
}
func getUserProfile(userID uint64) UserProfile {
// Implementation omitted for brevity
return UserProfile{}
}
func getViewedProducts(userID uint64) []Product {
// Implementation omitted for brevity
return []Product{}
}
Image Similarity Search¶
This example shows how to use Quiver for image similarity search:
package main
import (
"fmt"
"image"
_ "image/jpeg"
_ "image/png"
"os"
"path/filepath"
"github.com/TFMV/quiver"
"github.com/nfnt/resize"
"go.uber.org/zap"
"gorgonia.org/tensor"
)
func main() {
// Create a logger
logger, _ := zap.NewDevelopment()
// Initialize Quiver
idx, _ := quiver.New(quiver.Config{
Dimension: 512, // ResNet feature dimension
StoragePath: "./data/images.db",
Distance: quiver.L2, // L2 is better for image features
}, logger)
defer idx.Close()
// Load pre-trained model (placeholder)
model := loadResNetModel()
// Index images
imageDir := "./images"
files, _ := filepath.Glob(filepath.Join(imageDir, "*.jpg"))
for i, file := range files {
// Load and preprocess image
img, _ := loadImage(file)
resized := resize.Resize(224, 224, img, resize.Lanczos3)
// Extract features using the model
features := extractFeatures(model, resized)
// Add to Quiver
idx.Add(uint64(i), features, map[string]interface{}{
"category": "image",
"path": file,
"filename": filepath.Base(file),
})
}
// Search for similar images
queryImage, _ := loadImage("./query.jpg")
queryResized := resize.Resize(224, 224, queryImage, resize.Lanczos3)
queryFeatures := extractFeatures(model, queryResized)
results, _ := idx.Search(queryFeatures, 10, 1, 10)
// Print results
fmt.Println("Similar images:")
for i, result := range results {
fmt.Printf("%d. %s (Distance: %.4f)\n",
i+1,
result.Metadata["filename"],
result.Distance)
}
}
// Placeholder functions
func loadResNetModel() interface{} {
// Implementation omitted for brevity
return nil
}
func loadImage(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
return img, err
}
func extractFeatures(model interface{}, img image.Image) []float32 {
// Implementation omitted for brevity
// This would convert the image to a tensor, run it through the model,
// and return the feature vector
return make([]float32, 512)
}
Real-time Anomaly Detection¶
This example demonstrates using Quiver for real-time anomaly detection:
package main
import (
"fmt"
"time"
"github.com/TFMV/quiver"
"go.uber.org/zap"
)
func main() {
// Create a logger
logger, _ := zap.NewDevelopment()
// Initialize Quiver
idx, _ := quiver.New(quiver.Config{
Dimension: 64,
StoragePath: "./data/anomalies.db",
Distance: quiver.L2,
}, logger)
defer idx.Close()
// Load historical normal patterns
patterns := loadHistoricalPatterns()
// Add normal patterns to the index
for i, pattern := range patterns {
idx.Add(uint64(i), pattern.Features, map[string]interface{}{
"category": "pattern",
"timestamp": pattern.Timestamp,
"source": pattern.Source,
"is_anomaly": false,
})
}
// Anomaly detection function
detectAnomaly := func(newPattern Pattern) bool {
// Search for similar patterns
results, _ := idx.Search(newPattern.Features, 5, 1, 5)
// Calculate average distance to k nearest neighbors
var totalDistance float32
for _, result := range results {
totalDistance += result.Distance
}
avgDistance := totalDistance / float32(len(results))
// If average distance is above threshold, it's an anomaly
threshold := float32(0.8)
isAnomaly := avgDistance > threshold
// Log the result
if isAnomaly {
fmt.Printf("ANOMALY DETECTED! Source: %s, Time: %s, Score: %.4f\n",
newPattern.Source, newPattern.Timestamp.Format(time.RFC3339), avgDistance)
}
// Add the new pattern to the index
idx.Add(uint64(time.Now().UnixNano()), newPattern.Features, map[string]interface{}{
"category": "pattern",
"timestamp": newPattern.Timestamp.Unix(),
"source": newPattern.Source,
"is_anomaly": isAnomaly,
"score": avgDistance,
})
return isAnomaly
}
// Simulate real-time data stream
go func() {
for {
// Get new pattern from data source
newPattern := getNewPattern()
// Detect anomaly
detectAnomaly(newPattern)
// Wait for next data point
time.Sleep(1 * time.Second)
}
}()
// Keep the main thread alive
select {}
}
// Placeholder types and functions
type Pattern struct {
Features []float32
Timestamp time.Time
Source string
}
func loadHistoricalPatterns() []Pattern {
// Implementation omitted for brevity
return []Pattern{}
}
func getNewPattern() Pattern {
// Implementation omitted for brevity
return Pattern{
Features: make([]float32, 64),
Timestamp: time.Now(),
Source: "sensor-1",
}
}
Next Steps¶
These examples demonstrate just a few of the many ways you can use Quiver in your applications. For more detailed examples and best practices, check out:
- Go Library - Learn more about using Quiver as a library
- HTTP API - Use Quiver as a service
- Performance Tuning - Optimize Quiver for your needs