Migrate from Qdrant to REM Labs

Qdrant is a solid vector database with filtering and payload support. REM Labs extends beyond vector search with multi-signal fusion, entity graphs, and memory-specific features like temporal decay and Dream Engine. This guide walks through a complete migration.

Step 1: Export from Qdrant

Use the Qdrant client to scroll through all points in a collection and export them with payloads:

import { QdrantClient } from "@qdrant/js-client-rest"; import fs from "fs"; const qdrant = new QdrantClient({ url: "http://localhost:6333" }); const collectionName = "documents"; const exported = []; let offset = null; while (true) { const response = await qdrant.scroll(collectionName, { limit: 100, offset, with_payload: true, with_vectors: false // We only need payloads }); for (const point of response.points) { exported.push({ id: point.id, payload: point.payload, text: point.payload?.text || point.payload?.content || "" }); } offset = response.next_page_offset; if (!offset) break; } fs.writeFileSync("qdrant-export.json", JSON.stringify(exported, null, 2)); console.log(`Exported ${exported.length} points`);

Step 2: Import into REM Labs

import { RemClient } from "@remlabs/sdk"; import fs from "fs"; const rem = new RemClient({ apiKey: process.env.REMLABS_API_KEY }); const data = JSON.parse(fs.readFileSync("qdrant-export.json", "utf-8")); let count = 0; for (const point of data) { if (!point.text) continue; await rem.remember({ content: point.text, namespace: "qdrant-import", metadata: { qdrant_id: String(point.id), ...point.payload }, tags: point.payload?.tags || [] }); count++; if (count % 100 === 0) console.log(`${count}/${data.length}`); } console.log(`Imported ${count} memories`);

Step 3: Replace Qdrant Queries

Qdrant requires you to pass embedding vectors for search. REM Labs accepts natural language and handles embeddings internally:

// Before (Qdrant -- requires embedding computation) const embedding = await getEmbedding(userQuery); const results = await qdrant.search(collectionName, { vector: embedding, limit: 5, filter: { must: [{ key: "category", match: { value: "support" } }] } }); // After (REM Labs -- natural language, no embedding step) const results = await rem.recall({ query: userQuery, namespace: "qdrant-import", limit: 5 });

Mapping Qdrant Filters to REM Namespaces

Qdrant uses payload filters for scoping. In REM Labs, use namespaces and tags for the same purpose:

// Qdrant: filter by category const results = await qdrant.search("docs", { vector: embedding, filter: { must: [{ key: "category", match: { value: "faq" } }] } }); // REM Labs: use namespace or tag-based organization // Option 1: Separate namespace per category const results = await rem.recall({ query: "How do refunds work?", namespace: "docs:faq", limit: 5 }); // Option 2: Use tags (searched across all namespaces) const results = await rem.recall({ query: "How do refunds work?", tags: ["faq"], limit: 5 });

Batch Migration Script

For large collections, process in parallel with rate limiting:

import pLimit from "p-limit"; const limit = pLimit(10); // 10 concurrent requests const tasks = data.map(point => limit(async () => { if (!point.text) return; await rem.remember({ content: point.text, namespace: "qdrant-import", metadata: { qdrant_id: String(point.id) } }); }) ); await Promise.all(tasks); console.log("Migration complete");

No vector management: REM Labs generates embeddings from raw text, so you never need to compute, store, or version embeddings yourself. This eliminates the embedding pipeline from your architecture entirely.

Move from vectors to memories

Free tier. No embedding management. Neural reranking on every query.

Get Started