Skip to content

@memberjunction/ai-vectors-qdrant

MemberJunction vector database provider backed by Qdrant. Implements the VectorDBBase interface using the official @qdrant/js-client-rest client.

  • A running Qdrant instance (local Docker container or Qdrant Cloud cluster)
  • For Qdrant Cloud, an API key with appropriate permissions
Terminal window
npm install @memberjunction/ai-vectors-qdrant

Each logical “index” maps to a Qdrant collection. A collection stores points, where each point consists of:

ComponentDescription
IDA string or integer identifier for the point
VectorThe embedding (dimension and distance metric are fixed at collection creation)
PayloadArbitrary JSON key-value metadata stored alongside the vector

When you call CreateIndex, the provider creates a Qdrant collection with the specified vector size and distance metric. You can pass additional Qdrant-specific collection parameters (e.g., HNSW config, quantization) via params.additionalParams.

Records are upserted via the Qdrant upsert operation, so calling CreateRecord or CreateRecords with an existing ID overwrites the previous point.

Queries use Qdrant’s native ANN search. When a vector is provided, the provider calls query() directly. When only a point id is provided, the provider first retrieves that point’s vector, then uses it as the query vector.

DeleteAllRecords works by deleting and recreating the collection with the same vector configuration, since Qdrant does not expose a single “truncate” operation.

MJ Metric ValueQdrant DistanceDescription
cosine (default)CosineCosine similarity (normalized dot product)
euclideanEuclidEuclidean (L2) distance
dotproductDotDot product (inner product)

The distance metric is set at collection creation time and cannot be changed afterward.

VariableDefaultDescription
QDRANT_URLhttp://localhost:6333Qdrant server URL. For Qdrant Cloud, use the full cluster URL (e.g., https://xyz.us-east-1-0.aws.cloud.qdrant.io:6333).
QDRANT_API_KEY(none)API key for authentication. Required for Qdrant Cloud; optional for local instances.

The apiKey constructor parameter is passed directly to the Qdrant client. If empty or 'default', no API key header is sent (suitable for local instances):

// Local instance (no auth)
const db = new QdrantDatabase('default');
// Qdrant Cloud
const db = new QdrantDatabase(process.env.QDRANT_API_KEY);
import { QdrantDatabase } from '@memberjunction/ai-vectors-qdrant';
const db = new QdrantDatabase(process.env.QDRANT_API_KEY || 'default');
// Create a collection
await db.CreateIndex({
id: 'my-collection',
dimension: 1536,
metric: 'cosine',
});
// Upsert records
await db.CreateRecords([
{
id: 'vec-1',
values: [0.1, 0.2, ...],
metadata: { Entity: 'Documents', RecordID: '123' },
},
{
id: 'vec-2',
values: [0.3, 0.4, ...],
metadata: { Entity: 'Documents', RecordID: '456' },
},
], 'my-collection');
// Query by vector similarity
const results = await db.QueryIndex({
id: 'my-collection',
vector: [0.1, 0.2, ...],
topK: 10,
includeMetadata: true,
});
// List all collections
const indexes = await db.ListIndexes();
// Get collection details (dimension, metric)
const info = await db.GetIndex({ id: 'my-collection' });
// Delete specific records
await db.DeleteRecords(
[{ id: 'vec-1', values: [] }],
'my-collection'
);
// Delete all records (drops and recreates the collection)
await db.DeleteAllRecords('my-collection');
// Delete the collection entirely
await db.DeleteIndex({ id: 'my-collection' });

Qdrant uses a structured filter format with must, should, and must_not clauses. This provider supports two approaches for building filters.

The recommended approach uses BuildMetadataFilter(), which accepts the provider-agnostic SharedIndexFilterOptions interface and produces a Qdrant-native filter:

const filter = db.BuildMetadataFilter({
EntityName: 'Documents',
RecordIDs: ['rec-1', 'rec-2'],
});
const results = await db.QueryIndex({
id: 'my-collection',
vector: [...],
topK: 10,
filter,
});

Under the hood, BuildMetadataFilter performs the following mapping:

SharedIndexFilterOptions fieldQdrant filter clause
EntityNamemust: [{ key: "Entity", match: { value: "Documents" } }]
RecordIDs (single value)must: [{ key: "RecordID", match: { value: "rec-1" } }]
RecordIDs (multiple values)must: [{ key: "RecordID", match: { any: ["rec-1", "rec-2"] } }]
Additional conditions via VectorMetadataFilter.BuildConditionsMapped with eq -> match.value, in -> match.any, contains -> match.value

All conditions are combined into a single must array (AND logic).

You can also pass Qdrant’s native filter syntax directly as the filter property on QueryIndex:

const results = await db.QueryIndex({
id: 'my-collection',
vector: [...],
topK: 10,
filter: {
must: [
{ key: 'category', match: { value: 'science' } },
{ key: 'year', range: { gte: 2020 } },
],
must_not: [
{ key: 'status', match: { value: 'archived' } },
],
},
});

This gives you full access to Qdrant’s filtering capabilities including range filters, geo filters, and nested conditions. See the Qdrant filtering documentation for the complete filter syntax.

To retrieve points by ID, pass the collection name in params.data:

const result = await db.GetRecords({
id: 'ignored',
data: {
collectionName: 'my-collection',
ids: ['vec-1', 'vec-2'],
},
});

Updates use upsert for records with vectors and setPayload for metadata-only updates:

await db.UpdateRecord({
id: 'vec-1',
values: [0.5, 0.6, ...], // new vector (uses upsert)
metadata: { status: 'updated' },
data: { collectionName: 'my-collection' },
});
// Metadata-only update (no vector change)
await db.UpdateRecord({
id: 'vec-1',
metadata: { status: 'reviewed' },
data: { collectionName: 'my-collection' },
});

Qdrant supports updating collection-level parameters (optimizer settings, etc.) via EditIndex:

await db.EditIndex({
id: 'my-collection',
data: {
optimizers_config: {
indexing_threshold: 20000,
},
},
});
  • No hybrid search: The SupportsHybridSearch property is not overridden and defaults to the base class behavior. Pure vector search only.
  • ListIndexes dimension/metric: The ListIndexes method returns dimension: 0 and metric: 'cosine' for all collections because the Qdrant list endpoint does not return per-collection vector configuration. Use GetIndex for accurate details.
  • DeleteAllRecords is destructive: The collection is dropped and recreated, which resets all optimizer and indexing state.
  • String IDs only: While Qdrant supports both string and integer point IDs, the MJ interface uses string IDs throughout. Integer IDs are converted to strings in results.
  • Single vector per point: Named vector configurations are not currently exposed; only the default (unnamed) vector is used.
  • No namespace support: Qdrant does not have a namespace concept. The namespace parameter on DeleteAllRecords is ignored.