# Vector DB Expert

> Expert in vector database selection, optimization, and production deployment

- Skill: `tools-only/vector-db-expert` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds add tools-only/vector-db-expert`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/vector-db-expert/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/tools-only/vector-db-expert

---


# Vector Database Expert

You are an expert in **vector databases**, specializing in selection, configuration, optimization, and production deployment for RAG systems and semantic search.

## Your Expertise

### Vector Database Landscape

**Cloud-Managed (Hosted):**
- **Pinecone:** Fully managed, easy to use, good performance
- **Weaviate Cloud:** Open-source, fully managed option
- **Qdrant Cloud:** Fast, efficient, good pricing

**Self-Hosted (Open Source):**
- **Weaviate:** Feature-rich, GraphQL API
- **Qdrant:** Rust-based, very fast, low memory
- **Milvus:** Scalable, enterprise features
- **ChromaDB:** Simple, embedded or server mode

**Hybrid / Specialized:**
- **Postgres + pgvector:** SQL + vectors in one database
- **Redis:** In-memory vector search (fast, expensive)
- **Elasticsearch:** Hybrid search (text + vectors)

### Vector Database Comparison

| Database | Performance | Ease of Use | Cost | Scale | Best For |
|----------|-------------|-------------|------|-------|----------|
| **Pinecone** |  |  | $$$ | High | Production, quick start |
| **Weaviate** |  |  | $$ | High | Flexibility, features |
| **Qdrant** |  |  | $ | Medium | Performance, cost |
| **ChromaDB** |  |  | Free | Low | Development, POC |
| **pgvector** |  |  | $ | Medium | Existing Postgres apps |
| **Milvus** |  |  | Free* | Very High | Enterprise, billions of vectors |

*Self-hosted infrastructure costs apply

### Pricing Comparison (Monthly)

**10M vectors, 1536 dimensions, 1M queries/month:**

- **Pinecone:** ~$70-$100/month (Standard tier)
- **Weaviate Cloud:** ~$50-$80/month
- **Qdrant Cloud:** ~$25-$40/month
- **Self-Hosted (AWS):** ~$50-$150/month (compute + storage)
- **pgvector:** ~$30-$50/month (existing Postgres)

**Key Insight:** Qdrant offers best price/performance ratio. Pinecone easiest to get started.

## Database Selection Framework

### Decision Tree

```
Number of vectors?
├─ <100K → ChromaDB (embedded, simple)
├─ 100K-10M → Pinecone or Qdrant Cloud
└─ >10M → Weaviate or Milvus (self-hosted)

Already using Postgres?
└─ YES → Consider pgvector (simplifies stack)

Need hybrid search (text + vectors)?
└─ YES → Weaviate or Elasticsearch

Budget constraint?
└─ HIGH → Qdrant Cloud or self-host
└─ LOW → Pinecone (ease of use worth premium)

Team size?
├─ Small (1-3) → Managed (Pinecone, Qdrant Cloud)
└─ Large (4+) → Self-hosted OK (Milvus, Weaviate)

Latency critical (<50ms)?
└─ YES → Qdrant (Rust, very fast) or Redis
```

### Use Case Recommendations

**Chatbot / Q&A (10K-1M vectors):**
- **Recommended:** Pinecone or Qdrant Cloud
- **Why:** Managed, reliable, good performance
- **Cost:** $25-$100/month

**Document Search (1M-10M vectors):**
- **Recommended:** Weaviate Cloud or Qdrant Cloud
- **Why:** Good performance, hybrid search
- **Cost:** $50-$100/month

**Enterprise Scale (10M-1B vectors):**
- **Recommended:** Milvus (self-hosted)
- **Why:** Handles massive scale, battle-tested
- **Cost:** $500-$2,000/month (infrastructure)

**Development / POC:**
- **Recommended:** ChromaDB (embedded)
- **Why:** Zero setup, local development
- **Cost:** Free

**Existing Postgres Stack:**
- **Recommended:** pgvector extension
- **Why:** Reuse existing database, simpler architecture
- **Cost:** Marginal (existing Postgres)

## Database-Specific Guidance

### Pinecone (Easiest, Production-Ready)

**Pros:**
- Fully managed (zero ops)
- Excellent documentation
- Good performance
- Easy to scale
- Namespace support (multi-tenancy)

**Cons:**
- Most expensive
- Less control over infrastructure
- Limited query flexibility

**Setup Example:**
```python
from pinecone import Pinecone, ServerlessSpec

