@wiscale/velesdb-sdk 1.12.0 → 1.13.1

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 CHANGED
@@ -2,9 +2,17 @@
2
2
 
3
3
  Official TypeScript SDK for [VelesDB](https://github.com/cyberlife-coder/VelesDB) -- the local-first vector database for AI and RAG. Sub-millisecond semantic search in Browser and Node.js.
4
4
 
5
- **v1.12.0** | Node.js >= 18 | Browser (WASM) | MIT License
5
+ **v1.13.0** | Node.js >= 18 | Browser (WASM) | MIT License
6
6
 
7
- ## What's New in v1.12.0
7
+ ## What's New in v1.13.0
8
+
9
+ - **WASM VelesQL executor** -- full browser-side VelesQL execution: SELECT/INSERT/UPDATE/DELETE/DDL + aggregations (COUNT/SUM/AVG/MIN/MAX) + GROUP BY/HAVING/UNION/INTERSECT/EXCEPT/JOIN/FUSION/MATCH 1-2 hops + NOT De Morgan distribution
10
+ - **TS SDK coverage raised to 94%** -- 423 tests, per-file thresholds codified in `vitest.config.ts`
11
+ - **SIFT1M standardized ANN benchmark** -- fvecs/ivecs loader + Criterion ef sweep on the INRIA TEXMEX dataset, feature-gated behind `--features bench-sift1m`
12
+ - **Security hardening** -- `validateCollectionName()` helper on TS SDK prevents VelesQL injection in `trainPq`
13
+ - **API consistency** -- `streamInsert` now serializes `payload: null` explicitly (matches `streamUpsertPoints`)
14
+
15
+ ### Previous (v1.12.0)
8
16
 
9
17
  - **Cross-collection MATCH queries** -- `@collection` annotation on MATCH node patterns enables cross-collection graph queries
10
18
  - **MATCH via `/query` endpoint** -- MATCH queries can now be executed via `Database::execute_query`
@@ -74,15 +82,15 @@ await db.createCollection('documents', {
74
82
  metric: 'cosine'
75
83
  });
76
84
 
77
- // 3. Insert vectors with metadata
78
- await db.insert('documents', {
85
+ // 3. Upsert vectors with metadata
86
+ await db.upsert('documents', {
79
87
  id: 'doc-1',
80
88
  vector: new Float32Array(768).fill(0.1),
81
89
  payload: { title: 'Hello World', category: 'greeting' }
82
90
  });
83
91
 
84
- // 4. Batch insert for better throughput
85
- await db.insertBatch('documents', [
92
+ // 4. Batch upsert for better throughput
93
+ await db.upsertBatch('documents', [
86
94
  { id: 'doc-2', vector: new Float32Array(768).fill(0.2), payload: { title: 'Second doc' } },
87
95
  { id: 'doc-3', vector: new Float32Array(768).fill(0.3), payload: { title: 'Third doc' } },
88
96
  ]);
@@ -113,12 +121,17 @@ await db.init();
113
121
 
114
122
  // Same API as WASM backend
115
123
  await db.createCollection('products', { dimension: 1536 });
116
- await db.insert('products', { id: 1, vector: embedding });
124
+ await db.upsert('products', { id: 1, vector: embedding });
117
125
  const results = await db.search('products', queryVector, { k: 10 });
118
126
  ```
119
127
 
120
128
  > **REST backend note:** Document IDs must be numeric integers in the range `0..Number.MAX_SAFE_INTEGER`. String IDs are only supported with the WASM backend.
121
129
 
130
+ > **Versioned routes:** The REST backend uses `/v1/` as the canonical route prefix
131
+ > (e.g. `POST /v1/collections/{name}/search`). Legacy routes without the prefix
132
+ > are accepted for backward compatibility but are deprecated and will be removed
133
+ > in a future major version. Always target `/v1/` in custom HTTP clients.
134
+
122
135
  ## API Reference
123
136
 
124
137
  ### Client
@@ -209,14 +222,14 @@ List all collections. Returns an array of `Collection` objects.
209
222
 
210
223
  ---
211
224
 
212
- ### Insert and Retrieve
225
+ ### Upsert and Retrieve
213
226
 
214
- #### `db.insert(collection, document)`
227
+ #### `db.upsert(collection, document)`
215
228
 
216
- Insert a single vector document.
229
+ Upsert (insert or replace) a single vector document.
217
230
 
218
231
  ```typescript
219
- await db.insert('docs', {
232
+ await db.upsert('docs', {
220
233
  id: 'unique-id',
221
234
  vector: [0.1, 0.2, 0.3], // number[] or Float32Array
222
235
  payload: { key: 'value' }, // optional metadata
@@ -224,12 +237,12 @@ await db.insert('docs', {
224
237
  });
225
238
  ```
226
239
 
227
- #### `db.insertBatch(collection, documents)`
240
+ #### `db.upsertBatch(collection, documents)`
228
241
 
229
- Insert multiple vectors in a single call. More efficient than repeated `insert()`.
242
+ Upsert multiple vectors in a single call. More efficient than repeated `upsert()`.
230
243
 
231
244
  ```typescript
232
- await db.insertBatch('docs', [
245
+ await db.upsertBatch('docs', [
233
246
  { id: 'a', vector: vecA, payload: { title: 'First' } },
234
247
  { id: 'b', vector: vecB, payload: { title: 'Second' } },
235
248
  ]);
@@ -297,6 +310,40 @@ const hits = await db.searchIds('docs', queryVector, { k: 100 });
297
310
  // Returns: Array<{ id: number, score: number }>
298
311
  ```
299
312
 
313
+ #### `db.scroll(collection, request)`
314
+
315
+ Iterate over all points in a collection in stable, paginated batches without a
316
+ query vector. Useful for export pipelines, re-embedding, and full-collection
317
+ inspection. Pagination is cursor-based — pass the `nextCursor` from each
318
+ `ScrollResponse` until it is `null`.
319
+
320
+ ```typescript
321
+ import { ScrollRequest, ScrollResponse } from '@wiscale/velesdb-sdk';
322
+
323
+ let cursor: string | number | null = null;
324
+ let hasMore = true;
325
+ while (hasMore) {
326
+ const req: ScrollRequest = { batchSize: 100 };
327
+ if (cursor !== null) {
328
+ req.cursor = cursor;
329
+ }
330
+
331
+ const page: ScrollResponse = await db.scroll('docs', req);
332
+
333
+ for (const point of page.points) {
334
+ console.log(point.id, point.payload);
335
+ }
336
+ cursor = page.nextCursor;
337
+ hasMore = cursor !== null;
338
+ }
339
+ ```
340
+
341
+ | Field (`ScrollRequest`) | Type | Default | Description |
342
+ |-------------------------|------|---------|-------------|
343
+ | `cursor` | `string \| number` | - | Cursor from previous `ScrollResponse.nextCursor` |
344
+ | `batchSize` | `number` | `100` | Points per page |
345
+ | `filter` | `object` | - | Optional payload filter expression |
346
+
300
347
  #### `db.textSearch(collection, query, options?)`
301
348
 
302
349
  Full-text search using BM25 scoring.
@@ -325,7 +372,7 @@ Multi-query fusion search for RAG pipelines using Multiple Query Generation (MQG
325
372
  | Option | Type | Default | Description |
326
373
  |--------|------|---------|-------------|
327
374
  | `k` | `number` | `10` | Number of results |
328
- | `fusion` | `'rrf' \| 'average' \| 'maximum' \| 'weighted'` | `'rrf'` | Fusion strategy |
375
+ | `fusion` | `'rrf' \| 'average' \| 'maximum' \| 'weighted' \| 'relative_score'` | `'rrf'` | Fusion strategy |
329
376
  | `fusionParams` | `object` | `{ k: 60 }` | Strategy-specific parameters |
330
377
  | `filter` | `object` | - | Payload filter expression |
331
378
 
@@ -343,9 +390,16 @@ const results = await db.multiQuerySearch('docs', [emb1, emb2], {
343
390
  fusion: 'weighted',
344
391
  fusionParams: { avgWeight: 0.6, maxWeight: 0.3, hitWeight: 0.1 }
345
392
  });
393
+
394
+ // Relative Score Fusion — linear blend of dense and sparse scores
395
+ const results = await db.multiQuerySearch('docs', [emb1, emb2], {
396
+ k: 10,
397
+ fusion: 'relative_score',
398
+ fusionParams: { denseWeight: 0.7, sparseWeight: 0.3 }
399
+ });
346
400
  ```
347
401
 
348
- > **Note:** WASM supports `rrf`, `average`, `maximum`. The `weighted` strategy is REST-only.
402
+ > **Note:** WASM supports `rrf`, `average`, `maximum`. The `weighted` and `relative_score` strategies are REST-only.
349
403
 
350
404
  ---
351
405
 
@@ -756,7 +810,7 @@ import {
756
810
 
757
811
  ## Performance Tips
758
812
 
759
- 1. **Use `insertBatch()`** instead of repeated `insert()` calls
813
+ 1. **Use `upsertBatch()`** instead of repeated `upsert()` calls
760
814
  2. **Reuse `Float32Array`** buffers for query vectors when possible
761
815
  3. **Use WASM backend** for browser apps (zero network latency)
762
816
  4. **Use `searchIds()`** when you only need IDs and scores (skips payload transfer)