vexify
Version:
Portable vector database with in-process ONNX embeddings. Zero-config semantic search via SQLite. No external servers required.
78 lines (67 loc) • 1.78 kB
JavaScript
;
class SqliteVecSearch {
constructor(db, tableName = 'vec_index', dimensions = 768) {
this.db = db;
this.tableName = tableName;
this.dimensions = dimensions;
}
async initialize() {
this.db.exec(`
CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING vec0(
id TEXT PRIMARY KEY,
embedding FLOAT[${this.dimensions}]
)
`);
}
async addDocument(doc) {
const vectorJson = JSON.stringify(doc.vector);
try {
const stmt = this.db.prepare(`
INSERT INTO ${this.tableName} (id, embedding)
VALUES (?, ?)
`);
stmt.run(doc.id, vectorJson);
} catch (e) {
if (e.message && e.message.includes('UNIQUE constraint')) {
return;
}
throw e;
}
}
async searchIndex(queryVector, topK) {
const queryJson = JSON.stringify(queryVector);
const stmt = this.db.prepare(`
SELECT
d.id,
d.content,
d.metadata,
distance
FROM ${this.tableName} v
JOIN documents d ON v.id = d.id
WHERE embedding MATCH ?
AND k = ?
ORDER BY distance
`);
const rows = stmt.all(queryJson, topK);
return rows.map(row => ({
id: row.id,
content: row.content ? JSON.parse(row.content) : null,
metadata: row.metadata ? JSON.parse(row.metadata) : null,
score: 1 - row.distance
}));
}
async delete(id) {
const stmt = this.db.prepare(`DELETE FROM ${this.tableName} WHERE id = ?`);
stmt.run(id);
}
async getAllIds() {
try {
const stmt = this.db.prepare(`SELECT id FROM ${this.tableName}`);
const rows = stmt.all();
return rows.map(row => row.id);
} catch (e) {
return [];
}
}
}
module.exports = { SqliteVecSearch };