// 把 MongoDB knowledge 集合里已有的向量重建进 DashVector 索引。 // // 何时用:①首次接入 DashVector(建集合 + 灌存量向量);②补齐入库时同步失败的块(标了 vectorPending 的); // ③换过 embedding 维度后重灌(见下"换维度")。 // // 幂等性:本脚本用 insert 语义,只【补齐缺失】的向量、灌成功后清 vectorPending 标记。它【不覆盖】已存在 // 同 _id 的向量(在线入库每块都是全新 _id,不会撞;故正常场景无需覆盖)。 // ⚠ 换维度 / 修复"已存在但内容错"的向量:insert 不覆盖,必须【新建集合】——把 .env 的 DASHVECTOR_COLLECTION // 改成新名(或在阿里云控制台删掉旧集合)后再跑本脚本。ensureCollection 已会在维度不符时【大声报错】提示你。 // // 用法(服务器后端目录内,.env 里已配好 DASHVECTOR_*): node scripts/reindex-vectors.js // MongoDB 永远是真相源,本脚本只把真相源投影进向量索引;不改动 knowledge 任何数据(仅清 vectorPending 标记)。 import 'dotenv/config'; import { connectDb, getDb, closeDb } from '../src/db.js'; import { config } from '../src/config.js'; import { vectorEnabled, ensureCollection, vectorUpsert } from '../src/services/vectorStore.js'; async function run() { if (!vectorEnabled()) { console.error('❌ 向量库未启用。请在 .env 配 DASHVECTOR_ENDPOINT / DASHVECTOR_API_KEY(和可选 DASHVECTOR_COLLECTION),再重跑。'); process.exit(1); } console.log(`向量库: DashVector · collection=${config.vector.dashvector.collection} · dim=${config.vector.dashvector.dim}`); console.log('① 确保集合存在且就绪(维度不符会在此报错)…'); const c = await ensureCollection(); console.log(c.existed ? ' 集合已存在,复用。' : c.notReady ? ' 集合已创建(尚未就绪,后续写入会自动重试)。' : ' 集合已创建、就绪。'); await connectDb(); const db = getDb(); const cursor = db.collection('knowledge') .find({ embedding: { $type: 'array' } }) .project({ embedding: 1, twinId: 1, sourceType: 1 }) .addCursorFlag('noCursorTimeout', true) // 大集合 + 慢/限流的 DashVector:防两次 getMore 间的阻塞 upsert 超过游标空闲超时(10min) .batchSize(500); let batch = []; let ids = []; let total = 0; const BATCH = 200; const flush = async () => { if (!batch.length) return; await vectorUpsert(batch); // 灌成功 → 清掉这些块入库时可能打上的 vectorPending 标记(它们已进索引)。 try { await db.collection('knowledge').updateMany({ _id: { $in: ids } }, { $unset: { vectorPending: '' } }); } catch { /* 清标失败无害 */ } total += batch.length; process.stdout.write(`\r 已重建 ${total} 块…`); batch = []; ids = []; }; console.log('② 遍历 knowledge 集合,灌入向量…'); for await (const d of cursor) { if (!Array.isArray(d.embedding) || !d.embedding.length) continue; batch.push({ id: String(d._id), embedding: d.embedding, twinId: String(d.twinId), sourceType: d.sourceType || 'text' }); ids.push(d._id); if (batch.length >= BATCH) await flush(); } await flush(); console.log(`\n✅ 完成。共重建 ${total} 块向量进 DashVector。`); await closeDb(); } run().catch((e) => { console.error('\n❌ 重建失败:', e.message); process.exit(1); });