@omendb/omendb 0.0.24 → 0.0.25
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/README.md +12 -12
- package/index.d.ts +49 -2
- package/index.js +95 -5
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -31,11 +31,11 @@ db.set([
|
|
|
31
31
|
]);
|
|
32
32
|
|
|
33
33
|
// Search
|
|
34
|
-
const results = db.search(new Float32Array(384).fill(0.15),
|
|
34
|
+
const results = db.search(new Float32Array(384).fill(0.15), 5);
|
|
35
35
|
// [{ id: 'doc1', distance: 0.05, metadata: { title: 'Hello' } }, ...]
|
|
36
36
|
|
|
37
37
|
// Batch search (async, parallel)
|
|
38
|
-
const batchResults = await db.searchBatch(queries,
|
|
38
|
+
const batchResults = await db.searchBatch(queries, 10);
|
|
39
39
|
|
|
40
40
|
// Close when done (releases file locks)
|
|
41
41
|
db.close();
|
|
@@ -139,13 +139,13 @@ const deleted = db.deleteByFilter({
|
|
|
139
139
|
|
|
140
140
|
### Search
|
|
141
141
|
|
|
142
|
-
#### `db.search(query, options)`
|
|
142
|
+
#### `db.search(query, k, options?)`
|
|
143
143
|
|
|
144
144
|
Search for k nearest neighbors (sync).
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
|
-
const results = db.search(queryVector,
|
|
148
|
-
|
|
147
|
+
const results = db.search(queryVector, 10); // Basic
|
|
148
|
+
const results = db.search(queryVector, 10, {
|
|
149
149
|
ef: 200, // Search quality (higher = better recall)
|
|
150
150
|
filter: { category: "news" }, // Metadata filter
|
|
151
151
|
maxDistance: 0.5, // Distance threshold
|
|
@@ -153,12 +153,12 @@ const results = db.search(queryVector, {
|
|
|
153
153
|
// [{ id, distance, metadata }, ...]
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
#### `db.searchBatch(queries,
|
|
156
|
+
#### `db.searchBatch(queries, k, ef?)`
|
|
157
157
|
|
|
158
158
|
Batch search with parallel execution (async).
|
|
159
159
|
|
|
160
160
|
```typescript
|
|
161
|
-
const results = await db.searchBatch(queries,
|
|
161
|
+
const results = await db.searchBatch(queries, 10, 100);
|
|
162
162
|
// [[{ id, distance, metadata }, ...], ...]
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -224,7 +224,7 @@ Get or create a named collection.
|
|
|
224
224
|
```typescript
|
|
225
225
|
const users = db.collection("users");
|
|
226
226
|
users.set([...]);
|
|
227
|
-
users.search(query,
|
|
227
|
+
users.search(query, 5);
|
|
228
228
|
```
|
|
229
229
|
|
|
230
230
|
#### `db.collections()`
|
|
@@ -373,11 +373,11 @@ const merged = db.mergeFrom(otherDb);
|
|
|
373
373
|
|
|
374
374
|
**10K vectors, 128D, M=16, ef=100. Measured 2026-01-20 (Apple M3 Max):**
|
|
375
375
|
|
|
376
|
-
| Metric | Value
|
|
377
|
-
| ---------- |
|
|
378
|
-
| Search QPS | 11,542
|
|
376
|
+
| Metric | Value |
|
|
377
|
+
| ---------- | ------------ |
|
|
378
|
+
| Search QPS | 11,542 |
|
|
379
379
|
| Build | 30,826 vec/s |
|
|
380
|
-
| Recall@10 | 89.7%
|
|
380
|
+
| Recall@10 | 89.7% |
|
|
381
381
|
|
|
382
382
|
## License
|
|
383
383
|
|
package/index.d.ts
CHANGED
|
@@ -4,9 +4,14 @@ export declare class VectorDatabase {
|
|
|
4
4
|
/**
|
|
5
5
|
* Insert or update vectors.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Works for both single-vector and multi-vector stores:
|
|
8
|
+
* - Single-vector: items have `vector` field
|
|
9
|
+
* - Multi-vector: items have `vectors` field (array of vectors)
|
|
10
|
+
*
|
|
11
|
+
* @param items - Array of {id, vector, metadata?} or {id, vectors, metadata?}
|
|
12
|
+
* @returns Array of internal indices
|
|
8
13
|
*/
|
|
9
|
-
set(items: Array<
|
|
14
|
+
set(items: Array<SetItem>): Array<number>
|
|
10
15
|
/**
|
|
11
16
|
* Search for k nearest neighbors.
|
|
12
17
|
*
|
|
@@ -18,6 +23,18 @@ export declare class VectorDatabase {
|
|
|
18
23
|
* @returns Array of {id, distance, metadata}
|
|
19
24
|
*/
|
|
20
25
|
search(query: Array<number> | Float32Array, k: number, ef?: number | undefined | null, filter?: Record<string, unknown> | undefined, maxDistance?: number | undefined | null): Array<SearchResult>
|
|
26
|
+
/**
|
|
27
|
+
* Search multi-vector store with query tokens.
|
|
28
|
+
*
|
|
29
|
+
* Internal method used by unified search() for multi-vector stores.
|
|
30
|
+
*
|
|
31
|
+
* @param query - Query tokens (number[][] or Float32Array[])
|
|
32
|
+
* @param k - Number of results to return
|
|
33
|
+
* @param rerank - Enable MaxSim reranking for better quality (default: true)
|
|
34
|
+
* @param rerankFactor - Fetch k*rerankFactor candidates before reranking (default: 32)
|
|
35
|
+
* @returns Array of {id, distance, metadata}
|
|
36
|
+
*/
|
|
37
|
+
searchMulti(query: Array<Array<number>> | Array<Float32Array>, k: number, rerank?: boolean | undefined | null, rerankFactor?: number | undefined | null): Array<SearchResult>
|
|
21
38
|
/**
|
|
22
39
|
* Batch search with parallel execution (async).
|
|
23
40
|
*
|
|
@@ -83,6 +100,8 @@ export declare class VectorDatabase {
|
|
|
83
100
|
get length(): number
|
|
84
101
|
/** Get vector dimensions of this database. */
|
|
85
102
|
get dimensions(): number
|
|
103
|
+
/** Check if this is a multi-vector store. */
|
|
104
|
+
get isMultiVector(): boolean
|
|
86
105
|
/** Check if database is empty. */
|
|
87
106
|
isEmpty(): boolean
|
|
88
107
|
/** Get database statistics. */
|
|
@@ -234,6 +253,14 @@ export interface HybridSearchResult {
|
|
|
234
253
|
semanticScore?: number
|
|
235
254
|
}
|
|
236
255
|
|
|
256
|
+
export interface MultiVectorItem {
|
|
257
|
+
id: string
|
|
258
|
+
/** Multi-vector data as array of Float32Arrays */
|
|
259
|
+
vectors: Float32Array[]
|
|
260
|
+
/** Optional metadata */
|
|
261
|
+
metadata?: Record<string, unknown> | undefined
|
|
262
|
+
}
|
|
263
|
+
|
|
237
264
|
/**
|
|
238
265
|
* Open or create a vector database.
|
|
239
266
|
*
|
|
@@ -311,6 +338,14 @@ export interface OpenOptions {
|
|
|
311
338
|
oversample?: number
|
|
312
339
|
/** Distance metric: "l2"/"euclidean" (default), "cosine", "dot"/"ip" */
|
|
313
340
|
metric?: string
|
|
341
|
+
/**
|
|
342
|
+
* Enable multi-vector mode for ColBERT-style retrieval
|
|
343
|
+
* - true: Enable with default config (repetitions=8, partition_bits=4, dProj=16)
|
|
344
|
+
* - { repetitions?, partitionBits?, seed?, dProj? }: Custom config
|
|
345
|
+
* - dProj: Dimension projection (16 = 8x smaller FDE, null = full token dim)
|
|
346
|
+
* - false/null: Disabled (default, single-vector mode)
|
|
347
|
+
*/
|
|
348
|
+
multiVector?: boolean | { repetitions?: number; partitionBits?: number; seed?: number; dProj?: number | null } | null | undefined
|
|
314
349
|
}
|
|
315
350
|
|
|
316
351
|
export interface SearchResult {
|
|
@@ -320,6 +355,18 @@ export interface SearchResult {
|
|
|
320
355
|
metadata: Record<string, unknown>
|
|
321
356
|
}
|
|
322
357
|
|
|
358
|
+
export interface SetItem {
|
|
359
|
+
id: string
|
|
360
|
+
/** Single vector data (for regular stores) */
|
|
361
|
+
vector?: Float32Array
|
|
362
|
+
/** Multi-vector data (for multi-vector stores) */
|
|
363
|
+
vectors?: Float32Array[] | undefined
|
|
364
|
+
/** Optional metadata */
|
|
365
|
+
metadata?: Record<string, unknown> | undefined
|
|
366
|
+
/** Optional document text (stored in metadata.document) */
|
|
367
|
+
document?: string
|
|
368
|
+
}
|
|
369
|
+
|
|
323
370
|
export interface StatsResult {
|
|
324
371
|
dimensions: number
|
|
325
372
|
count: number
|
package/index.js
CHANGED
|
@@ -111,12 +111,41 @@ function toFloat32Array(arr) {
|
|
|
111
111
|
|
|
112
112
|
// Convert VectorItem to use Float32Array
|
|
113
113
|
function convertVectorItem(item) {
|
|
114
|
+
if (item.vector === undefined || item.vector === null) {
|
|
115
|
+
if (Array.isArray(item.vectors)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Item '${item.id}' has 'vectors' field but store is single-vector. Use multiVector: true when opening the database.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
throw new Error(`Item '${item.id}' missing required 'vector' field`);
|
|
121
|
+
}
|
|
114
122
|
return {
|
|
115
123
|
...item,
|
|
116
124
|
vector: toFloat32Array(item.vector),
|
|
117
125
|
};
|
|
118
126
|
}
|
|
119
127
|
|
|
128
|
+
// Convert MultiVectorItem to use Float32Arrays
|
|
129
|
+
function convertMultiVectorItem(item) {
|
|
130
|
+
if (!Array.isArray(item.vectors)) {
|
|
131
|
+
if (item.vector !== undefined) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Item '${item.id}' has 'vector' field but store is multi-vector. Use 'vectors' field (array of vectors) instead.`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
throw new Error(`Item '${item.id}' missing required 'vectors' field`);
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
...item,
|
|
140
|
+
vectors: item.vectors.map(toFloat32Array),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check if items contain multi-vector data (vectors field must be an array)
|
|
145
|
+
function isMultiVectorItem(item) {
|
|
146
|
+
return Array.isArray(item.vectors);
|
|
147
|
+
}
|
|
148
|
+
|
|
120
149
|
// Wrap VectorDatabase to handle array conversion
|
|
121
150
|
const NativeVectorDatabase = nativeBinding.VectorDatabase;
|
|
122
151
|
|
|
@@ -125,12 +154,69 @@ class VectorDatabase {
|
|
|
125
154
|
this._native = nativeDb;
|
|
126
155
|
}
|
|
127
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Insert or update vectors.
|
|
159
|
+
*
|
|
160
|
+
* Works for both single-vector and multi-vector stores:
|
|
161
|
+
* - Single-vector: items have `vector` field
|
|
162
|
+
* - Multi-vector: items have `vectors` field (array of vectors)
|
|
163
|
+
*
|
|
164
|
+
* @param {Array<{id: string, vector?: Float32Array|number[], vectors?: Float32Array[]|number[][], metadata?: object}>} items
|
|
165
|
+
* @returns {number[]} Array of internal indices
|
|
166
|
+
*/
|
|
128
167
|
set(items) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
168
|
+
if (!Array.isArray(items)) {
|
|
169
|
+
throw new Error("set() requires an array of items");
|
|
170
|
+
}
|
|
171
|
+
if (items.length === 0) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
// Unified set() handles both single and multi-vector via native set()
|
|
175
|
+
return this._native.set(items.map(this._native.isMultiVector ? convertMultiVectorItem : convertVectorItem));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Search for k nearest neighbors.
|
|
180
|
+
*
|
|
181
|
+
* Works for both single-vector and multi-vector stores:
|
|
182
|
+
* - Single-vector: query is number[] or Float32Array
|
|
183
|
+
* - Multi-vector: query is number[][] or Float32Array[]
|
|
184
|
+
*
|
|
185
|
+
* @param {number[]|Float32Array|number[][]|Float32Array[]} query - Query vector(s)
|
|
186
|
+
* @param {number} k - Number of results to return
|
|
187
|
+
* @param {object} [options] - Search options
|
|
188
|
+
* @param {number} [options.ef] - Search width override (single-vector only)
|
|
189
|
+
* @param {object} [options.filter] - Metadata filter (single-vector only)
|
|
190
|
+
* @param {number} [options.maxDistance] - Max distance threshold (single-vector only)
|
|
191
|
+
* @param {boolean} [options.rerank] - Enable MaxSim reranking (multi-vector, default: true)
|
|
192
|
+
* @param {number} [options.rerankFactor] - Rerank candidate multiplier (multi-vector, default: 4)
|
|
193
|
+
* @returns {Array<{id: string, distance: number, metadata: object}>}
|
|
194
|
+
*/
|
|
195
|
+
search(query, k, options) {
|
|
196
|
+
if (this._native.isMultiVector) {
|
|
197
|
+
// Multi-vector store
|
|
198
|
+
const rerank = options?.rerank;
|
|
199
|
+
const rerankFactor = options?.rerankFactor;
|
|
200
|
+
return this._native.searchMulti(query, k, rerank, rerankFactor);
|
|
201
|
+
} else {
|
|
202
|
+
// Single-vector store - support both old positional args and new options object
|
|
203
|
+
if (typeof options === "object" && options !== null && !Array.isArray(options)) {
|
|
204
|
+
// New options object style
|
|
205
|
+
return this._native.search(
|
|
206
|
+
query,
|
|
207
|
+
k,
|
|
208
|
+
options.ef,
|
|
209
|
+
options.filter,
|
|
210
|
+
options.maxDistance,
|
|
211
|
+
);
|
|
212
|
+
} else {
|
|
213
|
+
// Legacy positional args: search(query, k, ef, filter, maxDistance)
|
|
214
|
+
const ef = options; // 3rd arg was ef in old API
|
|
215
|
+
const filter = arguments[3];
|
|
216
|
+
const maxDistance = arguments[4];
|
|
217
|
+
return this._native.search(query, k, ef, filter, maxDistance);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
134
220
|
}
|
|
135
221
|
|
|
136
222
|
searchBatch(queries, k, ef) {
|
|
@@ -165,6 +251,10 @@ class VectorDatabase {
|
|
|
165
251
|
return this._native.dimensions;
|
|
166
252
|
}
|
|
167
253
|
|
|
254
|
+
get isMultiVector() {
|
|
255
|
+
return this._native.isMultiVector;
|
|
256
|
+
}
|
|
257
|
+
|
|
168
258
|
isEmpty() {
|
|
169
259
|
return this._native.isEmpty();
|
|
170
260
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omendb/omendb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "Fast embedded vector database with HNSW + ACORN-1 filtered search",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"test": "vitest run"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@napi-rs/cli": "^3.5.
|
|
44
|
+
"@napi-rs/cli": "^3.5.1",
|
|
45
45
|
"vitest": "^2.1.0"
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"omendb.node"
|
|
51
51
|
],
|
|
52
52
|
"optionalDependencies": {
|
|
53
|
-
"@omendb/omendb-darwin-x64": "0.0.
|
|
54
|
-
"@omendb/omendb-darwin-arm64": "0.0.
|
|
55
|
-
"@omendb/omendb-linux-x64-gnu": "0.0.
|
|
56
|
-
"@omendb/omendb-linux-arm64-gnu": "0.0.
|
|
57
|
-
"@omendb/omendb-win32-x64-msvc": "0.0.
|
|
53
|
+
"@omendb/omendb-darwin-x64": "0.0.25",
|
|
54
|
+
"@omendb/omendb-darwin-arm64": "0.0.25",
|
|
55
|
+
"@omendb/omendb-linux-x64-gnu": "0.0.25",
|
|
56
|
+
"@omendb/omendb-linux-arm64-gnu": "0.0.25",
|
|
57
|
+
"@omendb/omendb-win32-x64-msvc": "0.0.25"
|
|
58
58
|
}
|
|
59
59
|
}
|