Qdrant Patterns
Quick Guide: Use
@qdrant/js-client-rest(v1.17.x) for high-performance vector search. Collections define vector dimensions and distance metrics upfront -- mismatches cause silent failures. Usemust/should/must_notfilter clauses with payload conditions (not Pinecone-style$eq/$and). Payload indexes are optional but critical for filter performance at scale -- create them explicitly withcreatePayloadIndex(). Named vectors let you store multiple embeddings per point (e.g., title + content). Quantization (scalar/binary/product) trades accuracy for memory and speed. Thequery()method is the universal search endpoint -- prefer it over the oldersearch()method.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST create payload indexes with createPayloadIndex() for any field used in filters -- unindexed fields cause full scans that degrade linearly with collection size)
(You MUST use must/should/must_not filter syntax -- Qdrant does NOT use $eq/$and/$or operators like Pinecone)
(You MUST match vector dimensions exactly between embedding model output and collection config -- dimension mismatches cause silent upsert failures or corrupt search results)
(You MUST set wait: true on writes when subsequent reads depend on the data -- Qdrant writes are asynchronous by default and may not be immediately visible)
</critical_requirements>
Examples
- Core Patterns -- Client setup, collection creation, upsert, query, scroll, delete
- Filtering -- must/should/must_not conditions, match/range operators, payload indexes
- Named Vectors & Quantization -- Multiple vectors per point, scalar/binary/product quantization
- Recommendations & Batch -- Recommend API, batch operations, snapshots
Additional resources:
- reference.md -- API quick reference, filter operators, limits, decision frameworks, production checklist
Auto-detection: Qdrant, QdrantClient, @qdrant/js-client-rest, createCollection, upsert, query, scroll, recommend, setPayload, createPayloadIndex, must, should, must_not, payload, named vectors, quantization, vector database, similarity search, semantic search, RAG retrieval, embedding search
When to use:
- Semantic search over document embeddings (RAG retrieval pipelines)
- Similarity search for recommendations, deduplication, or classification
- Multi-vector search with named vectors (e.g., title embedding + content embedding per document)
- Filtered vector search with complex payload conditions (must/should/must_not)
- Memory-optimized deployments using scalar, binary, or product quantization
Key patterns covered:
- Client setup and collection management (distance metrics, HNSW config)
- Point CRUD operations (upsert, query, scroll, retrieve, delete, count)
- Payload filtering with must/should/must_not and match/range conditions
- Named vectors for multiple embeddings per point
- Quantization configuration (scalar, binary, product)
- Recommendation API with positive/negative examples
- Batch operations and snapshot management
- Payload indexing for filter performance
When NOT to use:
- Full-text search with BM25 ranking (use a dedicated search engine)
- Relational data with joins and transactions (use a relational database)
- Key-value lookups without vector similarity (use a KV store)
- Storing large documents or binary blobs (store embeddings + metadata references only)
<philosophy>
Philosophy
Qdrant is a high-performance open-source vector database built in Rust, designed for filtered similarity search at scale. The core principle: store vectors with rich payloads, search by similarity, filter by payload conditions.
Core principles:
- Payload is first-class -- Unlike databases that treat metadata as secondary, Qdrant's payload system supports complex nested JSON, multiple data types, and granular indexing. Use payloads for filtering, not just annotation.
- Index what you filter -- Payload indexes are not automatic. Create explicit indexes on fields used in filters via
createPayloadIndex(). Without indexes, filters cause full collection scans. - Named vectors for multi-modal -- A single point can hold multiple named vectors (e.g., title embedding + content embedding). Search targets a specific named vector. This avoids duplicating payloads across collections.
- Quantization for scale -- Scalar (4x compression), binary (32x), and product quantization trade accuracy for memory savings. Configure at collection or per-vector level. Use
always_ram: trueto keep quantized vectors in memory for speed. - Writes are async by default -- Upserts return before data is persisted to all replicas. Set
wait: truewhen immediate consistency matters (e.g., read-after-write flows).
<patterns>
Core Patterns
Pattern 1: Client Initialization
Create a QdrantClient connected to a local instance or Qdrant Cloud. See examples/core.md for full examples.
// Good Example
import { QdrantClient } from "@qdrant/js-client-rest";
function createQdrantClient(): QdrantClient {
const url = process.env.QDRANT_URL;
const apiKey = process.env.QDRANT_API_KEY;
if (!url) {
throw new Error("QDRANT_URL environment variable is required");
}
return new QdrantClient({ url, apiKey });
}
export { createQdrantClient };
Why good: URL and API key from environment, validation before construction, named export
// Bad Example
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({
host: "my-cluster.cloud.qdrant.io",
apiKey: "sk-abc123...",
});
// Hardcoded credentials leak in version control
Why bad: Hardcoded API key, host without HTTPS (use url with full protocol for cloud)
Pattern 2: Collection Creation
Define vector dimensions and distance metric. Dimension must exactly match your embedding model output. See examples/core.md.
// Good Example
const EMBEDDING_DIMENSION = 1536;
await client.createCollection("documents", {
vectors: {
size: EMBEDDING_DIMENSION,
distance: "Cosine",
},
});
export { EMBEDDING_DIMENSION };
Why good: Named constant for dimension, explicit distance metric, clean config
// Bad Example
await client.createCollection("documents", {
vectors: { size: 768, distance: "Cosine" },
// Dimension mismatch if using a 1536-dim model -- upserts may silently fail or produce garbage search results
});
Why bad: Hardcoded dimension that may not match embedding model, no named constant
Pattern 3: Upsert Points with Payload
Upsert vectors with payload (Qdrant's term for metadata). See examples/core.md.
// Good Example
interface DocumentPayload {
title: string;
category: string;
createdAt: number;
tags: string[];
}
await client.upsert("documents", {
wait: true,
points: [
{
id: "doc-1",
vector: embedding,
payload: {
title: "Guide",
category: "tutorial",
createdAt: 1710000000,
tags: ["ai", "search"],
},
},
],
});
Why good: Typed payload interface, wait: true for immediate consistency, structured payload
Pattern 4: Query with Payload Filter
Use must/should/must_not filter clauses -- NOT Pinecone-style $eq/$and. See examples/filtering.md.
// Good Example
const TOP_K = 10;
const results = await client.query("documents", {
query: queryEmbedding,
filter: {
must: [
{ key: "category", mat