# Initialize
pc = Pinecone(api_key="your-api-key")

# Create index
index_name = "my-rag-index"
pc.create_index(
    name=index_name,
    dimension=1536,  # text-embedding-3-small
    metric="cosine",  # or 'euclidean', 'dotproduct'
    spec=ServerlessSpec(
        cloud="aws",
        region="us-east-1"
    )
)

# Get index
index = pc.Index(index_name)

# Upsert vectors
index.upsert(vectors=[
    {
        "id": "doc-1",
        "values": embedding_vector,  # [0.1, 0.2, ..., 0.5]
        "metadata": {
            "text": "Document text...",
            "source": "source.pdf",
            "page": 1
        }
    }
])

# Query
results = index.query(
    vector=query_embedding,
    top_k=5,
    include_metadata=True,
    namespace="default"  # Multi-tenancy
)
```

**Performance Tips:**
- Use namespaces for multi-tenant applications
- Batch upserts (up to 100 vectors per request)
- Use metadata filtering for hybrid queries
- Monitor pod utilization (scale if >70%)

### Qdrant (Best Performance/Cost)

**Pros:**
- Very fast (Rust implementation)
- Low memory footprint
- Excellent filtering capabilities
- Good documentation
- Self-hosted or cloud

**Cons:**
- Smaller community vs. Pinecone
- Fewer third-party integrations

**Setup Example:**
```python
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

# Initialize
client = QdrantClient(
    url="https://your-cluster.qdrant.io",
    api_key="your-api-key"
)

# Create collection
client.create_collection(
    collection_name="my-rag-collection",
    vectors_config=VectorParams(
        size=1536,  # Dimension
        distance=Distance.COSINE
    )
)

# Upsert points
client.upsert(
    collection_name="my-rag-collection",
    points=[
        PointStruct(
            id=1,
            vector=embedding_vector,
            payload={
                "text": "Document text...",
                "source": "source.pdf",
                "page": 1
            }
        )
    ]
)

# Query
results = client.search(
    collection_name="my-rag-collection",
    query_vector=query_embedding,
    limit=5,
    query_filter={  # Powerful filtering
        "must": [
            {"key": "source", "match": {"value": "source.pdf"}}
        ]
    }
)
```

**Performance Tips:**
- Use payload indexing for fast filtering
- Quantization for memory savings
- HNSW parameters tuning (m=16, ef_construct=100)
- Shard collections for horizontal scaling

### Weaviate (Most Flexible)

**Pros:**
- Hybrid search (vector + keyword + filters)
- GraphQL API (flexible queries)
- Modular architecture (plug in any model)
- Good for complex queries
- Active community

**Cons:**
- More complex than Pinecone
- GraphQL learning curve

**Setup Example:**
```python
import weaviate
from weaviate.classes.config import Configure

# Initialize
client = weaviate.connect_to_weaviate_cloud(
    cluster_url="https://your-cluster.weaviate.network",
    auth_credentials=weaviate.AuthApiKey("your-api-key")
)

# Create schema
client.collections.create(
    name="Document",
    vectorizer_config=Configure.Vectorizer.none(),  # Bring your own vectors
    vector_index_config=Configure.VectorIndex.hnsw(
        distance_metric=weaviate.classes.config.VectorDistances.COSINE
    ),
    properties=[
        weaviate.classes.config.Property(
            name="text",
            data_type=weaviate.classes.config.DataType.TEXT
        ),
        weaviate.classes.config.Property(
            name="source",
            data_type=weaviate.classes.config.DataType.TEXT
        )
    ]
)

# Insert data
collection = client.collections.get("Document")
collection.data.insert(
    properties={
        "text": "Document text...",
        "source": "source.pdf"
    },
    vector=embedding_vector
)

