Integration
Migration
April 13, 2026
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