skills/vector-database-migration-tool/SKILL.md
Migrate vector data between Pinecone, Qdrant, Weaviate, pgvector with re-embedding and schema mapping. Activate on: vector DB migration, switch vector database, re-embed collection, migrate embeddings. NOT for: initial ingestion (rag-document-ingestion-pipeline), embedding model training (ai-engineer).
npx skillsauth add curiositech/windags-skills vector-database-migration-toolInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Migrate vector collections between Pinecone, Qdrant, Weaviate, and pgvector with schema mapping, optional re-embedding, and zero-downtime cutover strategies.
Activate on: "migrate vectors", "switch from Pinecone to Qdrant", "re-embed collection", "vector DB migration", "move embeddings to pgvector", "change embedding model", "vector schema migration"
NOT for: First-time document ingestion (rag-document-ingestion-pipeline), embedding model fine-tuning (ai-engineer), or vector search query optimization (ai-engineer)
| Domain | Technologies | Notes | |--------|-------------|-------| | Source/Target DBs | Pinecone, Qdrant, Weaviate, pgvector, Milvus, Chroma | Any-to-any migration support | | Re-embedding | OpenAI, Cohere, BGE, Nomic | When switching embedding models | | Schema Mapping | Custom Python, Pydantic transforms | Field renaming, type coercion, metadata reshaping | | Orchestration | Python asyncio, Apache Airflow, Prefect | Batched streaming with checkpoints | | Validation | Recall@k comparison, cosine similarity checks | Before/after retrieval quality |
Source DB ──→ [Stream Batches] ──→ [Transform Schema] ──→ [Upsert Target]
│ │ │ │
│ scroll/paginate map fields, batch upsert
│ batch_size=2000 rename keys, with retry
│ coerce types
└── Checkpoint: last_offset stored in Redis/file for resumability
# Direct migration: Qdrant → pgvector
import asyncio
from qdrant_client import QdrantClient
async def migrate_direct(source_url: str, pg_conn: str, collection: str):
qdrant = QdrantClient(url=source_url)
offset = load_checkpoint(collection) # Resume support
while True:
records, next_offset = qdrant.scroll(
collection, offset=offset, limit=2000, with_vectors=True
)
if not records:
break
rows = [(r.id, r.vector, json.dumps(r.payload)) for r in records]
await pg_upsert_batch(pg_conn, rows) # INSERT ... ON CONFLICT
save_checkpoint(collection, next_offset)
offset = next_offset
Source DB ──→ [Extract Text + Metadata] ──→ [New Embedder] ──→ [Target DB]
│ │ │ │
│ pull original text batch embed upsert with
│ from payload/metadata new dimensions new vectors
│
└── CRITICAL: original text must be stored in source metadata
If not available, extract from document store separately
Phase 1: Dual-write (new records go to both DBs)
Phase 2: Backfill (migrate historical data to target)
Phase 3: Shadow read (query both, compare results, log diffs)
Phase 4: Cutover (switch reads to target, stop writes to source)
Phase 5: Decommission (archive source after 7-day bake period)
data-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.