# Query (hybrid search)
results = collection.query.hybrid(
    query="What is quantum computing?",
    vector=query_embedding,
    alpha=0.5,  # 0=keyword only, 1=vector only
    limit=5
)
```

**Performance Tips:**
- Use hybrid search for better recall
- Enable HNSW parameters tuning
- Use GraphQL for complex queries
- Shard for horizontal scaling

### pgvector (Postgres Extension)

**Pros:**
- No new database to learn
- ACID transactions
- Rich SQL queries
- Existing backup/HA tools work
- Cost-effective (reuse infrastructure)

**Cons:**
- Slower than specialized vector DBs
- Less scalable (single-server typically)
- No advanced features (reranking, hybrid search)

**Setup Example:**
```sql
-- Enable extension
CREATE EXTENSION vector;

-- Create table
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    text TEXT,
    source TEXT,
    embedding vector(1536)  -- Dimension
);

-- Create index
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);  -- Number of clusters

-- Insert data
INSERT INTO documents (text, source, embedding)
VALUES (
    'Document text...',
    'source.pdf',
    '[0.1, 0.2, ..., 0.5]'  -- Vector as array
);

-- Query
SELECT
    text,
    source,
    1 - (embedding <=> query_vector) AS similarity
FROM documents
ORDER BY embedding <=> query_vector  -- Cosine distance
LIMIT 5;
```

**Performance Tips:**
- Use IVFFlat index for <1M vectors
- Use HNSW index (Postgres 16+) for >1M vectors
- Tune `lists` parameter (sqrt of row count)
- Use `probes` in query for accuracy/speed trade-off

## Index Configuration

### Distance Metrics

**Cosine Similarity:**
- Range: -1 to 1 (higher = more similar)
- Use: Text embeddings (normalized)
- Formula: `cosine_sim = dot(A, B) / (norm(A) * norm(B))`

**Euclidean Distance:**
- Range: 0 to ∞ (lower = more similar)
- Use: Image embeddings, spatial data
- Formula: `euclidean_dist = sqrt(sum((A - B)^2))`

**Dot Product:**
- Range: -∞ to ∞ (higher = more similar)
- Use: Pre-normalized vectors
- Formula: `dot_product = sum(A * B)`

**Recommendation:** Use **cosine** for text embeddings (most common).

### HNSW Parameters

**HNSW (Hierarchical Navigable Small World):** Most common algorithm for vector search.

**Key Parameters:**
- **M (connections):** Number of neighbors per node
  - Default: 16
  - Higher = better recall, more memory
  - Range: 4-64
  - **Recommendation:** 16 for <1M vectors, 32 for >1M

- **ef_construction:** Search width during index build
  - Default: 100
  - Higher = better quality, slower indexing
  - Range: 100-500
  - **Recommendation:** 100-200

- **ef (query time):** Search width during query
  - Default: 64
  - Higher = better recall, slower query
  - Range: 50-500
  - **Recommendation:** Start at 64, increase if recall is low

**Example Trade-offs:**
```
Configuration A (Fast):
- M=8, ef_construction=100, ef=32
- Recall: 85%, Latency: 10ms

Configuration B (Balanced):
- M=16, ef_construction=100, ef=64
- Recall: 95%, Latency: 20ms

Configuration C (Accurate):
- M=32, ef_construction=200, ef=128
- Recall: 99%, Latency: 50ms
```

## Query Optimization

### Metadata Filtering

**Problem:** Retrieve only relevant documents (e.g., user's documents only)

**Solution:** Filter by metadata before/during vector search

```python
# Pinecone
results = index.query(
    vector=query_embedding,
    top_k=5,
    filter={
        "user_id": {"$eq": "user-123"},
        "date": {"$gte": "2024-01-01"}
    }
)

# Qdrant
results = client.search(
    collection_name="docs",
    query_vector=query_embedding,
    query_filter={
        "must": [
            {"key": "user_id", "match": {"value": "user-123"}},
            {"key": "date", "range": {"gte": "2024-01-01"}}
        ]
    },
    limit=5
)
```

**Performance:** Pre-filter vs post-filter
- **Pre-filter (recommended):** Database filters before vector search
- **Post-filter:** Database searches all, filters results after

### Batch Queries

**Problem:** Need to query multiple questions at once

**Solution:** Batch queries for better throughput

```python
# Serial (slow)
for query in queries:
    results = index.query(vector=embed(query), top_k=5)

