From api-vector-db-qdrant
Qdrant vector database -- collection management, point operations, payload filtering, named vectors, quantization, recommendations, snapshots
How this skill is triggered — by the user, by Claude, or both
Slash command
/api-vector-db-qdrant:api-vector-db-qdrantThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **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. Use `must`/`should`/`must_not` filter clauses with payload conditions (not Pinecone-style `$eq`/`$and`). Payload indexes are optional but critical for filter performance at scale -- create them explic...
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>
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>
Additional resources:
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:
Key patterns covered:
When NOT to use:
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:
createPayloadIndex(). Without indexes, filters cause full collection scans.always_ram: true to keep quantized vectors in memory for speed.wait: true when immediate consistency matters (e.g., read-after-write flows).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)
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
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
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", match: { value: "tutorial" } },
{ key: "createdAt", range: { gte: 1700000000 } },
],
},
with_payload: true,
limit: TOP_K,
});
for (const point of results.points) {
console.log(point.id, point.score, point.payload);
}
Why good: Named constant for limit, Qdrant filter syntax (must + match/range), with_payload included
// Bad Example -- Pinecone syntax does NOT work in Qdrant
const results = await client.query("documents", {
query: embedding,
filter: {
$and: [{ category: { $eq: "tutorial" } }],
},
limit: 100,
});
Why bad: Pinecone-style $and/$eq operators are invalid in Qdrant, magic number for limit
Store multiple embeddings per point. See examples/named-vectors-quantization.md.
// Good Example
const TITLE_DIM = 384;
const CONTENT_DIM = 1536;
await client.createCollection("articles", {
vectors: {
title: { size: TITLE_DIM, distance: "Cosine" },
content: { size: CONTENT_DIM, distance: "Cosine" },
},
});
// Upsert with named vectors
await client.upsert("articles", {
wait: true,
points: [
{
id: "article-1",
vector: { title: titleEmbedding, content: contentEmbedding },
payload: { title: "Intro to Vectors" },
},
],
});
// Search by specific named vector
const results = await client.query("articles", {
query: queryEmbedding,
using: "content",
limit: TOP_K,
});
Why good: Different dimensions per named vector, using specifies which vector to search, avoids duplicating payloads across collections
Find similar points using positive/negative examples. See examples/recommendations-batch.md.
// Good Example
const results = await client.query("documents", {
query: {
recommend: {
positive: [1, 42],
negative: [7],
strategy: "best_score",
},
},
limit: TOP_K,
with_payload: true,
});
Why good: Uses point IDs as positive/negative examples, best_score strategy handles negatives better than default average_vector
<decision_framework>
Which distance metric should I use?
|-- Using normalized embeddings (OpenAI, Cohere)? -> Cosine (most common, safe default)
|-- Pre-normalized embeddings and need speed? -> Dot (faster, same results as Cosine for unit vectors)
|-- Raw feature vectors where magnitude matters? -> Euclid (L2 distance)
|-- City-block distance needed? -> Manhattan
'-- Unsure? -> Cosine (works with any embedding model)
How many embeddings per point?
|-- One embedding model? -> Single vector (simpler config)
|-- Multiple embedding models (title + content)? -> Named vectors
|-- Same model, different text segments? -> Named vectors
|-- Multi-modal (text + image)? -> Named vectors with different dimensions
'-- Want to avoid duplicating payloads across collections? -> Named vectors
How should I optimize memory?
|-- Good default, balanced accuracy/speed? -> Scalar (int8, 4x compression)
|-- Maximum speed, can tolerate accuracy loss? -> Binary (32x compression)
| '-- Best with high-dimensional models (>= 1024 dims)
|-- Maximum compression, speed not critical? -> Product (up to 64x compression)
| '-- Slowest quantization, most accuracy loss
'-- No memory pressure? -> Skip quantization (full float32 precision)
Should I create a payload index?
|-- Field used in filter conditions? -> YES, always index
|-- Field used in order_by for scroll? -> YES, index for sort performance
|-- Field only read after search (display only)? -> NO, skip index
|-- High-cardinality field (UUIDs, timestamps)? -> YES, but evaluate index type
'-- Low-cardinality field (enum-like)? -> YES, keyword index is very efficient
</decision_framework>
<red_flags>
High Priority Issues:
$eq, $and, $or) -- Qdrant uses must/should/must_not with match/range conditionswait: true when read-after-write consistency is needed -- writes are async by defaultMedium Priority Issues:
search() method instead of query() -- query() is the universal endpoint with prefetch and fusion supportwith_payload: true in queries -- payload is NOT included by defaultoffset for deep pagination in scroll -- performance degrades; use offset as cursor (point ID), not page numberCommon Mistakes:
filter at the wrong nesting level -- filter goes at the top level of the query args, not nested inside another objectid: 0 as a point ID -- Qdrant requires positive integers or UUID strings; 0 is invalidsetPayload (merge) with overwritePayload (replace) -- setPayload merges fields, overwritePayload replaces the entire payloaddeletePayload with field names but no point selector -- you must specify which points to update via points array or filterGotchas & Edge Cases:
scroll() with order_by requires a payload index on the sort field -- without it, the request failscount() with exact: true is slow on large collections -- use exact: false (default) for approximate countsquery() with prefetch enables multi-stage retrieval (retrieve 1000, then re-rank to top 10) -- but requires understanding the prefetch pipelineusing parameter -- omitting it searches the default (unnamed) vector, which may not existdeletePayload removes specific keys, clearPayload removes ALL keys -- they are different operations</red_flags>
<critical_reminders>
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)
Failure to follow these rules will cause empty search results, degraded filter performance, data consistency issues, and hard-to-debug dimension mismatch errors.
</critical_reminders>
npx claudepluginhub agents-inc/skills --plugin api-vector-db-qdrantGuides vector database selection for embeddings and semantic search, compares managed options like Pinecone and self-hosted like pgvector/Milvus, explains ANN algorithms like HNSW.
Optimizes Qdrant vector search performance covering indexing strategies, query optimization, search speed, indexing performance, and memory usage. Use to improve speed and efficiency of Qdrant deployment.
Provides patterns and Python templates for similarity search with vector databases, including metrics, indexes, and Pinecone implementation. Use for semantic search, RAG, recommendations, and scaling.