Skip to content

Qdrant Vector Storage

The @graphrag-js/qdrant package provides high-performance vector storage using Qdrant, an open-source vector database optimized for similarity search.

Installation

bash
pnpm add @graphrag-js/qdrant

Features

  • High Performance - Optimized HNSW indexing
  • Multiple Metrics - Cosine, Euclidean, Dot product
  • Advanced Filtering - Rich metadata queries
  • Horizontal Scaling - Distributed deployment support
  • Payload Storage - Store metadata with vectors
  • Collection Management - Namespace isolation

Prerequisites

Qdrant Database

Option 1: Docker (Recommended)

bash
docker run -d \
  --name qdrant \
  -p 6333:6333 \
  -p 6334:6334 \
  -v qdrant_storage:/qdrant/storage \
  qdrant/qdrant:latest

Option 2: Qdrant Cloud

Option 3: Local Binary

bash
wget https://github.com/qdrant/qdrant/releases/download/v1.7.0/qdrant
chmod +x qdrant
./qdrant

Verify Installation

Visit http://localhost:6333/dashboard or:

bash
curl http://localhost:6333/collections

Quick Start

typescript
import { createGraph } from '@graphrag-js/core';
import { similarityGraph } from '@graphrag-js/similarity';
import { qdrantVector } from '@graphrag-js/qdrant';
import { openai } from '@ai-sdk/openai';

const graph = createGraph({
  model: openai('gpt-4o-mini'),
  embedding: openai.embedding('text-embedding-3-small'),
  provider: similarityGraph(),
  storage: {
    vector: qdrantVector({
      url: 'http://localhost:6333',
      apiKey: undefined, // Optional for local
    }),
  }
});

await graph.insert('Your documents...');
const result = await graph.query('Your question?');

Configuration

qdrantVector(config)

typescript
interface QdrantVectorConfig {
  url: string;      // Qdrant server URL
  apiKey?: string;  // API key (required for cloud)
}

Connection Examples

typescript
// Local instance
qdrantVector({
  url: 'http://localhost:6333',
})

// Qdrant Cloud
qdrantVector({
  url: 'https://xxx-yyy-zzz.us-east.aws.cloud.qdrant.io:6333',
  apiKey: 'your-api-key',
})

Usage Examples

Basic Vector Operations

typescript
import { qdrantVector } from '@graphrag-js/qdrant';

const vectorStore = qdrantVector({
  url: 'http://localhost:6333',
})('my-namespace');

// Create index
await vectorStore.createIndex({
  indexName: 'documents',
  dimension: 1536,
  metric: 'cosine',
});

// Upsert vectors
await vectorStore.upsert({
  indexName: 'documents',
  vectors: [[0.1, 0.2, ...], [0.3, 0.4, ...]],
  metadata: [
    { text: 'Document 1', type: 'article' },
    { text: 'Document 2', type: 'blog' },
  ],
  ids: ['doc-1', 'doc-2'],
});

// Query with filtering
const results = await vectorStore.query({
  queryVector: [0.15, 0.25, ...],
  topK: 10,
  filter: { type: 'article' },
  includeVectors: false,
});

With LightRAG

Qdrant is ideal for LightRAG's dual vector search:

typescript
import { lightrag } from '@graphrag-js/lightrag';
import { qdrantVector } from '@graphrag-js/qdrant';

const graph = createGraph({
  model: openai('gpt-4o-mini'),
  embedding: openai.embedding('text-embedding-3-small'),
  provider: lightrag({
    entityTypes: ['person', 'organization', 'location'],
  }),
  storage: {
    vector: qdrantVector({
      url: 'http://localhost:6333',
    }),
  }
});

// LightRAG creates separate collections for:
// - Entity vectors
// - Relationship vectors
// - Chunk vectors

Metadata Filtering

typescript
// Filter by metadata fields
const results = await vectorStore.query({
  queryVector: embedding,
  topK: 5,
  filter: {
    category: 'technology',
    year: 2024,
    verified: true,
  },
});

Advanced Features

Distance Metrics

typescript
// Cosine similarity (default, best for normalized vectors)
await vectorStore.createIndex({
  indexName: 'cosine-index',
  dimension: 1536,
  metric: 'cosine',
});

// Euclidean distance (best for non-normalized vectors)
await vectorStore.createIndex({
  indexName: 'euclidean-index',
  dimension: 1536,
  metric: 'euclidean',
});

// Dot product (fast, good for normalized vectors)
await vectorStore.createIndex({
  indexName: 'dotproduct-index',
  dimension: 1536,
  metric: 'dotproduct',
});

Collection Management

typescript
// List all collections
const collections = await vectorStore.listIndexes();

// Get collection stats
const stats = await vectorStore.describeIndex({
  indexName: 'documents',
});
console.log(stats); // { dimension: 1536, count: 10000, metric: 'cosine' }

// Delete collection
await vectorStore.deleteIndex({
  indexName: 'documents',
});

Batch Operations

typescript
// Batch upsert for better performance
const vectors = Array(1000).fill(0).map(() =>
  Array(1536).fill(0).map(() => Math.random())
);