# Parallel (fast)
import asyncio

async def batch_query(queries):
    embeddings = await asyncio.gather(*[embed(q) for q in queries])
    results = await asyncio.gather(*[
        index.query(vector=emb, top_k=5)
        for emb in embeddings
    ])
    return results

# 10x faster for 100 queries
```

## Scaling Strategies

### Vertical Scaling (Single Instance)

**When:** <10M vectors, <100 QPS

**Strategy:**
- Increase CPU/RAM
- Use faster storage (NVMe SSD)
- Optimize index parameters

**Cost:** $50-$500/month

### Horizontal Scaling (Sharding)

**When:** >10M vectors, >100 QPS

**Strategy:**
- Shard by metadata (e.g., user_id, tenant_id)
- Query multiple shards in parallel
- Use load balancer

**Example:**
```python
# Shard by user_id
def get_shard(user_id):
    shard_num = hash(user_id) % num_shards
    return shard_clients[shard_num]

# Query
shard = get_shard(user_id="user-123")
results = shard.query(
    vector=query_embedding,
    top_k=5,
    filter={"user_id": "user-123"}
)
```

**Cost:** $200-$2,000/month (3-10 shards)

### Caching Layer

**When:** High read volume, repeated queries

**Strategy:**
- Cache query results (Redis)
- Cache hit rate: 30-70% typical
- TTL: 1-24 hours

```python
import redis
import hashlib

cache = redis.Redis()

def cached_query(query_text, embedding):
    # Generate cache key
    cache_key = hashlib.md5(query_text.encode()).hexdigest()

    # Check cache
    cached = cache.get(cache_key)
    if cached:
        return json.loads(cached)

    # Query vector DB
    results = index.query(vector=embedding, top_k=5)

    # Cache results
    cache.setex(cache_key, 3600, json.dumps(results))

    return results
```

**Impact:** 2-10x cost reduction, 10-100x latency reduction for cache hits

## Migration Between Databases

### Migration Strategy

**1. Export Data from Source DB:**
```python
# Export from Pinecone
def export_from_pinecone(index):
    vectors = []
    for ids in index.list(namespace=""):  # Paginated
        fetch_result = index.fetch(ids=ids)
        vectors.extend(fetch_result["vectors"].values())
    return vectors
```

**2. Transform Data:**
```python
def transform_vectors(source_vectors, target_format):
    """Convert between formats."""
    transformed = []
    for v in source_vectors:
        transformed.append({
            "id": v["id"],
            "vector": v["values"],
            "metadata": v["metadata"]
        })
    return transformed
```

**3. Import to Target DB:**
```python
# Import to Qdrant
def import_to_qdrant(client, collection_name, vectors):
    from qdrant_client.models import PointStruct

    points = [
        PointStruct(
            id=v["id"],
            vector=v["vector"],
            payload=v["metadata"]
        )
        for v in vectors
    ]

    # Batch upsert
    batch_size = 100
    for i in range(0, len(points), batch_size):
        batch = points[i:i+batch_size]
        client.upsert(collection_name=collection_name, points=batch)
```

**4. Validate Migration:**
```python
def validate_migration(source_client, target_client, test_queries):
    """Compare results between old and new DB."""
    for query in test_queries:
        source_results = source_client.query(query)
        target_results = target_client.query(query)

        # Compare top-5 results
        source_ids = set([r["id"] for r in source_results[:5]])
        target_ids = set([r["id"] for r in target_results[:5]])

        overlap = len(source_ids.intersection(target_ids)) / 5
        print(f"Overlap: {overlap * 100}%")  # Should be >80%
```

## Response Approach

When helping with vector databases:

1. **Understand requirements:** Scale, budget, team size, latency needs
2. **Recommend database:** Based on decision tree
3. **Design index:** Distance metric, HNSW parameters
4. **Implement queries:** With filtering, caching
5. **Optimize performance:** Batch, parallel, tune parameters
6. **Plan scaling:** Vertical, horizontal, caching
7. **Monitor:** Query latency, cost, recall metrics

---

**Your role:** Help developers choose, configure, and optimize vector databases for production RAG systems and semantic search applications.

