[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70GGCE9SMCHFX82CM8SKWJ5":3,"skill-same:01M70GGCE9SMCHFX82CM8SKWJ5":66},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":45,"repositoryHtmlUrl":46,"repositoryHomepageUrl":47,"ownerHtmlUrl":48,"ownerBlogUrl":49,"ownerDescription":50,"updatedAt":51,"install":52,"resources":65},"01M70GGCE9SMCHFX82CM8SKWJ5","similarity-search-patterns",null,"Implement efficient similarity search with vector databases. Use when building semantic search, implementing nearest neighbor queries, or optimizing retrieval performance.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fplugins\u002Fllm-application-dev\u002Fskills\u002Fsimilarity-search-patterns\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"search","Similarity Search","similarity","similarity search patterns","patterns","when","performance","implementing","with","retrieval","semantic","vector databases","queries","databases","use","vector","semantic search","implement","2026-11-10 10:12:30",false,"---\nname: similarity-search-patterns\ndescription: Implement efficient similarity search with vector databases. Use when building semantic search, implementing nearest neighbor queries, or optimizing retrieval performance.\n---\n\n# Similarity Search Patterns\n\nPatterns for implementing efficient similarity search in production systems.\n\n## When to Use This Skill\n\n- Building semantic search systems\n- Implementing RAG retrieval\n- Creating recommendation engines\n- Optimizing search latency\n- Scaling to millions of vectors\n- Combining semantic and keyword search\n\n## Core Concepts\n\n### 1. Distance Metrics\n\n| Metric | Formula | Best For |\n|--------|---------|----------|\n| **Cosine** | 1 - (A·B)\u002F(‖A‖‖B‖) | Normalized embeddings |\n| **Euclidean (L2)** | √Σ(a-b)² | Raw embeddings |\n| **Dot Product** | A·B | Magnitude matters |\n| **Manhattan (L1)** | Σ|a-b| | Sparse vectors |\n\n### 2. Index Types\n\n```\n┌─────────────────────────────────────────────────┐\n│                 Index Types                      │\n├─────────────┬───────────────┬───────────────────┤\n│    Flat     │     HNSW      │    IVF+PQ         │\n│ (Exact)     │ (Graph-based) │ (Quantized)       │\n├─────────────┼───────────────┼───────────────────┤\n│ O(n) search │ O(log n)      │ O(√n)             │\n│ 100% recall │ ~95-99%       │ ~90-95%           │\n│ Small data  │ Medium-Large  │ Very Large        │\n└─────────────┴───────────────┴───────────────────┘\n```\n\n## Templates\n\n### Template 1: Pinecone Implementation\n\n```python\nfrom pinecone import Pinecone, ServerlessSpec\nfrom typing import List, Dict, Optional\nimport hashlib\n\nclass PineconeVectorStore:\n    def __init__(\n        self,\n        api_key: str,\n        index_name: str,\n        dimension: int = 1536,\n        metric: str = \"cosine\"\n    ):\n        self.pc = Pinecone(api_key=api_key)\n\n        # Create index if not exists\n        if index_name not in self.pc.list_indexes().names():\n            self.pc.create_index(\n                name=index_name,\n                dimension=dimension,\n                metric=metric,\n                spec=ServerlessSpec(cloud=\"aws\", region=\"us-east-1\")\n            )\n\n        self.index = self.pc.Index(index_name)\n\n    def upsert(\n        self,\n        vectors: List[Dict],\n        namespace: str = \"\"\n    ) -> int:\n        \"\"\"\n        Upsert vectors.\n        vectors: [{\"id\": str, \"values\": List[float], \"metadata\": dict}]\n        \"\"\"\n        # Batch upsert\n        batch_size = 100\n        total = 0\n\n        for i in range(0, len(vectors), batch_size):\n            batch = vectors[i:i + batch_size]\n            self.index.upsert(vectors=batch, namespace=namespace)\n            total += len(batch)\n\n        return total\n\n    def search(\n        self,\n        query_vector: List[float],\n        top_k: int = 10,\n        namespace: str = \"\",\n        filter: Optional[Dict] = None,\n        include_metadata: bool = True\n    ) -> List[Dict]:\n        \"\"\"Search for similar vectors.\"\"\"\n        results = self.index.query(\n            vector=query_vector,\n            top_k=top_k,\n            namespace=namespace,\n            filter=filter,\n            include_metadata=include_metadata\n        )\n\n        return [\n            {\n                \"id\": match.id,\n                \"score\": match.score,\n                \"metadata\": match.metadata\n            }\n            for match in results.matches\n        ]\n\n    def search_with_rerank(\n        self,\n        query: str,\n        query_vector: List[float],\n        top_k: int = 10,\n        rerank_top_n: int = 50,\n        namespace: str = \"\"\n    ) -> List[Dict]:\n        \"\"\"Search and rerank results.\"\"\"\n        # Over-fetch for reranking\n        initial_results = self.search(\n            query_vector,\n            top_k=rerank_top_n,\n            namespace=namespace\n        )\n\n        # Rerank with cross-encoder or LLM\n        reranked = self._rerank(query, initial_results)\n\n        return reranked[:top_k]\n\n    def _rerank(self, query: str, results: List[Dict]) -> List[Dict]:\n        \"\"\"Rerank results using cross-encoder.\"\"\"\n        from sentence_transformers import CrossEncoder\n\n        model = CrossEncoder('cross-encoder\u002Fms-marco-MiniLM-L-6-v2')\n\n        pairs = [(query, r[\"metadata\"][\"text\"]) for r in results]\n        scores = model.predict(pairs)\n\n        for result, score in zip(results, scores):\n            result[\"rerank_score\"] = float(score)\n\n        return sorted(results, key=lambda x: x[\"rerank_score\"], reverse=True)\n\n    def delete(self, ids: List[str], namespace: str = \"\"):\n        \"\"\"Delete vectors by ID.\"\"\"\n        self.index.delete(ids=ids, namespace=namespace)\n\n    def delete_by_filter(self, filter: Dict, namespace: str = \"\"):\n        \"\"\"Delete vectors matching filter.\"\"\"\n        self.index.delete(filter=filter, namespace=namespace)\n```\n\n### Template 2: Qdrant Implementation\n\n```python\nfrom qdrant_client import QdrantClient\nfrom qdrant_client.http import models\nfrom typing import List, Dict, Optional\n\nclass QdrantVectorStore:\n    def __init__(\n        self,\n        url: str = \"localhost\",\n        port: int = 6333,\n        collection_name: str = \"documents\",\n        vector_size: int = 1536\n    ):\n        self.client = QdrantClient(url=url, port=port)\n        self.collection_name = collection_name\n\n        # Create collection if not exists\n        collections = self.client.get_collections().collections\n        if collection_name not in [c.name for c in collections]:\n            self.client.create_collection(\n                collection_name=collection_name,\n                vectors_config=models.VectorParams(\n                    size=vector_size,\n                    distance=models.Distance.COSINE\n                ),\n                # Optional: enable quantization for memory efficiency\n                quantization_config=models.ScalarQuantization(\n                    scalar=models.ScalarQuantizationConfig(\n                        type=models.ScalarType.INT8,\n                        quantile=0.99,\n                        always_ram=True\n                    )\n                )\n            )\n\n    def upsert(self, points: List[Dict]) -> int:\n        \"\"\"\n        Upsert points.\n        points: [{\"id\": str\u002Fint, \"vector\": List[float], \"payload\": dict}]\n        \"\"\"\n        qdrant_points = [\n            models.PointStruct(\n                id=p[\"id\"],\n                vector=p[\"vector\"],\n                payload=p.get(\"payload\", {})\n            )\n            for p in points\n        ]\n\n        self.client.upsert(\n            collection_name=self.collection_name,\n            points=qdrant_points\n        )\n        return len(points)\n\n    def search(\n        self,\n        query_vector: List[float],\n        limit: int = 10,\n        filter: Optional[models.Filter] = None,\n        score_threshold: Optional[float] = None\n    ) -> List[Dict]:\n        \"\"\"Search for similar vectors.\"\"\"\n        results = self.client.search(\n            collection_name=self.collection_name,\n            query_vector=query_vector,\n            limit=limit,\n            query_filter=filter,\n            score_threshold=score_threshold\n        )\n\n        return [\n            {\n                \"id\": r.id,\n                \"score\": r.score,\n                \"payload\": r.payload\n            }\n            for r in results\n        ]\n\n    def search_with_filter(\n        self,\n        query_vector: List[float],\n        must_conditions: List[Dict] = None,\n        should_conditions: List[Dict] = None,\n        must_not_conditions: List[Dict] = None,\n        limit: int = 10\n    ) -> List[Dict]:\n        \"\"\"Search with complex filters.\"\"\"\n        conditions = []\n\n        if must_conditions:\n            conditions.extend([\n                models.FieldCondition(\n                    key=c[\"key\"],\n                    match=models.MatchValue(value=c[\"value\"])\n                )\n                for c in must_conditions\n            ])\n\n        filter = models.Filter(must=conditions) if conditions else None\n\n        return self.search(query_vector, limit=limit, filter=filter)\n\n    def search_with_sparse(\n        self,\n        dense_vector: List[float],\n        sparse_vector: Dict[int, float],\n        limit: int = 10,\n        dense_weight: float = 0.7\n    ) -> List[Dict]:\n        \"\"\"Hybrid search with dense and sparse vectors.\"\"\"\n        # Requires collection with named vectors\n        results = self.client.search(\n            collection_name=self.collection_name,\n            query_vector=models.NamedVector(\n                name=\"dense\",\n                vector=dense_vector\n            ),\n            limit=limit\n        )\n        return [{\"id\": r.id, \"score\": r.score, \"payload\": r.payload} for r in results]\n```\n\n### Template 3: pgvector with PostgreSQL\n\n```python\nimport asyncpg\nfrom typing import List, Dict, Optional\nimport numpy as np\n\nclass PgVectorStore:\n    def __init__(self, connection_string: str):\n        self.connection_string = connection_string\n\n    async def init(self):\n        \"\"\"Initialize connection pool and extension.\"\"\"\n        self.pool = await asyncpg.create_pool(self.connection_string)\n\n        async with self.pool.acquire() as conn:\n            # Enable extension\n            await conn.execute(\"CREATE EXTENSION IF NOT EXISTS vector\")\n\n            # Create table\n            await conn.execute(\"\"\"\n                CREATE TABLE IF NOT EXISTS documents (\n                    id TEXT PRIMARY KEY,\n                    content TEXT,\n                    metadata JSONB,\n                    embedding vector(1536)\n                )\n            \"\"\")\n\n            # Create index (HNSW for better performance)\n            await conn.execute(\"\"\"\n                CREATE INDEX IF NOT EXISTS documents_embedding_idx\n                ON documents\n                USING hnsw (embedding vector_cosine_ops)\n                WITH (m = 16, ef_construction = 64)\n            \"\"\")\n\n    async def upsert(self, documents: List[Dict]):\n        \"\"\"Upsert documents with embeddings.\"\"\"\n        async with self.pool.acquire() as conn:\n            await conn.executemany(\n                \"\"\"\n                INSERT INTO documents (id, content, metadata, embedding)\n                VALUES ($1, $2, $3, $4)\n                ON CONFLICT (id) DO UPDATE SET\n                    content = EXCLUDED.content,\n                    metadata = EXCLUDED.metadata,\n                    embedding = EXCLUDED.embedding\n                \"\"\",\n                [\n                    (\n                        doc[\"id\"],\n                        doc[\"content\"],\n                        doc.get(\"metadata\", {}),\n                        np.array(doc[\"embedding\"]).tolist()\n                    )\n                    for doc in documents\n                ]\n            )\n\n    async def search(\n        self,\n        query_embedding: List[float],\n        limit: int = 10,\n        filter_metadata: Optional[Dict] = None\n    ) -> List[Dict]:\n        \"\"\"Search for similar documents.\"\"\"\n        query = \"\"\"\n            SELECT id, content, metadata,\n                   1 - (embedding \u003C=> $1::vector) as similarity\n            FROM documents\n        \"\"\"\n\n        params = [query_embedding]\n\n        if filter_metadata:\n            conditions = []\n            for key, value in filter_metadata.items():\n                params.append(value)\n                conditions.append(f\"metadata->>'{key}' = ${len(params)}\")\n            query += \" WHERE \" + \" AND \".join(conditions)\n\n        query += f\" ORDER BY embedding \u003C=> $1::vector LIMIT ${len(params) + 1}\"\n        params.append(limit)\n\n        async with self.pool.acquire() as conn:\n            rows = await conn.fetch(query, *params)\n\n        return [\n            {\n                \"id\": row[\"id\"],\n                \"content\": row[\"content\"],\n                \"metadata\": row[\"metadata\"],\n                \"score\": row[\"similarity\"]\n            }\n            for row in rows\n        ]\n\n    async def hybrid_search(\n        self,\n        query_embedding: List[float],\n        query_text: str,\n        limit: int = 10,\n        vector_weight: float = 0.5\n    ) -> List[Dict]:\n        \"\"\"Hybrid search combining vector and full-text.\"\"\"\n        async with self.pool.acquire() as conn:\n            rows = await conn.fetch(\n                \"\"\"\n                WITH vector_results AS (\n                    SELECT id, content, metadata,\n                           1 - (embedding \u003C=> $1::vector) as vector_score\n                    FROM documents\n                    ORDER BY embedding \u003C=> $1::vector\n                    LIMIT $3 * 2\n                ),\n                text_results AS (\n                    SELECT id, content, metadata,\n                           ts_rank(to_tsvector('english', content),\n                                   plainto_tsquery('english', $2)) as text_score\n                    FROM documents\n                    WHERE to_tsvector('english', content) @@ plainto_tsquery('english', $2)\n                    LIMIT $3 * 2\n                )\n                SELECT\n                    COALESCE(v.id, t.id) as id,\n                    COALESCE(v.content, t.content) as content,\n                    COALESCE(v.metadata, t.metadata) as metadata,\n                    COALESCE(v.vector_score, 0) * $4 +\n                    COALESCE(t.text_score, 0) * (1 - $4) as combined_score\n                FROM vector_results v\n                FULL OUTER JOIN text_results t ON v.id = t.id\n                ORDER BY combined_score DESC\n                LIMIT $3\n                \"\"\",\n                query_embedding, query_text, limit, vector_weight\n            )\n\n        return [dict(row) for row in rows]\n```\n\n### Template 4: Weaviate Implementation\n\n```python\nimport weaviate\nfrom weaviate.util import generate_uuid5\nfrom typing import List, Dict, Optional\n\nclass WeaviateVectorStore:\n    def __init__(\n        self,\n        url: str = \"http:\u002F\u002Flocalhost:8080\",\n        class_name: str = \"Document\"\n    ):\n        self.client = weaviate.Client(url=url)\n        self.class_name = class_name\n        self._ensure_schema()\n\n    def _ensure_schema(self):\n        \"\"\"Create schema if not exists.\"\"\"\n        schema = {\n            \"class\": self.class_name,\n            \"vectorizer\": \"none\",  # We provide vectors\n            \"properties\": [\n                {\"name\": \"content\", \"dataType\": [\"text\"]},\n                {\"name\": \"source\", \"dataType\": [\"string\"]},\n                {\"name\": \"chunk_id\", \"dataType\": [\"int\"]}\n            ]\n        }\n\n        if not self.client.schema.exists(self.class_name):\n            self.client.schema.create_class(schema)\n\n    def upsert(self, documents: List[Dict]):\n        \"\"\"Batch upsert documents.\"\"\"\n        with self.client.batch as batch:\n            batch.batch_size = 100\n\n            for doc in documents:\n                batch.add_data_object(\n                    data_object={\n                        \"content\": doc[\"content\"],\n                        \"source\": doc.get(\"source\", \"\"),\n                        \"chunk_id\": doc.get(\"chunk_id\", 0)\n                    },\n                    class_name=self.class_name,\n                    uuid=generate_uuid5(doc[\"id\"]),\n                    vector=doc[\"embedding\"]\n                )\n\n    def search(\n        self,\n        query_vector: List[float],\n        limit: int = 10,\n        where_filter: Optional[Dict] = None\n    ) -> List[Dict]:\n        \"\"\"Vector search.\"\"\"\n        query = (\n            self.client.query\n            .get(self.class_name, [\"content\", \"source\", \"chunk_id\"])\n            .with_near_vector({\"vector\": query_vector})\n            .with_limit(limit)\n            .with_additional([\"distance\", \"id\"])\n        )\n\n        if where_filter:\n            query = query.with_where(where_filter)\n\n        results = query.do()\n\n        return [\n            {\n                \"id\": item[\"_additional\"][\"id\"],\n                \"content\": item[\"content\"],\n                \"source\": item[\"source\"],\n                \"score\": 1 - item[\"_additional\"][\"distance\"]\n            }\n            for item in results[\"data\"][\"Get\"][self.class_name]\n        ]\n\n    def hybrid_search(\n        self,\n        query: str,\n        query_vector: List[float],\n        limit: int = 10,\n        alpha: float = 0.5  # 0 = keyword, 1 = vector\n    ) -> List[Dict]:\n        \"\"\"Hybrid search combining BM25 and vector.\"\"\"\n        results = (\n            self.client.query\n            .get(self.class_name, [\"content\", \"source\"])\n            .with_hybrid(query=query, vector=query_vector, alpha=alpha)\n            .with_limit(limit)\n            .with_additional([\"score\"])\n            .do()\n        )\n\n        return [\n            {\n                \"content\": item[\"content\"],\n                \"source\": item[\"source\"],\n                \"score\": item[\"_additional\"][\"score\"]\n            }\n            for item in results[\"data\"][\"Get\"][self.class_name]\n        ]\n```\n\n## Best Practices\n\n### Do's\n- **Use appropriate index** - HNSW for most cases\n- **Tune parameters** - ef_search, nprobe for recall\u002Fspeed\n- **Implement hybrid search** - Combine with keyword search\n- **Monitor recall** - Measure search quality\n- **Pre-filter when possible** - Reduce search space\n\n### Don'ts\n- **Don't skip evaluation** - Measure before optimizing\n- **Don't over-index** - Start with flat, scale up\n- **Don't ignore latency** - P99 matters for UX\n- **Don't forget costs** - Vector storage adds up\n\n## Resources\n\n- [Pinecone Docs](https:\u002F\u002Fdocs.pinecone.io\u002F)\n- [Qdrant Docs](https:\u002F\u002Fqdrant.tech\u002Fdocumentation\u002F)\n- [pgvector](https:\u002F\u002Fgithub.com\u002Fpgvector\u002Fpgvector)\n- [Weaviate Docs](https:\u002F\u002Fweaviate.io\u002Fdevelopers\u002Fweaviate)\n","# Similarity Search Patterns\n\nPatterns for implementing efficient similarity search in production systems.\n\n## When to Use This Skill\n\n- Building semantic search systems\n- Implementing RAG retrieval\n- Creating recommendation engines\n- Optimizing search latency\n- Scaling to millions of vectors\n- Combining semantic and keyword search\n\n## Core Concepts\n\n### 1. Distance Metrics\n\n| Metric | Formula | Best For |\n|--------|---------|----------|\n| **Cosine** | 1 - (A·B)\u002F(‖A‖‖B‖) | Normalized embeddings |\n| **Euclidean (L2)** | √Σ(a-b)² | Raw embeddings |\n| **Dot Product** | A·B | Magnitude matters |\n| **Manhattan (L1)** | Σ|a-b| | Sparse vectors |\n\n### 2. Index Types\n\n```\n┌─────────────────────────────────────────────────┐\n│                 Index Types                      │\n├─────────────┬───────────────┬───────────────────┤\n│    Flat     │     HNSW      │    IVF+PQ         │\n│ (Exact)     │ (Graph-based) │ (Quantized)       │\n├─────────────┼───────────────┼───────────────────┤\n│ O(n) search │ O(log n)      │ O(√n)             │\n│ 100% recall │ ~95-99%       │ ~90-95%           │\n│ Small data  │ Medium-Large  │ Very Large        │\n└─────────────┴───────────────┴───────────────────┘\n```\n\n## Templates\n\n### Template 1: Pinecone Implementation\n\n```python\nfrom pinecone import Pinecone, ServerlessSpec\nfrom typing import List, Dict, Optional\nimport hashlib\n\nclass PineconeVectorStore:\n    def __init__(\n        self,\n        api_key: str,\n        index_name: str,\n        dimension: int = 1536,\n        metric: str = \"cosine\"\n    ):\n        self.pc = Pinecone(api_key=api_key)\n\n        # Create index if not exists\n        if index_name not in self.pc.list_indexes().names():\n            self.pc.create_index(\n                name=index_name,\n                dimension=dimension,\n                metric=metric,\n                spec=ServerlessSpec(cloud=\"aws\", region=\"us-east-1\")\n            )\n\n        self.index = self.pc.Index(index_name)\n\n    def upsert(\n        self,\n        vectors: List[Dict],\n        namespace: str = \"\"\n    ) -> int:\n        \"\"\"\n        Upsert vectors.\n        vectors: [{\"id\": str, \"values\": List[float], \"metadata\": dict}]\n        \"\"\"\n        # Batch upsert\n        batch_size = 100\n        total = 0\n\n        for i in range(0, len(vectors), batch_size):\n            batch = vectors[i:i + batch_size]\n            self.index.upsert(vectors=batch, namespace=namespace)\n            total += len(batch)\n\n        return total\n\n    def search(\n        self,\n        query_vector: List[float],\n        top_k: int = 10,\n        namespace: str = \"\",\n        filter: Optional[Dict] = None,\n        include_metadata: bool = True\n    ) -> List[Dict]:\n        \"\"\"Search for similar vectors.\"\"\"\n        results = self.index.query(\n            vector=query_vector,\n            top_k=top_k,\n            namespace=namespace,\n            filter=filter,\n            include_metadata=include_metadata\n        )\n\n        return [\n            {\n                \"id\": match.id,\n                \"score\": match.score,\n                \"metadata\": match.metadata\n            }\n            for match in results.matches\n        ]\n\n    def search_with_rerank(\n        self,\n        query: str,\n        query_vector: List[float],\n        top_k: int = 10,\n        rerank_top_n: int = 50,\n        namespace: str = \"\"\n    ) -> List[Dict]:\n        \"\"\"Search and rerank results.\"\"\"\n        # Over-fetch for reranking\n        initial_results = self.search(\n            query_vector,\n            top_k=rerank_top_n,\n            namespace=namespace\n        )\n\n        # Rerank with cross-encoder or LLM\n        reranked = self._rerank(query, initial_results)\n\n        return reranked[:top_k]\n\n    def _rerank(self, query: str, results: List[Dict]) -> List[Dict]:\n        \"\"\"Rerank results using cross-encoder.\"\"\"\n        from sentence_transformers import CrossEncoder\n\n        model = CrossEncoder('cross-encoder\u002Fms-marco-MiniLM-L-6-v2')\n\n        pairs = [(query, r[\"metadata\"][\"text\"]) for r in results]\n        scores = model.predict(pairs)\n\n        for result, score in zip(results, scores):\n            result[\"rerank_score\"] = float(score)\n\n        return sorted(results, key=lambda x: x[\"rerank_score\"], reverse=True)\n\n    def delete(self, ids: List[str], namespace: str = \"\"):\n        \"\"\"Delete vectors by ID.\"\"\"\n        self.index.delete(ids=ids, namespace=namespace)\n\n    def delete_by_filter(self, filter: Dict, namespace: str = \"\"):\n        \"\"\"Delete vectors matching filter.\"\"\"\n        self.index.delete(filter=filter, namespace=namespace)\n```\n\n### Template 2: Qdrant Implementation\n\n```python\nfrom qdrant_client import QdrantClient\nfrom qdrant_client.http import models\nfrom typing import List, Dict, Optional\n\nclass QdrantVectorStore:\n    def __init__(\n        self,\n        url: str = \"localhost\",\n        port: int = 6333,\n        collection_name: str = \"documents\",\n        vector_size: int = 1536\n    ):\n        self.client = QdrantClient(url=url, port=port)\n        self.collection_name = collection_name\n\n        # Create collection if not exists\n        collections = self.client.get_collections().collections\n        if collection_name not in [c.name for c in collections]:\n            self.client.create_collection(\n                collection_name=collection_name,\n                vectors_config=models.VectorParams(\n                    size=vector_size,\n                    distance=models.Distance.COSINE\n                ),\n                # Optional: enable quantization for memory efficiency\n                quantization_config=models.ScalarQuantization(\n                    scalar=models.ScalarQuantizationConfig(\n                        type=models.ScalarType.INT8,\n                        quantile=0.99,\n                        always_ram=True\n                    )\n                )\n            )\n\n    def upsert(self, points: List[Dict]) -> int:\n        \"\"\"\n        Upsert points.\n        points: [{\"id\": str\u002Fint, \"vector\": List[float], \"payload\": dict}]\n        \"\"\"\n        qdrant_points = [\n            models.PointStruct(\n                id=p[\"id\"],\n                vector=p[\"vector\"],\n                payload=p.get(\"payload\", {})\n            )\n            for p in points\n        ]\n\n        self.client.upsert(\n            collection_name=self.collection_name,\n            points=qdrant_points\n        )\n        return len(points)\n\n    def search(\n        self,\n        query_vector: List[float],\n        limit: int = 10,\n        filter: Optional[models.Filter] = None,\n        score_threshold: Optional[float] = None\n    ) -> List[Dict]:\n        \"\"\"Search for similar vectors.\"\"\"\n        results = self.client.search(\n            collection_name=self.collection_name,\n            query_vector=query_vector,\n            limit=limit,\n            query_filter=filter,\n            score_threshold=score_threshold\n        )\n\n        return [\n            {\n                \"id\": r.id,\n                \"score\": r.score,\n                \"payload\": r.payload\n            }\n            for r in results\n        ]\n\n    def search_with_filter(\n        self,\n        query_vector: List[float],\n        must_conditions: List[Dict] = None,\n        should_conditions: List[Dict] = None,\n        must_not_conditions: List[Dict] = None,\n        limit: int = 10\n    ) -> List[Dict]:\n        \"\"\"Search with complex filters.\"\"\"\n        conditions = []\n\n        if must_conditions:\n            conditions.extend([\n                models.FieldCondition(\n                    key=c[\"key\"],\n                    match=models.MatchValue(value=c[\"value\"])\n                )\n                for c in must_conditions\n            ])\n\n        filter = models.Filter(must=conditions) if conditions else None\n\n        return self.search(query_vector, limit=limit, filter=filter)\n\n    def search_with_sparse(\n        self,\n        dense_vector: List[float],\n        sparse_vector: Dict[int, float],\n        limit: int = 10,\n        dense_weight: float = 0.7\n    ) -> List[Dict]:\n        \"\"\"Hybrid search with dense and sparse vectors.\"\"\"\n        # Requires collection with named vectors\n        results = self.client.search(\n            collection_name=self.collection_name,\n            query_vector=models.NamedVector(\n                name=\"dense\",\n                vector=dense_vector\n            ),\n            limit=limit\n        )\n        return [{\"id\": r.id, \"score\": r.score, \"payload\": r.payload} for r in results]\n```\n\n### Template 3: pgvector with PostgreSQL\n\n```python\nimport asyncpg\nfrom typing import List, Dict, Optional\nimport numpy as np\n\nclass PgVectorStore:\n    def __init__(self, connection_string: str):\n        self.connection_string = connection_string\n\n    async def init(self):\n        \"\"\"Initialize connection pool and extension.\"\"\"\n        self.pool = await asyncpg.create_pool(self.connection_string)\n\n        async with self.pool.acquire() as conn:\n            # Enable extension\n            await conn.execute(\"CREATE EXTENSION IF NOT EXISTS vector\")\n\n            # Create table\n            await conn.execute(\"\"\"\n                CREATE TABLE IF NOT EXISTS documents (\n                    id TEXT PRIMARY KEY,\n                    content TEXT,\n                    metadata JSONB,\n                    embedding vector(1536)\n                )\n            \"\"\")\n\n            # Create index (HNSW for better performance)\n            await conn.execute(\"\"\"\n                CREATE INDEX IF NOT EXISTS documents_embedding_idx\n                ON documents\n                USING hnsw (embedding vector_cosine_ops)\n                WITH (m = 16, ef_construction = 64)\n            \"\"\")\n\n    async def upsert(self, documents: List[Dict]):\n        \"\"\"Upsert documents with embeddings.\"\"\"\n        async with self.pool.acquire() as conn:\n            await conn.executemany(\n                \"\"\"\n                INSERT INTO documents (id, content, metadata, embedding)\n                VALUES ($1, $2, $3, $4)\n                ON CONFLICT (id) DO UPDATE SET\n                    content = EXCLUDED.content,\n                    metadata = EXCLUDED.metadata,\n                    embedding = EXCLUDED.embedding\n                \"\"\",\n                [\n                    (\n                        doc[\"id\"],\n                        doc[\"content\"],\n                        doc.get(\"metadata\", {}),\n                        np.array(doc[\"embedding\"]).tolist()\n                    )\n                    for doc in documents\n                ]\n            )\n\n    async def search(\n        self,\n        query_embedding: List[float],\n        limit: int = 10,\n        filter_metadata: Optional[Dict] = None\n    ) -> List[Dict]:\n        \"\"\"Search for similar documents.\"\"\"\n        query = \"\"\"\n            SELECT id, content, metadata,\n                   1 - (embedding \u003C=> $1::vector) as similarity\n            FROM documents\n        \"\"\"\n\n        params = [query_embedding]\n\n        if filter_metadata:\n            conditions = []\n            for key, value in filter_metadata.items():\n                params.append(value)\n                conditions.append(f\"metadata->>'{key}' = ${len(params)}\")\n            query += \" WHERE \" + \" AND \".join(conditions)\n\n        query += f\" ORDER BY embedding \u003C=> $1::vector LIMIT ${len(params) + 1}\"\n        params.append(limit)\n\n        async with self.pool.acquire() as conn:\n            rows = await conn.fetch(query, *params)\n\n        return [\n            {\n                \"id\": row[\"id\"],\n                \"content\": row[\"content\"],\n                \"metadata\": row[\"metadata\"],\n                \"score\": row[\"similarity\"]\n            }\n            for row in rows\n        ]\n\n    async def hybrid_search(\n        self,\n        query_embedding: List[float],\n        query_text: str,\n        limit: int = 10,\n        vector_weight: float = 0.5\n    ) -> List[Dict]:\n        \"\"\"Hybrid search combining vector and full-text.\"\"\"\n        async with self.pool.acquire() as conn:\n            rows = await conn.fetch(\n                \"\"\"\n                WITH vector_results AS (\n                    SELECT id, content, metadata,\n                           1 - (embedding \u003C=> $1::vector) as vector_score\n                    FROM documents\n                    ORDER BY embedding \u003C=> $1::vector\n                    LIMIT $3 * 2\n                ),\n                text_results AS (\n                    SELECT id, content, metadata,\n                           ts_rank(to_tsvector('english', content),\n                                   plainto_tsquery('english', $2)) as text_score\n                    FROM documents\n                    WHERE to_tsvector('english', content) @@ plainto_tsquery('english', $2)\n                    LIMIT $3 * 2\n                )\n                SELECT\n                    COALESCE(v.id, t.id) as id,\n                    COALESCE(v.content, t.content) as content,\n                    COALESCE(v.metadata, t.metadata) as metadata,\n                    COALESCE(v.vector_score, 0) * $4 +\n                    COALESCE(t.text_score, 0) * (1 - $4) as combined_score\n                FROM vector_results v\n                FULL OUTER JOIN text_results t ON v.id = t.id\n                ORDER BY combined_score DESC\n                LIMIT $3\n                \"\"\",\n                query_embedding, query_text, limit, vector_weight\n            )\n\n        return [dict(row) for row in rows]\n```\n\n### Template 4: Weaviate Implementation\n\n```python\nimport weaviate\nfrom weaviate.util import generate_uuid5\nfrom typing import List, Dict, Optional\n\nclass WeaviateVectorStore:\n    def __init__(\n        self,\n        url: str = \"http:\u002F\u002Flocalhost:8080\",\n        class_name: str = \"Document\"\n    ):\n        self.client = weaviate.Client(url=url)\n        self.class_name = class_name\n        self._ensure_schema()\n\n    def _ensure_schema(self):\n        \"\"\"Create schema if not exists.\"\"\"\n        schema = {\n            \"class\": self.class_name,\n            \"vectorizer\": \"none\",  # We provide vectors\n            \"properties\": [\n                {\"name\": \"content\", \"dataType\": [\"text\"]},\n                {\"name\": \"source\", \"dataType\": [\"string\"]},\n                {\"name\": \"chunk_id\", \"dataType\": [\"int\"]}\n            ]\n        }\n\n        if not self.client.schema.exists(self.class_name):\n            self.client.schema.create_class(schema)\n\n    def upsert(self, documents: List[Dict]):\n        \"\"\"Batch upsert documents.\"\"\"\n        with self.client.batch as batch:\n            batch.batch_size = 100\n\n            for doc in documents:\n                batch.add_data_object(\n                    data_object={\n                        \"content\": doc[\"content\"],\n                        \"source\": doc.get(\"source\", \"\"),\n                        \"chunk_id\": doc.get(\"chunk_id\", 0)\n                    },\n                    class_name=self.class_name,\n                    uuid=generate_uuid5(doc[\"id\"]),\n                    vector=doc[\"embedding\"]\n                )\n\n    def search(\n        self,\n        query_vector: List[float],\n        limit: int = 10,\n        where_filter: Optional[Dict] = None\n    ) -> List[Dict]:\n        \"\"\"Vector search.\"\"\"\n        query = (\n            self.client.query\n            .get(self.class_name, [\"content\", \"source\", \"chunk_id\"])\n            .with_near_vector({\"vector\": query_vector})\n            .with_limit(limit)\n            .with_additional([\"distance\", \"id\"])\n        )\n\n        if where_filter:\n            query = query.with_where(where_filter)\n\n        results = query.do()\n\n        return [\n            {\n                \"id\": item[\"_additional\"][\"id\"],\n                \"content\": item[\"content\"],\n                \"source\": item[\"source\"],\n                \"score\": 1 - item[\"_additional\"][\"distance\"]\n            }\n            for item in results[\"data\"][\"Get\"][self.class_name]\n        ]\n\n    def hybrid_search(\n        self,\n        query: str,\n        query_vector: List[float],\n        limit: int = 10,\n        alpha: float = 0.5  # 0 = keyword, 1 = vector\n    ) -> List[Dict]:\n        \"\"\"Hybrid search combining BM25 and vector.\"\"\"\n        results = (\n            self.client.query\n            .get(self.class_name, [\"content\", \"source\"])\n            .with_hybrid(query=query, vector=query_vector, alpha=alpha)\n            .with_limit(limit)\n            .with_additional([\"score\"])\n            .do()\n        )\n\n        return [\n            {\n                \"content\": item[\"content\"],\n                \"source\": item[\"source\"],\n                \"score\": item[\"_additional\"][\"score\"]\n            }\n            for item in results[\"data\"][\"Get\"][self.class_name]\n        ]\n```\n\n## Best Practices\n\n### Do's\n- **Use appropriate index** - HNSW for most cases\n- **Tune parameters** - ef_search, nprobe for recall\u002Fspeed\n- **Implement hybrid search** - Combine with keyword search\n- **Monitor recall** - Measure search quality\n- **Pre-filter when possible** - Reduce search space\n\n### Don'ts\n- **Don't skip evaluation** - Measure before optimizing\n- **Don't over-index** - Start with flat, scale up\n- **Don't ignore latency** - P99 matters for UX\n- **Don't forget costs** - Vector storage adds up\n\n## Resources\n\n- [Pinecone Docs](https:\u002F\u002Fdocs.pinecone.io\u002F)\n- [Qdrant Docs](https:\u002F\u002Fqdrant.tech\u002Fdocumentation\u002F)\n- [pgvector](https:\u002F\u002Fgithub.com\u002Fpgvector\u002Fpgvector)\n- [Weaviate Docs](https:\u002F\u002Fweaviate.io\u002Fdevelopers\u002Fweaviate)\n",{"name":5,"description":7},"main","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002F_collections\u002Fplugins\u002Fllm-application-dev\u002Fskills\u002Fsimilarity-search-patterns",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":53,"prompt":63,"zipAvailable":64},[54,57,60],{"tool":55,"command":56},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill similarity-search-patterns",{"tool":58,"command":59},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill similarity-search-patterns",{"tool":61,"command":62},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill similarity-search-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fplugins\u002Fllm-application-dev\u002Fskills\u002Fsimilarity-search-patterns\u002FSKILL.md",true,[],[67,86,120,145,167,187],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":78,"publishedAt":37},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[73],{"categoryNo":74,"categoryName":75,"taxonomyType":76,"taxonomyTypeDesc":77},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",[79,80,81,82,83,84,85],"methodology","xiaolai","xiaolai methodology","朋友","投资","writing","收入",{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":100,"publishedAt":37},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[92,95],{"categoryNo":93,"categoryName":94,"taxonomyType":76,"taxonomyTypeDesc":77},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":96,"categoryName":97,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[88,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","generate","intent","cli","map","documentation","project wiki","disk","architecture","code","unknown","generated",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":130,"publishedAt":37},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[126,127],{"categoryNo":93,"categoryName":94,"taxonomyType":76,"taxonomyTypeDesc":77},{"categoryNo":128,"categoryName":129,"taxonomyType":98,"taxonomyTypeDesc":99},"01JBSEEDCAT000000000000036","内容创作者",[122,131,132,133,134,135,136,137,109,138,139,140,24,33,141,142,143,144],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","auto","types","Video","创作",{"skillNo":146,"name":147,"nameZh":6,"description":148,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":149,"categories":150,"keywords":152,"publishedAt":37},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[151],{"categoryNo":93,"categoryName":94,"taxonomyType":76,"taxonomyTypeDesc":77},[153,154,122,155,156,143,157,158,159,160,161,162,163,164,107,165,24,33,101,166],"sync","script","youtube script sync","recording","shorts","first","base","person","update","active","child","optimize","idempotent","long",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":177,"publishedAt":37},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[173,174,175,176],{"categoryNo":74,"categoryName":75,"taxonomyType":76,"taxonomyTypeDesc":77},{"categoryNo":93,"categoryName":94,"taxonomyType":76,"taxonomyTypeDesc":77},{"categoryNo":96,"categoryName":97,"taxonomyType":98,"taxonomyTypeDesc":99},{"categoryNo":128,"categoryName":129,"taxonomyType":98,"taxonomyTypeDesc":99},[178,84,179,180,181,182,117,24,33,183,184,185,186],"writing skills","how","teach","write skill","write","claude code skills","files","how to","trigger",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":195,"publishedAt":37},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[193,194],{"categoryNo":74,"categoryName":75,"taxonomyType":76,"taxonomyTypeDesc":77},{"categoryNo":128,"categoryName":129,"taxonomyType":98,"taxonomyTypeDesc":99},[196,84,197,105,198,184,24,179,182,33,185],"Rules","writing rules","project rules"]