@mastra/duckdb 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,506 @@
1
+ import { DuckDBInstance } from '@duckdb/node-api';
2
+ import { MastraVector } from '@mastra/core/vector';
3
+
4
+ // src/vector/index.ts
5
+
6
+ // src/vector/filter-builder.ts
7
+ function escapeString(value) {
8
+ return value.replace(/'/g, "''");
9
+ }
10
+ function toSqlLiteral(value) {
11
+ if (value === null || value === void 0) {
12
+ return "NULL";
13
+ }
14
+ if (typeof value === "string") {
15
+ return `'${escapeString(value)}'`;
16
+ }
17
+ if (typeof value === "number") {
18
+ return String(value);
19
+ }
20
+ if (typeof value === "boolean") {
21
+ return value ? "true" : "false";
22
+ }
23
+ return `'${escapeString(JSON.stringify(value))}'`;
24
+ }
25
+ function buildFilterClause(filter) {
26
+ if (!filter || Object.keys(filter).length === 0) {
27
+ return { clause: "1=1", params: [] };
28
+ }
29
+ const conditions = [];
30
+ for (const [key, value] of Object.entries(filter)) {
31
+ if (key === "$and") {
32
+ if (Array.isArray(value) && value.length > 0) {
33
+ const subConditions = value.map((subFilter) => buildFilterClause(subFilter));
34
+ const andClause = subConditions.map((sc) => `(${sc.clause})`).join(" AND ");
35
+ conditions.push(`(${andClause})`);
36
+ }
37
+ continue;
38
+ }
39
+ if (key === "$or") {
40
+ if (Array.isArray(value) && value.length > 0) {
41
+ const subConditions = value.map((subFilter) => buildFilterClause(subFilter));
42
+ const orClause = subConditions.map((sc) => `(${sc.clause})`).join(" OR ");
43
+ conditions.push(`(${orClause})`);
44
+ }
45
+ continue;
46
+ }
47
+ if (key === "$not") {
48
+ if (typeof value === "object" && value !== null) {
49
+ const subResult = buildFilterClause(value);
50
+ conditions.push(`NOT (${subResult.clause})`);
51
+ }
52
+ continue;
53
+ }
54
+ if (key === "$nor") {
55
+ if (Array.isArray(value) && value.length > 0) {
56
+ const subConditions = value.map((subFilter) => buildFilterClause(subFilter));
57
+ const norClause = subConditions.map((sc) => `(${sc.clause})`).join(" OR ");
58
+ conditions.push(`NOT (${norClause})`);
59
+ }
60
+ continue;
61
+ }
62
+ const fieldPath = buildJsonPath(key);
63
+ if (value === null) {
64
+ conditions.push(`${fieldPath} IS NULL`);
65
+ } else if (typeof value === "object" && !Array.isArray(value)) {
66
+ const operatorResult = buildOperatorCondition(key, value);
67
+ if (operatorResult) {
68
+ conditions.push(operatorResult);
69
+ }
70
+ } else {
71
+ conditions.push(`${fieldPath} = ${toSqlLiteral(value)}`);
72
+ }
73
+ }
74
+ if (conditions.length === 0) {
75
+ return { clause: "1=1", params: [] };
76
+ }
77
+ return { clause: conditions.join(" AND "), params: [] };
78
+ }
79
+ function buildJsonPath(field) {
80
+ const parts = field.split(".");
81
+ const jsonPath = "$." + parts.map((p) => escapeString(p)).join(".");
82
+ return `json_extract_string(metadata, '${jsonPath}')`;
83
+ }
84
+ function buildOperatorCondition(field, operators) {
85
+ const conditions = [];
86
+ const fieldPath = buildJsonPath(field);
87
+ for (const [op, value] of Object.entries(operators)) {
88
+ switch (op) {
89
+ case "$eq":
90
+ conditions.push(`${fieldPath} = ${toSqlLiteral(value)}`);
91
+ break;
92
+ case "$ne":
93
+ conditions.push(`${fieldPath} != ${toSqlLiteral(value)}`);
94
+ break;
95
+ case "$gt":
96
+ conditions.push(`CAST(${fieldPath} AS DOUBLE) > ${toSqlLiteral(value)}`);
97
+ break;
98
+ case "$gte":
99
+ conditions.push(`CAST(${fieldPath} AS DOUBLE) >= ${toSqlLiteral(value)}`);
100
+ break;
101
+ case "$lt":
102
+ conditions.push(`CAST(${fieldPath} AS DOUBLE) < ${toSqlLiteral(value)}`);
103
+ break;
104
+ case "$lte":
105
+ conditions.push(`CAST(${fieldPath} AS DOUBLE) <= ${toSqlLiteral(value)}`);
106
+ break;
107
+ case "$in":
108
+ if (Array.isArray(value) && value.length > 0) {
109
+ const literals = value.map((v) => toSqlLiteral(v)).join(", ");
110
+ conditions.push(`${fieldPath} IN (${literals})`);
111
+ } else {
112
+ conditions.push("1=0");
113
+ }
114
+ break;
115
+ case "$nin":
116
+ if (Array.isArray(value) && value.length > 0) {
117
+ const literals = value.map((v) => toSqlLiteral(v)).join(", ");
118
+ conditions.push(`${fieldPath} NOT IN (${literals})`);
119
+ }
120
+ break;
121
+ case "$exists":
122
+ if (value) {
123
+ conditions.push(`${fieldPath} IS NOT NULL`);
124
+ } else {
125
+ conditions.push(`${fieldPath} IS NULL`);
126
+ }
127
+ break;
128
+ case "$contains":
129
+ if (typeof value === "string") {
130
+ conditions.push(`${fieldPath} LIKE '%${escapeString(value)}%'`);
131
+ } else if (Array.isArray(value)) {
132
+ const jsonPath = `json_extract(metadata, '$.${escapeString(field)}')`;
133
+ const arrayConditions = value.map((v) => {
134
+ return `list_contains(TRY_CAST(${jsonPath} AS VARCHAR[]), ${toSqlLiteral(v)})`;
135
+ });
136
+ conditions.push(`(${arrayConditions.join(" AND ")})`);
137
+ } else {
138
+ conditions.push(`${fieldPath} = ${toSqlLiteral(value)}`);
139
+ }
140
+ break;
141
+ case "$not":
142
+ if (typeof value === "object" && value !== null) {
143
+ const subResult = buildOperatorCondition(field, value);
144
+ if (subResult) {
145
+ conditions.push(`NOT (${subResult})`);
146
+ }
147
+ }
148
+ break;
149
+ default:
150
+ throw new Error(`Unsupported operator: ${op}`);
151
+ }
152
+ }
153
+ if (conditions.length === 0) {
154
+ return null;
155
+ }
156
+ return conditions.join(" AND ");
157
+ }
158
+
159
+ // src/vector/index.ts
160
+ var DuckDBVector = class extends MastraVector {
161
+ config;
162
+ instance = null;
163
+ initialized = false;
164
+ initPromise = null;
165
+ constructor(config) {
166
+ super({ id: config.id });
167
+ this.config = {
168
+ path: ":memory:",
169
+ dimensions: 1536,
170
+ metric: "cosine",
171
+ ...config
172
+ };
173
+ }
174
+ /**
175
+ * Initialize the database connection and load required extensions.
176
+ */
177
+ async initialize() {
178
+ if (this.initialized && this.instance) return;
179
+ if (this.initPromise) {
180
+ await this.initPromise;
181
+ if (!this.instance) {
182
+ this.initPromise = null;
183
+ this.initialized = false;
184
+ } else {
185
+ return;
186
+ }
187
+ }
188
+ this.initPromise = (async () => {
189
+ try {
190
+ this.instance = await DuckDBInstance.create(this.config.path);
191
+ const connection = await this.instance.connect();
192
+ try {
193
+ await connection.run("INSTALL vss;");
194
+ await connection.run("LOAD vss;");
195
+ } catch {
196
+ try {
197
+ await connection.run("LOAD vss;");
198
+ } catch {
199
+ this.logger.warn("VSS extension not available, using basic array operations");
200
+ }
201
+ }
202
+ this.initialized = true;
203
+ } catch (error) {
204
+ this.instance = null;
205
+ this.initialized = false;
206
+ this.initPromise = null;
207
+ throw error;
208
+ }
209
+ })();
210
+ return this.initPromise;
211
+ }
212
+ /**
213
+ * Get a database connection.
214
+ */
215
+ async getConnection() {
216
+ await this.initialize();
217
+ if (!this.instance) {
218
+ throw new Error("DuckDB instance not initialized");
219
+ }
220
+ return this.instance.connect();
221
+ }
222
+ /**
223
+ * Execute a SQL query and return results.
224
+ */
225
+ async runQuery(sql, params = []) {
226
+ const connection = await this.getConnection();
227
+ try {
228
+ let paramIndex = 0;
229
+ const preparedSql = sql.replace(/\?/g, () => `$${++paramIndex}`);
230
+ const stmt = await connection.prepare(preparedSql);
231
+ stmt.bind(params);
232
+ const result = await stmt.run();
233
+ const rows = await result.getRows();
234
+ const columns = result.columnNames();
235
+ return rows.map((row) => {
236
+ const obj = {};
237
+ columns.forEach((col, i) => {
238
+ obj[col] = row[i];
239
+ });
240
+ return obj;
241
+ });
242
+ } finally {
243
+ }
244
+ }
245
+ /**
246
+ * Execute a SQL statement without returning results.
247
+ */
248
+ async runStatement(sql, params = []) {
249
+ const connection = await this.getConnection();
250
+ try {
251
+ if (params.length === 0) {
252
+ await connection.run(sql);
253
+ } else {
254
+ let paramIndex = 0;
255
+ const preparedSql = sql.replace(/\?/g, () => `$${++paramIndex}`);
256
+ const stmt = await connection.prepare(preparedSql);
257
+ stmt.bind(params);
258
+ await stmt.run();
259
+ }
260
+ } finally {
261
+ }
262
+ }
263
+ /**
264
+ * Validate and escape a SQL identifier (table name, column name).
265
+ */
266
+ escapeIdentifier(name) {
267
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
268
+ throw new Error(`Invalid identifier: ${name}. Only alphanumeric characters and underscores are allowed.`);
269
+ }
270
+ return `"${name}"`;
271
+ }
272
+ /**
273
+ * Get the distance function for the configured metric.
274
+ */
275
+ getDistanceFunction() {
276
+ switch (this.config.metric) {
277
+ case "cosine":
278
+ return "array_cosine_distance";
279
+ case "euclidean":
280
+ return "array_distance";
281
+ case "dotproduct":
282
+ return "array_inner_product";
283
+ default:
284
+ return "array_cosine_distance";
285
+ }
286
+ }
287
+ async query(params) {
288
+ await this.initialize();
289
+ const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;
290
+ const tableName = this.escapeIdentifier(indexName);
291
+ const distanceFunc = this.getDistanceFunction();
292
+ const vectorLiteral = `[${queryVector.join(", ")}]::FLOAT[${queryVector.length}]`;
293
+ const { clause: filterClause } = filter ? buildFilterClause(filter) : { clause: "" };
294
+ const selectCols = includeVector ? "id, vector, metadata, distance" : "id, metadata, distance";
295
+ const sql = `
296
+ SELECT ${selectCols}
297
+ FROM (
298
+ SELECT
299
+ id,
300
+ ${includeVector ? "vector," : ""}
301
+ metadata,
302
+ ${distanceFunc}(vector, ${vectorLiteral}) as distance
303
+ FROM ${tableName}
304
+ ${filterClause ? `WHERE ${filterClause}` : ""}
305
+ ) subq
306
+ ORDER BY distance ${this.config.metric === "dotproduct" ? "DESC" : "ASC"}
307
+ LIMIT ${topK}
308
+ `;
309
+ const connection = await this.getConnection();
310
+ const result = await connection.run(sql);
311
+ const rows = await result.getRows();
312
+ const columns = result.columnNames();
313
+ return rows.map((row) => {
314
+ const rowObj = {};
315
+ columns.forEach((col, i) => {
316
+ rowObj[col] = row[i];
317
+ });
318
+ const distance = rowObj.distance;
319
+ const score = this.config.metric === "cosine" ? 1 - distance : this.config.metric === "euclidean" ? 1 / (1 + distance) : distance;
320
+ const metadata = typeof rowObj.metadata === "string" ? JSON.parse(rowObj.metadata) : rowObj.metadata;
321
+ const queryResult = {
322
+ id: rowObj.id,
323
+ score,
324
+ metadata
325
+ };
326
+ if (includeVector && rowObj.vector) {
327
+ queryResult.vector = Array.isArray(rowObj.vector) ? rowObj.vector : JSON.parse(rowObj.vector);
328
+ }
329
+ return queryResult;
330
+ });
331
+ }
332
+ async upsert(params) {
333
+ await this.initialize();
334
+ const { indexName, vectors, metadata, ids } = params;
335
+ const tableName = this.escapeIdentifier(indexName);
336
+ const vectorIds = ids || vectors.map(() => crypto.randomUUID());
337
+ for (let i = 0; i < vectors.length; i++) {
338
+ const id = vectorIds[i];
339
+ const vector = vectors[i];
340
+ const meta = metadata?.[i] || {};
341
+ const vectorLiteral = `[${vector.join(", ")}]::FLOAT[${vector.length}]`;
342
+ const metadataJson = JSON.stringify(meta);
343
+ const sql = `
344
+ INSERT OR REPLACE INTO ${tableName} (id, vector, metadata)
345
+ VALUES (?, ${vectorLiteral}, '${metadataJson.replace(/'/g, "''")}')
346
+ `;
347
+ await this.runStatement(sql, [id]);
348
+ }
349
+ return vectorIds;
350
+ }
351
+ async createIndex(params) {
352
+ await this.initialize();
353
+ const { indexName, dimension, metric } = params;
354
+ const tableName = this.escapeIdentifier(indexName);
355
+ if (metric) {
356
+ this.config.metric = metric;
357
+ }
358
+ const connection = await this.getConnection();
359
+ const createTableSql = `
360
+ CREATE TABLE IF NOT EXISTS ${tableName} (
361
+ id VARCHAR PRIMARY KEY,
362
+ vector FLOAT[${dimension}],
363
+ metadata JSON
364
+ )
365
+ `;
366
+ await connection.run(createTableSql);
367
+ try {
368
+ const indexNameStr = `${indexName}_hnsw_idx`;
369
+ const createIndexSql = `
370
+ CREATE INDEX IF NOT EXISTS "${indexNameStr}"
371
+ ON ${tableName}
372
+ USING HNSW (vector)
373
+ `;
374
+ await connection.run(createIndexSql);
375
+ } catch {
376
+ this.logger.warn(`Could not create HNSW index for ${indexName}, falling back to linear scan`);
377
+ }
378
+ }
379
+ async listIndexes() {
380
+ await this.initialize();
381
+ const connection = await this.getConnection();
382
+ const result = await connection.run(`
383
+ SELECT table_name
384
+ FROM information_schema.tables
385
+ WHERE table_schema = 'main'
386
+ AND table_type = 'BASE TABLE'
387
+ `);
388
+ const rows = await result.getRows();
389
+ return rows.map((row) => row[0]);
390
+ }
391
+ async describeIndex(params) {
392
+ await this.initialize();
393
+ const { indexName } = params;
394
+ const tableName = this.escapeIdentifier(indexName);
395
+ const connection = await this.getConnection();
396
+ const schemaResult = await connection.run(`
397
+ SELECT data_type
398
+ FROM information_schema.columns
399
+ WHERE table_name = '${indexName}' AND column_name = 'vector'
400
+ `);
401
+ const schemaRows = await schemaResult.getRows();
402
+ if (schemaRows.length === 0) {
403
+ throw new Error(`Index "${indexName}" not found`);
404
+ }
405
+ const dataType = schemaRows[0][0];
406
+ const dimensionMatch = dataType.match(/\[(\d+)\]/);
407
+ const dimension = dimensionMatch ? parseInt(dimensionMatch[1], 10) : 0;
408
+ const countResult = await connection.run(`SELECT COUNT(*) as count FROM ${tableName}`);
409
+ const countRows = await countResult.getRows();
410
+ const count = Number(countRows[0]?.[0] || 0);
411
+ return {
412
+ dimension,
413
+ count,
414
+ metric: this.config.metric || "cosine"
415
+ };
416
+ }
417
+ async deleteIndex(params) {
418
+ await this.initialize();
419
+ const { indexName } = params;
420
+ const tableName = this.escapeIdentifier(indexName);
421
+ const connection = await this.getConnection();
422
+ await connection.run(`DROP TABLE IF EXISTS ${tableName}`);
423
+ }
424
+ async updateVector(params) {
425
+ await this.initialize();
426
+ const { indexName, update } = params;
427
+ const tableName = this.escapeIdentifier(indexName);
428
+ if (!update.vector && !update.metadata) {
429
+ throw new Error("No updates provided");
430
+ }
431
+ const hasId = "id" in params && params.id;
432
+ const hasFilter = "filter" in params && params.filter;
433
+ if (hasId && hasFilter) {
434
+ throw new Error("id and filter are mutually exclusive - provide only one");
435
+ }
436
+ if (!hasId && !hasFilter) {
437
+ throw new Error("Either id or filter must be provided");
438
+ }
439
+ const updates = [];
440
+ if (update.vector) {
441
+ updates.push(`vector = [${update.vector.join(", ")}]::FLOAT[${update.vector.length}]`);
442
+ }
443
+ if (update.metadata) {
444
+ const metadataJson = JSON.stringify(update.metadata).replace(/'/g, "''");
445
+ updates.push(`metadata = '${metadataJson}'`);
446
+ }
447
+ if (hasId) {
448
+ const sql = `UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = ?`;
449
+ await this.runStatement(sql, [params.id]);
450
+ } else if (hasFilter) {
451
+ const filter = params.filter;
452
+ if (Object.keys(filter).length === 0) {
453
+ throw new Error("Cannot update with empty filter");
454
+ }
455
+ const { clause } = buildFilterClause(filter);
456
+ await this.runStatement(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE ${clause}`);
457
+ }
458
+ }
459
+ async deleteVector(params) {
460
+ await this.initialize();
461
+ const { indexName, id } = params;
462
+ const tableName = this.escapeIdentifier(indexName);
463
+ const sql = `DELETE FROM ${tableName} WHERE id = ?`;
464
+ await this.runStatement(sql, [id]);
465
+ }
466
+ async deleteVectors(params) {
467
+ await this.initialize();
468
+ const { indexName, ids, filter } = params;
469
+ const tableName = this.escapeIdentifier(indexName);
470
+ if (!ids && !filter) {
471
+ throw new Error("Either filter or ids must be provided");
472
+ }
473
+ if (ids && filter) {
474
+ throw new Error("ids and filter are mutually exclusive - provide only one");
475
+ }
476
+ if (ids) {
477
+ if (ids.length === 0) {
478
+ throw new Error("Cannot delete with empty ids array");
479
+ }
480
+ const placeholders = ids.map(() => "?").join(", ");
481
+ const sql = `DELETE FROM ${tableName} WHERE id IN (${placeholders})`;
482
+ await this.runStatement(sql, ids);
483
+ } else if (filter) {
484
+ if (Object.keys(filter).length === 0) {
485
+ throw new Error("Cannot delete with empty filter");
486
+ }
487
+ const { clause } = buildFilterClause(filter);
488
+ await this.runStatement(`DELETE FROM ${tableName} WHERE ${clause}`);
489
+ }
490
+ }
491
+ /**
492
+ * Close the database connection.
493
+ * After closing, the vector store can be reused by calling methods that require initialization.
494
+ */
495
+ async close() {
496
+ if (this.instance) {
497
+ this.instance = null;
498
+ this.initialized = false;
499
+ this.initPromise = null;
500
+ }
501
+ }
502
+ };
503
+
504
+ export { DuckDBVector };
505
+ //# sourceMappingURL=index.js.map
506
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vector/filter-builder.ts","../src/vector/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { DuckDBVectorFilter } from './types.js';\n\nexport interface FilterResult {\n clause: string;\n params: unknown[];\n}\n\n/**\n * Escape a string for safe use in SQL.\n */\nfunction escapeString(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\n/**\n * Convert a value to a SQL literal for comparison with JSON-extracted values.\n * DuckDB's ->> operator returns the raw value without JSON quoting.\n */\nfunction toSqlLiteral(value: unknown): string {\n if (value === null || value === undefined) {\n return 'NULL';\n }\n if (typeof value === 'string') {\n return `'${escapeString(value)}'`;\n }\n if (typeof value === 'number') {\n return String(value);\n }\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false';\n }\n // For objects/arrays, JSON stringify but don't add extra quotes\n return `'${escapeString(JSON.stringify(value))}'`;\n}\n\n/**\n * Build a SQL WHERE clause from a filter object.\n * Supports MongoDB-style query operators.\n */\nexport function buildFilterClause(filter: DuckDBVectorFilter): FilterResult {\n if (!filter || Object.keys(filter).length === 0) {\n return { clause: '1=1', params: [] };\n }\n\n const conditions: string[] = [];\n\n for (const [key, value] of Object.entries(filter)) {\n // Handle logical operators\n if (key === '$and') {\n if (Array.isArray(value) && value.length > 0) {\n const subConditions = value.map(subFilter => buildFilterClause(subFilter));\n const andClause = subConditions.map(sc => `(${sc.clause})`).join(' AND ');\n conditions.push(`(${andClause})`);\n }\n continue;\n }\n\n if (key === '$or') {\n if (Array.isArray(value) && value.length > 0) {\n const subConditions = value.map(subFilter => buildFilterClause(subFilter));\n const orClause = subConditions.map(sc => `(${sc.clause})`).join(' OR ');\n conditions.push(`(${orClause})`);\n }\n continue;\n }\n\n if (key === '$not') {\n if (typeof value === 'object' && value !== null) {\n const subResult = buildFilterClause(value);\n conditions.push(`NOT (${subResult.clause})`);\n }\n continue;\n }\n\n if (key === '$nor') {\n if (Array.isArray(value) && value.length > 0) {\n const subConditions = value.map(subFilter => buildFilterClause(subFilter));\n const norClause = subConditions.map(sc => `(${sc.clause})`).join(' OR ');\n conditions.push(`NOT (${norClause})`);\n }\n continue;\n }\n\n // Handle field conditions\n const fieldPath = buildJsonPath(key);\n\n if (value === null) {\n conditions.push(`${fieldPath} IS NULL`);\n } else if (typeof value === 'object' && !Array.isArray(value)) {\n // Handle operators\n const operatorResult = buildOperatorCondition(key, value);\n if (operatorResult) {\n conditions.push(operatorResult);\n }\n } else {\n // Direct equality - for strings, compare directly; for others, use SQL literal\n conditions.push(`${fieldPath} = ${toSqlLiteral(value)}`);\n }\n }\n\n if (conditions.length === 0) {\n return { clause: '1=1', params: [] };\n }\n\n return { clause: conditions.join(' AND '), params: [] };\n}\n\n/**\n * Build a JSON path expression for accessing nested fields in metadata.\n * DuckDB uses json_extract_string for extracting string values from JSON.\n */\nfunction buildJsonPath(field: string): string {\n // Handle nested paths with dot notation\n const parts = field.split('.');\n\n // Build the JSON path with $ prefix for DuckDB\n const jsonPath = '$.' + parts.map(p => escapeString(p)).join('.');\n\n // Use json_extract_string for proper string extraction in DuckDB\n return `json_extract_string(metadata, '${jsonPath}')`;\n}\n\n/**\n * Build a condition from an operator object.\n */\nfunction buildOperatorCondition(field: string, operators: Record<string, unknown>): string | null {\n const conditions: string[] = [];\n const fieldPath = buildJsonPath(field);\n\n for (const [op, value] of Object.entries(operators)) {\n switch (op) {\n case '$eq':\n conditions.push(`${fieldPath} = ${toSqlLiteral(value)}`);\n break;\n\n case '$ne':\n conditions.push(`${fieldPath} != ${toSqlLiteral(value)}`);\n break;\n\n case '$gt':\n conditions.push(`CAST(${fieldPath} AS DOUBLE) > ${toSqlLiteral(value)}`);\n break;\n\n case '$gte':\n conditions.push(`CAST(${fieldPath} AS DOUBLE) >= ${toSqlLiteral(value)}`);\n break;\n\n case '$lt':\n conditions.push(`CAST(${fieldPath} AS DOUBLE) < ${toSqlLiteral(value)}`);\n break;\n\n case '$lte':\n conditions.push(`CAST(${fieldPath} AS DOUBLE) <= ${toSqlLiteral(value)}`);\n break;\n\n case '$in':\n if (Array.isArray(value) && value.length > 0) {\n const literals = value.map(v => toSqlLiteral(v)).join(', ');\n conditions.push(`${fieldPath} IN (${literals})`);\n } else {\n // Empty array - no matches\n conditions.push('1=0');\n }\n break;\n\n case '$nin':\n if (Array.isArray(value) && value.length > 0) {\n const literals = value.map(v => toSqlLiteral(v)).join(', ');\n conditions.push(`${fieldPath} NOT IN (${literals})`);\n }\n // Empty array - all matches (no condition added)\n break;\n\n case '$exists':\n if (value) {\n conditions.push(`${fieldPath} IS NOT NULL`);\n } else {\n conditions.push(`${fieldPath} IS NULL`);\n }\n break;\n\n case '$contains':\n // Check if the field contains the value (for arrays or strings)\n if (typeof value === 'string') {\n conditions.push(`${fieldPath} LIKE '%${escapeString(value)}%'`);\n } else if (Array.isArray(value)) {\n // Check if array contains all specified elements\n // Use TRY_CAST to handle type mismatches gracefully (returns NULL if not an array)\n const jsonPath = `json_extract(metadata, '$.${escapeString(field)}')`;\n const arrayConditions = value.map(v => {\n return `list_contains(TRY_CAST(${jsonPath} AS VARCHAR[]), ${toSqlLiteral(v)})`;\n });\n conditions.push(`(${arrayConditions.join(' AND ')})`);\n } else {\n // Fallback to equality\n conditions.push(`${fieldPath} = ${toSqlLiteral(value)}`);\n }\n break;\n\n case '$not':\n if (typeof value === 'object' && value !== null) {\n const subResult = buildOperatorCondition(field, value as Record<string, unknown>);\n if (subResult) {\n conditions.push(`NOT (${subResult})`);\n }\n }\n break;\n\n default:\n throw new Error(`Unsupported operator: ${op}`);\n }\n }\n\n if (conditions.length === 0) {\n return null;\n }\n\n return conditions.join(' AND ');\n}\n","import { DuckDBInstance } from '@duckdb/node-api';\nimport type { DuckDBValue } from '@duckdb/node-api';\nimport { MastraVector } from '@mastra/core/vector';\nimport type {\n IndexStats,\n QueryResult,\n QueryVectorParams,\n CreateIndexParams,\n UpsertVectorParams,\n DescribeIndexParams,\n DeleteIndexParams,\n DeleteVectorParams,\n UpdateVectorParams,\n DeleteVectorsParams,\n} from '@mastra/core/vector';\nimport { buildFilterClause } from './filter-builder.js';\nimport type { DuckDBVectorConfig, DuckDBVectorFilter } from './types.js';\n\n/**\n * DuckDB vector store implementation for Mastra.\n *\n * Provides embedded high-performance vector storage with HNSW indexing\n * using the DuckDB VSS extension for vector similarity search.\n *\n * Key features:\n * - Embedded database (no server required)\n * - HNSW indexing for fast similarity search\n * - SQL interface for metadata filtering\n * - Native Parquet support\n */\nexport class DuckDBVector extends MastraVector<DuckDBVectorFilter> {\n private config: DuckDBVectorConfig;\n private instance: DuckDBInstance | null = null;\n private initialized: boolean = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(config: DuckDBVectorConfig) {\n super({ id: config.id });\n this.config = {\n path: ':memory:',\n dimensions: 1536,\n metric: 'cosine',\n ...config,\n };\n }\n\n /**\n * Initialize the database connection and load required extensions.\n */\n private async initialize(): Promise<void> {\n if (this.initialized && this.instance) return;\n\n // If there's an existing initPromise, wait for it, but verify instance exists\n if (this.initPromise) {\n await this.initPromise;\n // If instance was closed while initializing, reset and retry\n if (!this.instance) {\n this.initPromise = null;\n this.initialized = false;\n } else {\n return;\n }\n }\n\n this.initPromise = (async () => {\n try {\n // Create DuckDB instance\n this.instance = await DuckDBInstance.create(this.config.path!);\n const connection = await this.instance.connect();\n\n try {\n // Install and load the VSS extension for vector operations\n await connection.run('INSTALL vss;');\n await connection.run('LOAD vss;');\n } catch {\n // VSS might already be installed, try just loading it\n try {\n await connection.run('LOAD vss;');\n } catch {\n // Continue without VSS - will use basic array operations\n this.logger.warn('VSS extension not available, using basic array operations');\n }\n }\n\n this.initialized = true;\n } catch (error) {\n // Reset state on error to allow retry\n this.instance = null;\n this.initialized = false;\n this.initPromise = null;\n throw error;\n }\n })();\n\n return this.initPromise;\n }\n\n /**\n * Get a database connection.\n */\n private async getConnection() {\n await this.initialize();\n if (!this.instance) {\n throw new Error('DuckDB instance not initialized');\n }\n return this.instance.connect();\n }\n\n /**\n * Execute a SQL query and return results.\n */\n private async runQuery<T = Record<string, unknown>>(sql: string, params: unknown[] = []): Promise<T[]> {\n const connection = await this.getConnection();\n try {\n // Replace ? placeholders with $1, $2, etc. for DuckDB\n let paramIndex = 0;\n const preparedSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n\n const stmt = await connection.prepare(preparedSql);\n // Bind parameters as array - DuckDB Node API bind() accepts array\n stmt.bind(params as DuckDBValue[]);\n const result = await stmt.run();\n const rows = await result.getRows();\n\n // Convert rows to objects\n const columns = result.columnNames();\n return rows.map(row => {\n const obj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n obj[col] = row[i];\n });\n return obj as T;\n });\n } finally {\n // Connection cleanup is automatic in @duckdb/node-api\n }\n }\n\n /**\n * Execute a SQL statement without returning results.\n */\n private async runStatement(sql: string, params: unknown[] = []): Promise<void> {\n const connection = await this.getConnection();\n try {\n if (params.length === 0) {\n await connection.run(sql);\n } else {\n // Replace ? placeholders with $1, $2, etc. for DuckDB\n let paramIndex = 0;\n const preparedSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n\n const stmt = await connection.prepare(preparedSql);\n // Bind parameters as array - DuckDB Node API bind() accepts array\n stmt.bind(params as DuckDBValue[]);\n await stmt.run();\n }\n } finally {\n // Connection cleanup is automatic\n }\n }\n\n /**\n * Validate and escape a SQL identifier (table name, column name).\n */\n private escapeIdentifier(name: string): string {\n // Validate identifier format\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(`Invalid identifier: ${name}. Only alphanumeric characters and underscores are allowed.`);\n }\n return `\"${name}\"`;\n }\n\n /**\n * Get the distance function for the configured metric.\n */\n private getDistanceFunction(): string {\n switch (this.config.metric) {\n case 'cosine':\n return 'array_cosine_distance';\n case 'euclidean':\n return 'array_distance';\n case 'dotproduct':\n return 'array_inner_product';\n default:\n return 'array_cosine_distance';\n }\n }\n\n async query(params: QueryVectorParams<DuckDBVectorFilter>): Promise<QueryResult[]> {\n await this.initialize();\n\n const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;\n\n const tableName = this.escapeIdentifier(indexName);\n const distanceFunc = this.getDistanceFunction();\n\n // Build the vector literal\n const vectorLiteral = `[${queryVector.join(', ')}]::FLOAT[${queryVector.length}]`;\n\n // Build filter clause\n const { clause: filterClause } = filter ? buildFilterClause(filter) : { clause: '' };\n\n // Build query\n const selectCols = includeVector ? 'id, vector, metadata, distance' : 'id, metadata, distance';\n\n const sql = `\n SELECT ${selectCols}\n FROM (\n SELECT \n id,\n ${includeVector ? 'vector,' : ''}\n metadata,\n ${distanceFunc}(vector, ${vectorLiteral}) as distance\n FROM ${tableName}\n ${filterClause ? `WHERE ${filterClause}` : ''}\n ) subq\n ORDER BY distance ${this.config.metric === 'dotproduct' ? 'DESC' : 'ASC'}\n LIMIT ${topK}\n `;\n\n const connection = await this.getConnection();\n const result = await connection.run(sql);\n const rows = await result.getRows();\n const columns = result.columnNames();\n\n return rows.map(row => {\n const rowObj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n rowObj[col] = row[i];\n });\n\n const distance = rowObj.distance as number;\n const score =\n this.config.metric === 'cosine'\n ? 1 - distance\n : this.config.metric === 'euclidean'\n ? 1 / (1 + distance)\n : distance;\n\n const metadata = typeof rowObj.metadata === 'string' ? JSON.parse(rowObj.metadata as string) : rowObj.metadata;\n\n const queryResult: QueryResult = {\n id: rowObj.id as string,\n score,\n metadata: metadata as Record<string, unknown>,\n };\n\n if (includeVector && rowObj.vector) {\n queryResult.vector = Array.isArray(rowObj.vector)\n ? (rowObj.vector as number[])\n : JSON.parse(rowObj.vector as string);\n }\n\n return queryResult;\n });\n }\n\n async upsert(params: UpsertVectorParams): Promise<string[]> {\n await this.initialize();\n\n const { indexName, vectors, metadata, ids } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n // Generate IDs if not provided\n const vectorIds = ids || vectors.map(() => crypto.randomUUID());\n\n // Insert each vector using parameterized queries for IDs\n for (let i = 0; i < vectors.length; i++) {\n const id = vectorIds[i]!;\n const vector = vectors[i]!;\n const meta = metadata?.[i] || {};\n\n const vectorLiteral = `[${vector.join(', ')}]::FLOAT[${vector.length}]`;\n const metadataJson = JSON.stringify(meta);\n\n // Use INSERT OR REPLACE for upsert behavior with parameterized ID\n const sql = `\n INSERT OR REPLACE INTO ${tableName} (id, vector, metadata)\n VALUES (?, ${vectorLiteral}, '${metadataJson.replace(/'/g, \"''\")}')\n `;\n\n await this.runStatement(sql, [id]);\n }\n\n return vectorIds;\n }\n\n async createIndex(params: CreateIndexParams): Promise<void> {\n await this.initialize();\n\n const { indexName, dimension, metric } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n // Store the metric for this index if provided\n if (metric) {\n this.config.metric = metric;\n }\n\n const connection = await this.getConnection();\n\n // Create table with vector column\n const createTableSql = `\n CREATE TABLE IF NOT EXISTS ${tableName} (\n id VARCHAR PRIMARY KEY,\n vector FLOAT[${dimension}],\n metadata JSON\n )\n `;\n\n await connection.run(createTableSql);\n\n // Create HNSW index for fast similarity search\n try {\n const indexNameStr = `${indexName}_hnsw_idx`;\n const createIndexSql = `\n CREATE INDEX IF NOT EXISTS \"${indexNameStr}\"\n ON ${tableName}\n USING HNSW (vector)\n `;\n await connection.run(createIndexSql);\n } catch {\n // HNSW index creation might fail if not supported, continue without it\n this.logger.warn(`Could not create HNSW index for ${indexName}, falling back to linear scan`);\n }\n }\n\n async listIndexes(): Promise<string[]> {\n await this.initialize();\n\n const connection = await this.getConnection();\n const result = await connection.run(`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = 'main'\n AND table_type = 'BASE TABLE'\n `);\n\n const rows = await result.getRows();\n return rows.map(row => row[0] as string);\n }\n\n async describeIndex(params: DescribeIndexParams): Promise<IndexStats> {\n await this.initialize();\n\n const { indexName } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n const connection = await this.getConnection();\n\n // Get vector dimension from table schema\n const schemaResult = await connection.run(`\n SELECT data_type\n FROM information_schema.columns\n WHERE table_name = '${indexName}' AND column_name = 'vector'\n `);\n\n const schemaRows = await schemaResult.getRows();\n\n if (schemaRows.length === 0) {\n throw new Error(`Index \"${indexName}\" not found`);\n }\n\n // Parse dimension from type like \"FLOAT[1536]\"\n const dataType = schemaRows[0]![0] as string;\n const dimensionMatch = dataType.match(/\\[(\\d+)\\]/);\n const dimension = dimensionMatch ? parseInt(dimensionMatch[1]!, 10) : 0;\n\n // Get row count\n const countResult = await connection.run(`SELECT COUNT(*) as count FROM ${tableName}`);\n const countRows = await countResult.getRows();\n const count = Number(countRows[0]?.[0] || 0);\n\n return {\n dimension,\n count,\n metric: this.config.metric || 'cosine',\n };\n }\n\n async deleteIndex(params: DeleteIndexParams): Promise<void> {\n await this.initialize();\n\n const { indexName } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n const connection = await this.getConnection();\n await connection.run(`DROP TABLE IF EXISTS ${tableName}`);\n }\n\n async updateVector(params: UpdateVectorParams<DuckDBVectorFilter>): Promise<void> {\n await this.initialize();\n\n const { indexName, update } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n if (!update.vector && !update.metadata) {\n throw new Error('No updates provided');\n }\n\n const hasId = 'id' in params && params.id;\n const hasFilter = 'filter' in params && params.filter;\n\n // Check for mutual exclusivity\n if (hasId && hasFilter) {\n throw new Error('id and filter are mutually exclusive - provide only one');\n }\n\n if (!hasId && !hasFilter) {\n throw new Error('Either id or filter must be provided');\n }\n\n const updates: string[] = [];\n\n if (update.vector) {\n updates.push(`vector = [${update.vector.join(', ')}]::FLOAT[${update.vector.length}]`);\n }\n\n if (update.metadata) {\n const metadataJson = JSON.stringify(update.metadata).replace(/'/g, \"''\");\n updates.push(`metadata = '${metadataJson}'`);\n }\n\n if (hasId) {\n // Update by ID with parameterized query\n const sql = `UPDATE ${tableName} SET ${updates.join(', ')} WHERE id = ?`;\n await this.runStatement(sql, [params.id]);\n } else if (hasFilter) {\n // Update by filter - check for empty filter\n const filter = params.filter!;\n if (Object.keys(filter).length === 0) {\n throw new Error('Cannot update with empty filter');\n }\n\n const { clause } = buildFilterClause(filter);\n // Update ALL matching vectors, not just the first one\n await this.runStatement(`UPDATE ${tableName} SET ${updates.join(', ')} WHERE ${clause}`);\n }\n }\n\n async deleteVector(params: DeleteVectorParams): Promise<void> {\n await this.initialize();\n\n const { indexName, id } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n // Use parameterized query for ID\n const sql = `DELETE FROM ${tableName} WHERE id = ?`;\n await this.runStatement(sql, [id]);\n }\n\n async deleteVectors(params: DeleteVectorsParams<DuckDBVectorFilter>): Promise<void> {\n await this.initialize();\n\n const { indexName, ids, filter } = params;\n const tableName = this.escapeIdentifier(indexName);\n\n if (!ids && !filter) {\n throw new Error('Either filter or ids must be provided');\n }\n\n if (ids && filter) {\n throw new Error('ids and filter are mutually exclusive - provide only one');\n }\n\n if (ids) {\n // Delete by IDs with parameterized query\n if (ids.length === 0) {\n throw new Error('Cannot delete with empty ids array');\n }\n\n // Create placeholders for each ID\n const placeholders = ids.map(() => '?').join(', ');\n const sql = `DELETE FROM ${tableName} WHERE id IN (${placeholders})`;\n await this.runStatement(sql, ids);\n } else if (filter) {\n // Delete by filter - check for empty filter\n if (Object.keys(filter).length === 0) {\n throw new Error('Cannot delete with empty filter');\n }\n\n const { clause } = buildFilterClause(filter);\n await this.runStatement(`DELETE FROM ${tableName} WHERE ${clause}`);\n }\n }\n\n /**\n * Close the database connection.\n * After closing, the vector store can be reused by calling methods that require initialization.\n */\n async close(): Promise<void> {\n if (this.instance) {\n // DuckDBInstance doesn't have a close method - just reset the reference\n // The garbage collector will handle cleanup\n this.instance = null;\n this.initialized = false;\n this.initPromise = null; // Reset initPromise to allow re-initialization\n }\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { DuckDBVectorFilter } from './types.js';
2
+ export interface FilterResult {
3
+ clause: string;
4
+ params: unknown[];
5
+ }
6
+ /**
7
+ * Build a SQL WHERE clause from a filter object.
8
+ * Supports MongoDB-style query operators.
9
+ */
10
+ export declare function buildFilterClause(filter: DuckDBVectorFilter): FilterResult;
11
+ //# sourceMappingURL=filter-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-builder.d.ts","sourceRoot":"","sources":["../../src/vector/filter-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB;AA8BD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAkE1E"}
@@ -0,0 +1,61 @@
1
+ import { MastraVector } from '@mastra/core/vector';
2
+ import type { IndexStats, QueryResult, QueryVectorParams, CreateIndexParams, UpsertVectorParams, DescribeIndexParams, DeleteIndexParams, DeleteVectorParams, UpdateVectorParams, DeleteVectorsParams } from '@mastra/core/vector';
3
+ import type { DuckDBVectorConfig, DuckDBVectorFilter } from './types.js';
4
+ /**
5
+ * DuckDB vector store implementation for Mastra.
6
+ *
7
+ * Provides embedded high-performance vector storage with HNSW indexing
8
+ * using the DuckDB VSS extension for vector similarity search.
9
+ *
10
+ * Key features:
11
+ * - Embedded database (no server required)
12
+ * - HNSW indexing for fast similarity search
13
+ * - SQL interface for metadata filtering
14
+ * - Native Parquet support
15
+ */
16
+ export declare class DuckDBVector extends MastraVector<DuckDBVectorFilter> {
17
+ private config;
18
+ private instance;
19
+ private initialized;
20
+ private initPromise;
21
+ constructor(config: DuckDBVectorConfig);
22
+ /**
23
+ * Initialize the database connection and load required extensions.
24
+ */
25
+ private initialize;
26
+ /**
27
+ * Get a database connection.
28
+ */
29
+ private getConnection;
30
+ /**
31
+ * Execute a SQL query and return results.
32
+ */
33
+ private runQuery;
34
+ /**
35
+ * Execute a SQL statement without returning results.
36
+ */
37
+ private runStatement;
38
+ /**
39
+ * Validate and escape a SQL identifier (table name, column name).
40
+ */
41
+ private escapeIdentifier;
42
+ /**
43
+ * Get the distance function for the configured metric.
44
+ */
45
+ private getDistanceFunction;
46
+ query(params: QueryVectorParams<DuckDBVectorFilter>): Promise<QueryResult[]>;
47
+ upsert(params: UpsertVectorParams): Promise<string[]>;
48
+ createIndex(params: CreateIndexParams): Promise<void>;
49
+ listIndexes(): Promise<string[]>;
50
+ describeIndex(params: DescribeIndexParams): Promise<IndexStats>;
51
+ deleteIndex(params: DeleteIndexParams): Promise<void>;
52
+ updateVector(params: UpdateVectorParams<DuckDBVectorFilter>): Promise<void>;
53
+ deleteVector(params: DeleteVectorParams): Promise<void>;
54
+ deleteVectors(params: DeleteVectorsParams<DuckDBVectorFilter>): Promise<void>;
55
+ /**
56
+ * Close the database connection.
57
+ * After closing, the vector store can be reused by calling methods that require initialization.
58
+ */
59
+ close(): Promise<void>;
60
+ }
61
+ //# sourceMappingURL=index.d.ts.map