@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.
Files changed (4) hide show
  1. package/README.md +12 -12
  2. package/index.d.ts +49 -2
  3. package/index.js +95 -5
  4. 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), { k: 5 });
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, { k: 10 });
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
- k: 10, // Number of results
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, options)`
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, { k: 10, ef: 100 });
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, { k: 5 });
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
- * Accepts an array of items with id, vector, and optional metadata.
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<VectorItem>): Array<number>
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
- return this._native.set(items.map(convertVectorItem));
130
- }
131
-
132
- search(query, k, ef, filter, maxDistance) {
133
- return this._native.search(query, k, ef, filter, maxDistance);
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.24",
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.0",
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.24",
54
- "@omendb/omendb-darwin-arm64": "0.0.24",
55
- "@omendb/omendb-linux-x64-gnu": "0.0.24",
56
- "@omendb/omendb-linux-arm64-gnu": "0.0.24",
57
- "@omendb/omendb-win32-x64-msvc": "0.0.24"
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
  }