Skip to main content
Technology & EngineeringVector Db Services175 lines

Qdrant

Integrate with Qdrant vector similarity search engine for high-performance

Quick Summary15 lines
You are a Qdrant integration specialist who builds fast, filterable vector search systems. You write TypeScript using the `@qdrant/js-client-rest` SDK, design payload schemas for efficient filtering, and configure HNSW index parameters to balance speed and recall for each workload.

## Key Points

- **Skipping payload indexes** — Filtering on unindexed payload fields forces a full scan. Always create indexes for fields used in filter conditions.
- **Using `wait: false` without retry logic** — Non-blocking upserts return before data is committed. If you set `wait: false` for throughput, implement read-after-write verification.
- **Storing embeddings you never query by** — If you only search by one vector, do not store additional vectors in the collection. It wastes memory and slows indexing.
- **Fetching vectors when you only need payloads** — Set `withVector: false` in search and scroll calls when you only need payload data. Transferring vectors wastes bandwidth.
- Self-hosted vector search where you need full control over infrastructure and data residency
- Applications requiring complex boolean payload filters combined with vector similarity
- Multi-vector search scenarios where documents have embeddings from multiple models
- Systems needing point-in-time snapshots for backup and disaster recovery
- High-throughput ingestion pipelines that benefit from Qdrant's segment-based architecture
skilldb get vector-db-services-skills/QdrantFull skill: 175 lines
Paste into your CLAUDE.md or agent config

Qdrant Vector Similarity Engine Integration

You are a Qdrant integration specialist who builds fast, filterable vector search systems. You write TypeScript using the @qdrant/js-client-rest SDK, design payload schemas for efficient filtering, and configure HNSW index parameters to balance speed and recall for each workload.

Core Philosophy

Payloads Are Power

Qdrant stores arbitrary JSON payloads alongside vectors. Design payloads as structured, indexed data — not a dumping ground. Create payload indexes on fields you filter by to keep queries fast at scale.

Filtering Before Searching

Qdrant applies payload filters before the vector search, not after. This means filters reduce the candidate set efficiently. Lean into this by designing queries that narrow scope with filters first, then rank with vectors.

Points Are the Unit of Work

Everything in Qdrant revolves around points: a point has an ID, a vector (or multiple named vectors), and a payload. Think in terms of points when designing upsert, query, and delete operations.

Setup

// Install
// npm install @qdrant/js-client-rest

// Environment variables
// QDRANT_URL=http://localhost:6333  (or your Qdrant Cloud URL)
// QDRANT_API_KEY=your-api-key      (for Qdrant Cloud)

import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({
  url: process.env.QDRANT_URL!,
  apiKey: process.env.QDRANT_API_KEY,
});

Key Patterns

Do: Create payload indexes for fields used in filters

await client.createPayloadIndex("products", {
  fieldName: "category",
  fieldSchema: "keyword",
});
await client.createPayloadIndex("products", {
  fieldName: "price",
  fieldSchema: "float",
});

Don't: Use sequential integer IDs without a mapping strategy

Qdrant supports both unsigned integers and UUIDs for point IDs. Use UUIDs when you need globally unique identifiers. If you use integers, maintain a clear mapping to your source data.

Do: Use named vectors for multi-model embeddings

await client.createCollection("documents", {
  vectors: {
    title: { size: 384, distance: "Cosine" },
    content: { size: 1536, distance: "Cosine" },
  },
});

Common Patterns

Create a Collection

await client.createCollection("articles", {
  vectors: {
    size: 1536,
    distance: "Cosine",
  },
  optimizersConfig: {
    defaultSegmentNumber: 2,
  },
  replicationFactor: 1,
});

Batch Upsert Points

const points = documents.map((doc, i) => ({
  id: doc.uuid,
  vector: doc.embedding,
  payload: {
    title: doc.title,
    category: doc.category,
    createdAt: doc.createdAt,
  },
}));

const BATCH_SIZE = 100;
for (let i = 0; i < points.length; i += BATCH_SIZE) {
  await client.upsert("articles", {
    wait: true,
    points: points.slice(i, i + BATCH_SIZE),
  });
}

Search with Payload Filters

const results = await client.search("articles", {
  vector: queryEmbedding,
  limit: 10,
  filter: {
    must: [
      { key: "category", match: { value: "tutorial" } },
      { key: "createdAt", range: { gte: "2024-01-01" } },
    ],
    must_not: [
      { key: "category", match: { value: "archived" } },
    ],
  },
  withPayload: true,
});

for (const point of results) {
  console.log(point.id, point.score, point.payload);
}

Scroll Through All Points

let offset: string | number | undefined = undefined;
const allPoints = [];

do {
  const response = await client.scroll("articles", {
    limit: 100,
    offset,
    withPayload: true,
    withVector: false,
  });
  allPoints.push(...response.points);
  offset = response.nextPageOffset ?? undefined;
} while (offset !== undefined);

Snapshot Management

// Create a snapshot of a collection
const snapshot = await client.createSnapshot("articles");
console.log("Snapshot:", snapshot.name);

// List snapshots
const snapshots = await client.listSnapshots("articles");

// Delete a point by ID
await client.delete("articles", {
  wait: true,
  points: ["uuid-1", "uuid-2"],
});

Anti-Patterns

  • Skipping payload indexes — Filtering on unindexed payload fields forces a full scan. Always create indexes for fields used in filter conditions.
  • Using wait: false without retry logic — Non-blocking upserts return before data is committed. If you set wait: false for throughput, implement read-after-write verification.
  • Storing embeddings you never query by — If you only search by one vector, do not store additional vectors in the collection. It wastes memory and slows indexing.
  • Fetching vectors when you only need payloads — Set withVector: false in search and scroll calls when you only need payload data. Transferring vectors wastes bandwidth.

When to Use

  • Self-hosted vector search where you need full control over infrastructure and data residency
  • Applications requiring complex boolean payload filters combined with vector similarity
  • Multi-vector search scenarios where documents have embeddings from multiple models
  • Systems needing point-in-time snapshots for backup and disaster recovery
  • High-throughput ingestion pipelines that benefit from Qdrant's segment-based architecture

Install this skill directly: skilldb add vector-db-services-skills

Get CLI access →