const ids = await vectorStore.upsert({
  indexName: 'documents',
  vectors,
  metadata: vectors.map((_, i) => ({ id: `doc-${i}` })),
});

Performance Optimization

HNSW Parameters

Qdrant uses HNSW (Hierarchical Navigable Small World) indexing. Tune via Qdrant API:

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

const client = new QdrantClient({
  url: 'http://localhost:6333',
});

await client.createCollection('optimized', {
  vectors: {
    size: 1536,
    distance: 'Cosine',
  },
  hnsw_config: {
    m: 16,                // Connections per layer (higher = better quality, more memory)
    ef_construct: 100,    // Build-time search (higher = better quality, slower indexing)
  },
  optimizers_config: {
    indexing_threshold: 20000,  // When to build index
  },
});

// Set search-time parameters
await client.search('optimized', {
  vector: queryVector,
  limit: 10,
  params: {
    hnsw_ef: 128,        // Search-time quality (higher = better quality, slower)
  },
});

Quantization

Reduce memory usage with scalar quantization:

typescript
await client.updateCollection('documents', {
  quantization_config: {
    scalar: {
      type: 'int8',
      quantile: 0.99,
    },
  },
});

Monitoring & Debugging

Qdrant Dashboard

Access at http://localhost:6333/dashboard

Features:

  • Collection overview
  • Search interface
  • Performance metrics
  • Point inspection

API Monitoring

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

const client = new QdrantClient({ url: 'http://localhost:6333' });

// Collection info
const info = await client.getCollection('documents');
console.log('Points:', info.points_count);
console.log('Segments:', info.segments_count);

// Cluster status (if using distributed mode)
const cluster = await client.getCluster();
console.log('Peers:', cluster.peers);

Performance Testing

bash
# Benchmark search performance
curl -X POST 'http://localhost:6333/collections/documents/points/search' \
  -H 'Content-Type: application/json' \
  -d '{
    "vector": [0.1, 0.2, ...],
    "limit": 10,
    "with_payload": false
  }'

Production Deployment

Docker Compose

yaml
version: '3.8'
services:
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"  # HTTP API
      - "6334:6334"  # gRPC (optional)
    volumes:
      - qdrant-storage:/qdrant/storage
    environment:
      QDRANT__SERVICE__GRPC_PORT: 6334
    restart: unless-stopped

volumes:
  qdrant-storage:

Distributed Setup

For high availability and horizontal scaling:

yaml
version: '3.8'
services:
  qdrant-1:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"
    volumes:
      - qdrant-1-storage:/qdrant/storage
    environment:
      QDRANT__CLUSTER__ENABLED: true
      QDRANT__CLUSTER__P2P__PORT: 6335

  qdrant-2:
    image: qdrant/qdrant:latest
    ports:
      - "6433:6333"
    volumes:
      - qdrant-2-storage:/qdrant/storage
    environment:
      QDRANT__CLUSTER__ENABLED: true
      QDRANT__CLUSTER__P2P__PORT: 6335
      QDRANT__CLUSTER__BOOTSTRAP__PEER: qdrant-1:6335

volumes:
  qdrant-1-storage:
  qdrant-2-storage:

Backup Strategy

bash
# Create snapshot
curl -X POST 'http://localhost:6333/collections/documents/snapshots'

# List snapshots
curl 'http://localhost:6333/collections/documents/snapshots'

# Download snapshot
curl 'http://localhost:6333/collections/documents/snapshots/snapshot-2024-01-01.snapshot' \
  --output backup.snapshot

# Restore from snapshot
curl -X PUT 'http://localhost:6333/collections/documents/snapshots/upload' \
  -F 'snapshot=@backup.snapshot'

Troubleshooting

Connection Refused

Error: Failed to fetch: ECONNREFUSED

Solution:

  1. Verify Qdrant is running: docker ps | grep qdrant
  2. Check port: 6333 for HTTP
  3. Test: curl http://localhost:6333/collections

Dimension Mismatch

Error: Vector dimension mismatch

Solution:

  • Ensure embedding dimension matches index dimension
  • For OpenAI text-embedding-3-small: 1536 dimensions
  • For OpenAI text-embedding-3-large: 3072 dimensions

Out of Memory

Error: Cannot allocate memory

Solution:

  1. Enable quantization to reduce memory
  2. Increase Docker memory limit
  3. Use smaller HNSW m parameter

Cost Considerations

DeploymentCostBest For
Self-hostedFree + hostingFull control, any scale
Qdrant Cloud StarterFree (1M vectors)Testing, small apps
Qdrant Cloud Pro$25+/monthProduction workloads
Qdrant Cloud EnterpriseCustomEnterprise scale

Benchmarks

On 1M vectors (1536-dim, M1 Mac):

OperationLatencyThroughput
Insert5-10ms100-200 vec/s
Search (k=10)10-20ms50-100 qps
Batch insert (100)50-100ms1000-2000 vec/s

Next Steps

Released under the Elastic License 2.0.