taladb 0.3.0 → 0.5.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.d.mts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +79 -52
- package/dist/index.mjs +79 -52
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -5,6 +5,26 @@ interface VectorIndexOptions {
|
|
|
5
5
|
dimensions: number;
|
|
6
6
|
/** Similarity metric. Defaults to `"cosine"`. */
|
|
7
7
|
metric?: VectorMetric;
|
|
8
|
+
/**
|
|
9
|
+
* Index algorithm. Defaults to `"flat"` (exact brute-force).
|
|
10
|
+
* Use `"hnsw"` for approximate nearest-neighbour search — much faster on
|
|
11
|
+
* large collections at the cost of occasional missed results.
|
|
12
|
+
* Requires the `vector-hnsw` feature to be compiled in.
|
|
13
|
+
*/
|
|
14
|
+
indexType?: 'flat' | 'hnsw';
|
|
15
|
+
/** HNSW connectivity parameter M (default 16). Higher = better recall, more memory. */
|
|
16
|
+
hnswM?: number;
|
|
17
|
+
/** HNSW build-time quality parameter ef_construction (default 200). */
|
|
18
|
+
hnswEfConstruction?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Describes the indexes that exist on a collection. */
|
|
21
|
+
interface CollectionIndexInfo {
|
|
22
|
+
/** B-tree indexes (created with `createIndex`). */
|
|
23
|
+
btree: string[];
|
|
24
|
+
/** Full-text search indexes (created with `createFtsIndex`). */
|
|
25
|
+
fts: string[];
|
|
26
|
+
/** Vector indexes (created with `createVectorIndex`). */
|
|
27
|
+
vector: string[];
|
|
8
28
|
}
|
|
9
29
|
/** A single result returned by `Collection.findNearest`. */
|
|
10
30
|
interface VectorSearchResult<T extends Document = Document> {
|
|
@@ -72,6 +92,25 @@ interface Collection<T extends Document = Document> {
|
|
|
72
92
|
count(filter?: Filter<T>): Promise<number>;
|
|
73
93
|
createIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
74
94
|
dropIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Create a full-text search index on a string field.
|
|
97
|
+
*
|
|
98
|
+
* Enables fast `{ field: { $contains: 'token' } }` queries using an
|
|
99
|
+
* inverted token index instead of a full collection scan.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* await notes.createFtsIndex('body');
|
|
103
|
+
*/
|
|
104
|
+
createFtsIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
105
|
+
/** Drop a full-text search index. */
|
|
106
|
+
dropFtsIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Return the indexes that currently exist on this collection.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* const { btree, fts, vector } = await notes.listIndexes();
|
|
112
|
+
*/
|
|
113
|
+
listIndexes(): Promise<CollectionIndexInfo>;
|
|
75
114
|
/**
|
|
76
115
|
* Create a vector index on a numeric-array field.
|
|
77
116
|
*
|
|
@@ -84,6 +123,14 @@ interface Collection<T extends Document = Document> {
|
|
|
84
123
|
createVectorIndex(field: keyof Omit<T, '_id'> & string, options: VectorIndexOptions): Promise<void>;
|
|
85
124
|
/** Drop a vector index. */
|
|
86
125
|
dropVectorIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Upgrade a flat vector index to HNSW in-place.
|
|
128
|
+
*
|
|
129
|
+
* After calling this, `findNearest` uses approximate nearest-neighbour
|
|
130
|
+
* search which is significantly faster on large collections.
|
|
131
|
+
* Requires the `vector-hnsw` feature to be compiled in; no-op otherwise.
|
|
132
|
+
*/
|
|
133
|
+
upgradeVectorIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
87
134
|
/**
|
|
88
135
|
* Find the `topK` most similar documents to `vector` using a vector index.
|
|
89
136
|
*
|
|
@@ -136,4 +183,4 @@ interface TalaDB {
|
|
|
136
183
|
*/
|
|
137
184
|
declare function openDB(dbName?: string): Promise<TalaDB>;
|
|
138
185
|
|
|
139
|
-
export { type Collection, type Document, type Filter, type TalaDB, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
|
186
|
+
export { type Collection, type CollectionIndexInfo, type Document, type Filter, type TalaDB, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,26 @@ interface VectorIndexOptions {
|
|
|
5
5
|
dimensions: number;
|
|
6
6
|
/** Similarity metric. Defaults to `"cosine"`. */
|
|
7
7
|
metric?: VectorMetric;
|
|
8
|
+
/**
|
|
9
|
+
* Index algorithm. Defaults to `"flat"` (exact brute-force).
|
|
10
|
+
* Use `"hnsw"` for approximate nearest-neighbour search — much faster on
|
|
11
|
+
* large collections at the cost of occasional missed results.
|
|
12
|
+
* Requires the `vector-hnsw` feature to be compiled in.
|
|
13
|
+
*/
|
|
14
|
+
indexType?: 'flat' | 'hnsw';
|
|
15
|
+
/** HNSW connectivity parameter M (default 16). Higher = better recall, more memory. */
|
|
16
|
+
hnswM?: number;
|
|
17
|
+
/** HNSW build-time quality parameter ef_construction (default 200). */
|
|
18
|
+
hnswEfConstruction?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Describes the indexes that exist on a collection. */
|
|
21
|
+
interface CollectionIndexInfo {
|
|
22
|
+
/** B-tree indexes (created with `createIndex`). */
|
|
23
|
+
btree: string[];
|
|
24
|
+
/** Full-text search indexes (created with `createFtsIndex`). */
|
|
25
|
+
fts: string[];
|
|
26
|
+
/** Vector indexes (created with `createVectorIndex`). */
|
|
27
|
+
vector: string[];
|
|
8
28
|
}
|
|
9
29
|
/** A single result returned by `Collection.findNearest`. */
|
|
10
30
|
interface VectorSearchResult<T extends Document = Document> {
|
|
@@ -72,6 +92,25 @@ interface Collection<T extends Document = Document> {
|
|
|
72
92
|
count(filter?: Filter<T>): Promise<number>;
|
|
73
93
|
createIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
74
94
|
dropIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Create a full-text search index on a string field.
|
|
97
|
+
*
|
|
98
|
+
* Enables fast `{ field: { $contains: 'token' } }` queries using an
|
|
99
|
+
* inverted token index instead of a full collection scan.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* await notes.createFtsIndex('body');
|
|
103
|
+
*/
|
|
104
|
+
createFtsIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
105
|
+
/** Drop a full-text search index. */
|
|
106
|
+
dropFtsIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Return the indexes that currently exist on this collection.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* const { btree, fts, vector } = await notes.listIndexes();
|
|
112
|
+
*/
|
|
113
|
+
listIndexes(): Promise<CollectionIndexInfo>;
|
|
75
114
|
/**
|
|
76
115
|
* Create a vector index on a numeric-array field.
|
|
77
116
|
*
|
|
@@ -84,6 +123,14 @@ interface Collection<T extends Document = Document> {
|
|
|
84
123
|
createVectorIndex(field: keyof Omit<T, '_id'> & string, options: VectorIndexOptions): Promise<void>;
|
|
85
124
|
/** Drop a vector index. */
|
|
86
125
|
dropVectorIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Upgrade a flat vector index to HNSW in-place.
|
|
128
|
+
*
|
|
129
|
+
* After calling this, `findNearest` uses approximate nearest-neighbour
|
|
130
|
+
* search which is significantly faster on large collections.
|
|
131
|
+
* Requires the `vector-hnsw` feature to be compiled in; no-op otherwise.
|
|
132
|
+
*/
|
|
133
|
+
upgradeVectorIndex(field: keyof Omit<T, '_id'> & string): Promise<void>;
|
|
87
134
|
/**
|
|
88
135
|
* Find the `topK` most similar documents to `vector` using a vector index.
|
|
89
136
|
*
|
|
@@ -136,4 +183,4 @@ interface TalaDB {
|
|
|
136
183
|
*/
|
|
137
184
|
declare function openDB(dbName?: string): Promise<TalaDB>;
|
|
138
185
|
|
|
139
|
-
export { type Collection, type Document, type Filter, type TalaDB, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
|
186
|
+
export { type Collection, type CollectionIndexInfo, type Document, type Filter, type TalaDB, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
package/dist/index.js
CHANGED
|
@@ -67,11 +67,42 @@ var WorkerProxy = class {
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
|
+
function makePoller(findFn, callback) {
|
|
71
|
+
let active = true;
|
|
72
|
+
let lastJson = "";
|
|
73
|
+
const poll = async () => {
|
|
74
|
+
if (!active) return;
|
|
75
|
+
try {
|
|
76
|
+
const docs = await findFn();
|
|
77
|
+
const json = JSON.stringify(docs);
|
|
78
|
+
if (json !== lastJson) {
|
|
79
|
+
lastJson = json;
|
|
80
|
+
callback(docs);
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
if (active) setTimeout(poll, 300);
|
|
85
|
+
};
|
|
86
|
+
poll();
|
|
87
|
+
return () => {
|
|
88
|
+
active = false;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
70
91
|
async function createBrowserDB(dbName) {
|
|
71
92
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import_meta.url);
|
|
72
93
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
73
94
|
const proxy = new WorkerProxy(worker);
|
|
74
95
|
await proxy.send("init", { dbName });
|
|
96
|
+
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
97
|
+
let channel = null;
|
|
98
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
99
|
+
channel = new BroadcastChannel(`taladb:${dbName}`);
|
|
100
|
+
channel.onmessage = (e) => {
|
|
101
|
+
if (e.data === "taladb:changed") {
|
|
102
|
+
for (const nudge of nudgeCallbacks) nudge();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
75
106
|
function wrapCollection(name) {
|
|
76
107
|
const s = JSON.stringify;
|
|
77
108
|
return {
|
|
@@ -115,13 +146,26 @@ async function createBrowserDB(dbName) {
|
|
|
115
146
|
}),
|
|
116
147
|
createIndex: (field) => proxy.send("createIndex", { collection: name, field }),
|
|
117
148
|
dropIndex: (field) => proxy.send("dropIndex", { collection: name, field }),
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
149
|
+
createFtsIndex: (field) => proxy.send("createFtsIndex", { collection: name, field }),
|
|
150
|
+
dropFtsIndex: (field) => proxy.send("dropFtsIndex", { collection: name, field }),
|
|
151
|
+
createVectorIndex: (field, options) => {
|
|
152
|
+
if (options.indexType === "hnsw") return Promise.reject(new Error("HNSW vector indexes are not available in the browser (requires native threads). Use Node.js or React Native."));
|
|
153
|
+
return proxy.send("createVectorIndex", {
|
|
154
|
+
collection: name,
|
|
155
|
+
field,
|
|
156
|
+
dimensions: options.dimensions,
|
|
157
|
+
metric: options.metric,
|
|
158
|
+
indexType: null,
|
|
159
|
+
hnswM: null,
|
|
160
|
+
hnswEfConstruction: null
|
|
161
|
+
});
|
|
162
|
+
},
|
|
124
163
|
dropVectorIndex: (field) => proxy.send("dropVectorIndex", { collection: name, field }),
|
|
164
|
+
upgradeVectorIndex: (_field) => Promise.reject(new Error("HNSW vector indexes are not available in the browser (requires native threads). Use Node.js or React Native.")),
|
|
165
|
+
listIndexes: async () => {
|
|
166
|
+
const json = await proxy.send("listIndexes", { collection: name });
|
|
167
|
+
return JSON.parse(json);
|
|
168
|
+
},
|
|
125
169
|
findNearest: async (field, vector, topK, filter) => {
|
|
126
170
|
const json = await proxy.send("findNearest", {
|
|
127
171
|
collection: name,
|
|
@@ -134,9 +178,14 @@ async function createBrowserDB(dbName) {
|
|
|
134
178
|
},
|
|
135
179
|
subscribe: (filter, callback) => {
|
|
136
180
|
let active = true;
|
|
137
|
-
let lastJson = "";
|
|
181
|
+
let lastJson = "[]";
|
|
182
|
+
let timer = null;
|
|
138
183
|
const poll = async () => {
|
|
139
184
|
if (!active) return;
|
|
185
|
+
if (timer !== null) {
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
timer = null;
|
|
188
|
+
}
|
|
140
189
|
try {
|
|
141
190
|
const json = await proxy.send("find", {
|
|
142
191
|
collection: name,
|
|
@@ -148,11 +197,17 @@ async function createBrowserDB(dbName) {
|
|
|
148
197
|
}
|
|
149
198
|
} catch {
|
|
150
199
|
}
|
|
151
|
-
if (active) setTimeout(poll, 300);
|
|
200
|
+
if (active) timer = setTimeout(poll, 300);
|
|
152
201
|
};
|
|
202
|
+
nudgeCallbacks.add(poll);
|
|
153
203
|
poll();
|
|
154
204
|
return () => {
|
|
155
205
|
active = false;
|
|
206
|
+
nudgeCallbacks.delete(poll);
|
|
207
|
+
if (timer !== null) {
|
|
208
|
+
clearTimeout(timer);
|
|
209
|
+
timer = null;
|
|
210
|
+
}
|
|
156
211
|
};
|
|
157
212
|
}
|
|
158
213
|
};
|
|
@@ -160,6 +215,7 @@ async function createBrowserDB(dbName) {
|
|
|
160
215
|
return {
|
|
161
216
|
collection: (name) => wrapCollection(name),
|
|
162
217
|
close: async () => {
|
|
218
|
+
channel?.close();
|
|
163
219
|
await proxy.send("close");
|
|
164
220
|
worker.terminate();
|
|
165
221
|
}
|
|
@@ -182,33 +238,20 @@ async function createNodeDB(dbName) {
|
|
|
182
238
|
count: async (filter) => col.count(filter ?? null),
|
|
183
239
|
createIndex: async (field) => col.createIndex(field),
|
|
184
240
|
dropIndex: async (field) => col.dropIndex(field),
|
|
185
|
-
|
|
241
|
+
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
242
|
+
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
243
|
+
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
186
244
|
dropVectorIndex: async (field) => col.dropVectorIndex(field),
|
|
245
|
+
upgradeVectorIndex: async (field) => col.upgradeVectorIndex(field),
|
|
246
|
+
listIndexes: async () => {
|
|
247
|
+
const json = col.listIndexes();
|
|
248
|
+
return JSON.parse(json);
|
|
249
|
+
},
|
|
187
250
|
findNearest: async (field, vector, topK, filter) => {
|
|
188
251
|
const raw = await col.findNearest(field, vector, topK, filter ?? null);
|
|
189
252
|
return raw;
|
|
190
253
|
},
|
|
191
|
-
subscribe: (filter, callback) =>
|
|
192
|
-
let active = true;
|
|
193
|
-
let lastJson = "";
|
|
194
|
-
const poll = async () => {
|
|
195
|
-
if (!active) return;
|
|
196
|
-
try {
|
|
197
|
-
const docs = await col.find(filter ?? null);
|
|
198
|
-
const json = JSON.stringify(docs);
|
|
199
|
-
if (json !== lastJson) {
|
|
200
|
-
lastJson = json;
|
|
201
|
-
callback(docs);
|
|
202
|
-
}
|
|
203
|
-
} catch {
|
|
204
|
-
}
|
|
205
|
-
if (active) setTimeout(poll, 300);
|
|
206
|
-
};
|
|
207
|
-
poll();
|
|
208
|
-
return () => {
|
|
209
|
-
active = false;
|
|
210
|
-
};
|
|
211
|
-
}
|
|
254
|
+
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? null), callback)
|
|
212
255
|
};
|
|
213
256
|
}
|
|
214
257
|
return {
|
|
@@ -239,33 +282,17 @@ async function createNativeDB(_dbName) {
|
|
|
239
282
|
count: async (filter) => col.count(filter ?? {}),
|
|
240
283
|
createIndex: async (field) => col.createIndex(field),
|
|
241
284
|
dropIndex: async (field) => col.dropIndex(field),
|
|
242
|
-
|
|
285
|
+
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
286
|
+
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
287
|
+
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
243
288
|
dropVectorIndex: async (field) => col.dropVectorIndex(field),
|
|
289
|
+
upgradeVectorIndex: async (field) => col.upgradeVectorIndex(field),
|
|
290
|
+
listIndexes: async () => JSON.parse(col.listIndexes()),
|
|
244
291
|
findNearest: async (field, vector, topK, filter) => {
|
|
245
292
|
const raw = col.findNearest(field, vector, topK, filter ?? null);
|
|
246
293
|
return raw;
|
|
247
294
|
},
|
|
248
|
-
subscribe: (filter, callback) => {
|
|
249
|
-
let active = true;
|
|
250
|
-
let lastJson = "";
|
|
251
|
-
const poll = () => {
|
|
252
|
-
if (!active) return;
|
|
253
|
-
try {
|
|
254
|
-
const docs = col.find(filter ?? {});
|
|
255
|
-
const json = JSON.stringify(docs);
|
|
256
|
-
if (json !== lastJson) {
|
|
257
|
-
lastJson = json;
|
|
258
|
-
callback(docs);
|
|
259
|
-
}
|
|
260
|
-
} catch {
|
|
261
|
-
}
|
|
262
|
-
if (active) setTimeout(poll, 300);
|
|
263
|
-
};
|
|
264
|
-
poll();
|
|
265
|
-
return () => {
|
|
266
|
-
active = false;
|
|
267
|
-
};
|
|
268
|
-
}
|
|
295
|
+
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? {}), callback)
|
|
269
296
|
};
|
|
270
297
|
}
|
|
271
298
|
return {
|
package/dist/index.mjs
CHANGED
|
@@ -32,11 +32,42 @@ var WorkerProxy = class {
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
|
+
function makePoller(findFn, callback) {
|
|
36
|
+
let active = true;
|
|
37
|
+
let lastJson = "";
|
|
38
|
+
const poll = async () => {
|
|
39
|
+
if (!active) return;
|
|
40
|
+
try {
|
|
41
|
+
const docs = await findFn();
|
|
42
|
+
const json = JSON.stringify(docs);
|
|
43
|
+
if (json !== lastJson) {
|
|
44
|
+
lastJson = json;
|
|
45
|
+
callback(docs);
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
if (active) setTimeout(poll, 300);
|
|
50
|
+
};
|
|
51
|
+
poll();
|
|
52
|
+
return () => {
|
|
53
|
+
active = false;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
35
56
|
async function createBrowserDB(dbName) {
|
|
36
57
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import.meta.url);
|
|
37
58
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
38
59
|
const proxy = new WorkerProxy(worker);
|
|
39
60
|
await proxy.send("init", { dbName });
|
|
61
|
+
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
62
|
+
let channel = null;
|
|
63
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
64
|
+
channel = new BroadcastChannel(`taladb:${dbName}`);
|
|
65
|
+
channel.onmessage = (e) => {
|
|
66
|
+
if (e.data === "taladb:changed") {
|
|
67
|
+
for (const nudge of nudgeCallbacks) nudge();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
40
71
|
function wrapCollection(name) {
|
|
41
72
|
const s = JSON.stringify;
|
|
42
73
|
return {
|
|
@@ -80,13 +111,26 @@ async function createBrowserDB(dbName) {
|
|
|
80
111
|
}),
|
|
81
112
|
createIndex: (field) => proxy.send("createIndex", { collection: name, field }),
|
|
82
113
|
dropIndex: (field) => proxy.send("dropIndex", { collection: name, field }),
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
114
|
+
createFtsIndex: (field) => proxy.send("createFtsIndex", { collection: name, field }),
|
|
115
|
+
dropFtsIndex: (field) => proxy.send("dropFtsIndex", { collection: name, field }),
|
|
116
|
+
createVectorIndex: (field, options) => {
|
|
117
|
+
if (options.indexType === "hnsw") return Promise.reject(new Error("HNSW vector indexes are not available in the browser (requires native threads). Use Node.js or React Native."));
|
|
118
|
+
return proxy.send("createVectorIndex", {
|
|
119
|
+
collection: name,
|
|
120
|
+
field,
|
|
121
|
+
dimensions: options.dimensions,
|
|
122
|
+
metric: options.metric,
|
|
123
|
+
indexType: null,
|
|
124
|
+
hnswM: null,
|
|
125
|
+
hnswEfConstruction: null
|
|
126
|
+
});
|
|
127
|
+
},
|
|
89
128
|
dropVectorIndex: (field) => proxy.send("dropVectorIndex", { collection: name, field }),
|
|
129
|
+
upgradeVectorIndex: (_field) => Promise.reject(new Error("HNSW vector indexes are not available in the browser (requires native threads). Use Node.js or React Native.")),
|
|
130
|
+
listIndexes: async () => {
|
|
131
|
+
const json = await proxy.send("listIndexes", { collection: name });
|
|
132
|
+
return JSON.parse(json);
|
|
133
|
+
},
|
|
90
134
|
findNearest: async (field, vector, topK, filter) => {
|
|
91
135
|
const json = await proxy.send("findNearest", {
|
|
92
136
|
collection: name,
|
|
@@ -99,9 +143,14 @@ async function createBrowserDB(dbName) {
|
|
|
99
143
|
},
|
|
100
144
|
subscribe: (filter, callback) => {
|
|
101
145
|
let active = true;
|
|
102
|
-
let lastJson = "";
|
|
146
|
+
let lastJson = "[]";
|
|
147
|
+
let timer = null;
|
|
103
148
|
const poll = async () => {
|
|
104
149
|
if (!active) return;
|
|
150
|
+
if (timer !== null) {
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
timer = null;
|
|
153
|
+
}
|
|
105
154
|
try {
|
|
106
155
|
const json = await proxy.send("find", {
|
|
107
156
|
collection: name,
|
|
@@ -113,11 +162,17 @@ async function createBrowserDB(dbName) {
|
|
|
113
162
|
}
|
|
114
163
|
} catch {
|
|
115
164
|
}
|
|
116
|
-
if (active) setTimeout(poll, 300);
|
|
165
|
+
if (active) timer = setTimeout(poll, 300);
|
|
117
166
|
};
|
|
167
|
+
nudgeCallbacks.add(poll);
|
|
118
168
|
poll();
|
|
119
169
|
return () => {
|
|
120
170
|
active = false;
|
|
171
|
+
nudgeCallbacks.delete(poll);
|
|
172
|
+
if (timer !== null) {
|
|
173
|
+
clearTimeout(timer);
|
|
174
|
+
timer = null;
|
|
175
|
+
}
|
|
121
176
|
};
|
|
122
177
|
}
|
|
123
178
|
};
|
|
@@ -125,6 +180,7 @@ async function createBrowserDB(dbName) {
|
|
|
125
180
|
return {
|
|
126
181
|
collection: (name) => wrapCollection(name),
|
|
127
182
|
close: async () => {
|
|
183
|
+
channel?.close();
|
|
128
184
|
await proxy.send("close");
|
|
129
185
|
worker.terminate();
|
|
130
186
|
}
|
|
@@ -147,33 +203,20 @@ async function createNodeDB(dbName) {
|
|
|
147
203
|
count: async (filter) => col.count(filter ?? null),
|
|
148
204
|
createIndex: async (field) => col.createIndex(field),
|
|
149
205
|
dropIndex: async (field) => col.dropIndex(field),
|
|
150
|
-
|
|
206
|
+
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
207
|
+
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
208
|
+
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
151
209
|
dropVectorIndex: async (field) => col.dropVectorIndex(field),
|
|
210
|
+
upgradeVectorIndex: async (field) => col.upgradeVectorIndex(field),
|
|
211
|
+
listIndexes: async () => {
|
|
212
|
+
const json = col.listIndexes();
|
|
213
|
+
return JSON.parse(json);
|
|
214
|
+
},
|
|
152
215
|
findNearest: async (field, vector, topK, filter) => {
|
|
153
216
|
const raw = await col.findNearest(field, vector, topK, filter ?? null);
|
|
154
217
|
return raw;
|
|
155
218
|
},
|
|
156
|
-
subscribe: (filter, callback) =>
|
|
157
|
-
let active = true;
|
|
158
|
-
let lastJson = "";
|
|
159
|
-
const poll = async () => {
|
|
160
|
-
if (!active) return;
|
|
161
|
-
try {
|
|
162
|
-
const docs = await col.find(filter ?? null);
|
|
163
|
-
const json = JSON.stringify(docs);
|
|
164
|
-
if (json !== lastJson) {
|
|
165
|
-
lastJson = json;
|
|
166
|
-
callback(docs);
|
|
167
|
-
}
|
|
168
|
-
} catch {
|
|
169
|
-
}
|
|
170
|
-
if (active) setTimeout(poll, 300);
|
|
171
|
-
};
|
|
172
|
-
poll();
|
|
173
|
-
return () => {
|
|
174
|
-
active = false;
|
|
175
|
-
};
|
|
176
|
-
}
|
|
219
|
+
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? null), callback)
|
|
177
220
|
};
|
|
178
221
|
}
|
|
179
222
|
return {
|
|
@@ -204,33 +247,17 @@ async function createNativeDB(_dbName) {
|
|
|
204
247
|
count: async (filter) => col.count(filter ?? {}),
|
|
205
248
|
createIndex: async (field) => col.createIndex(field),
|
|
206
249
|
dropIndex: async (field) => col.dropIndex(field),
|
|
207
|
-
|
|
250
|
+
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
251
|
+
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
252
|
+
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
208
253
|
dropVectorIndex: async (field) => col.dropVectorIndex(field),
|
|
254
|
+
upgradeVectorIndex: async (field) => col.upgradeVectorIndex(field),
|
|
255
|
+
listIndexes: async () => JSON.parse(col.listIndexes()),
|
|
209
256
|
findNearest: async (field, vector, topK, filter) => {
|
|
210
257
|
const raw = col.findNearest(field, vector, topK, filter ?? null);
|
|
211
258
|
return raw;
|
|
212
259
|
},
|
|
213
|
-
subscribe: (filter, callback) => {
|
|
214
|
-
let active = true;
|
|
215
|
-
let lastJson = "";
|
|
216
|
-
const poll = () => {
|
|
217
|
-
if (!active) return;
|
|
218
|
-
try {
|
|
219
|
-
const docs = col.find(filter ?? {});
|
|
220
|
-
const json = JSON.stringify(docs);
|
|
221
|
-
if (json !== lastJson) {
|
|
222
|
-
lastJson = json;
|
|
223
|
-
callback(docs);
|
|
224
|
-
}
|
|
225
|
-
} catch {
|
|
226
|
-
}
|
|
227
|
-
if (active) setTimeout(poll, 300);
|
|
228
|
-
};
|
|
229
|
-
poll();
|
|
230
|
-
return () => {
|
|
231
|
-
active = false;
|
|
232
|
-
};
|
|
233
|
-
}
|
|
260
|
+
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? {}), callback)
|
|
234
261
|
};
|
|
235
262
|
}
|
|
236
263
|
return {
|