serializable-bptree 8.2.0 → 8.3.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.
@@ -1696,6 +1696,39 @@ var BPTreeTransaction = class _BPTreeTransaction {
1696
1696
  }
1697
1697
  return true;
1698
1698
  }
1699
+ /**
1700
+ * Inserts a key-value pair into an already-cloned leaf node in-place.
1701
+ * Unlike _insertAtLeaf, this does NOT clone or update the node via MVCC.
1702
+ * Used by batchInsert to batch multiple insertions with a single clone/update.
1703
+ * @returns true if the leaf was modified, false if the key already exists.
1704
+ */
1705
+ _insertValueIntoLeaf(leaf, key, value) {
1706
+ if (leaf.values.length) {
1707
+ for (let i = 0, len = leaf.values.length; i < len; i++) {
1708
+ const nValue = leaf.values[i];
1709
+ if (this.comparator.isSame(value, nValue)) {
1710
+ if (leaf.keys[i].includes(key)) {
1711
+ return false;
1712
+ }
1713
+ leaf.keys[i].push(key);
1714
+ return true;
1715
+ } else if (this.comparator.isLower(value, nValue)) {
1716
+ leaf.values.splice(i, 0, value);
1717
+ leaf.keys.splice(i, 0, [key]);
1718
+ return true;
1719
+ } else if (i + 1 === leaf.values.length) {
1720
+ leaf.values.push(value);
1721
+ leaf.keys.push([key]);
1722
+ return true;
1723
+ }
1724
+ }
1725
+ } else {
1726
+ leaf.values = [value];
1727
+ leaf.keys = [[key]];
1728
+ return true;
1729
+ }
1730
+ return false;
1731
+ }
1699
1732
  _cloneNode(node) {
1700
1733
  return JSON.parse(JSON.stringify(node));
1701
1734
  }
@@ -2256,6 +2289,50 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2256
2289
  this._insertInParent(before, after.values[0], after);
2257
2290
  }
2258
2291
  }
2292
+ batchInsert(entries) {
2293
+ if (entries.length === 0) return;
2294
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
2295
+ let currentLeaf = null;
2296
+ let modified = false;
2297
+ for (const [key, value] of sorted) {
2298
+ const targetLeaf = this.insertableNode(value);
2299
+ if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
2300
+ } else {
2301
+ if (currentLeaf !== null && modified) {
2302
+ this._updateNode(currentLeaf);
2303
+ }
2304
+ currentLeaf = this._cloneNode(targetLeaf);
2305
+ modified = false;
2306
+ }
2307
+ const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
2308
+ modified = modified || changed;
2309
+ if (currentLeaf.values.length === this.order) {
2310
+ this._updateNode(currentLeaf);
2311
+ let after = this._createNode(
2312
+ true,
2313
+ [],
2314
+ [],
2315
+ currentLeaf.parent,
2316
+ null,
2317
+ null
2318
+ );
2319
+ const mid = Math.ceil(this.order / 2) - 1;
2320
+ after = this._cloneNode(after);
2321
+ after.values = currentLeaf.values.slice(mid + 1);
2322
+ after.keys = currentLeaf.keys.slice(mid + 1);
2323
+ currentLeaf.values = currentLeaf.values.slice(0, mid + 1);
2324
+ currentLeaf.keys = currentLeaf.keys.slice(0, mid + 1);
2325
+ this._updateNode(currentLeaf);
2326
+ this._updateNode(after);
2327
+ this._insertInParent(currentLeaf, after.values[0], after);
2328
+ currentLeaf = null;
2329
+ modified = false;
2330
+ }
2331
+ }
2332
+ if (currentLeaf !== null && modified) {
2333
+ this._updateNode(currentLeaf);
2334
+ }
2335
+ }
2259
2336
  _deleteEntry(node, key) {
2260
2337
  if (!node.leaf) {
2261
2338
  let keyIndex = -1;
@@ -2611,6 +2688,14 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
2611
2688
  throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2612
2689
  }
2613
2690
  }
2691
+ batchInsert(entries) {
2692
+ const tx = this.createTransaction();
2693
+ tx.batchInsert(entries);
2694
+ const result = tx.commit();
2695
+ if (!result.success) {
2696
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2697
+ }
2698
+ }
2614
2699
  };
2615
2700
 
2616
2701
  // node_modules/ryoiki/dist/esm/index.mjs
@@ -3344,6 +3429,52 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3344
3429
  }
3345
3430
  });
3346
3431
  }
3432
+ async batchInsert(entries) {
3433
+ if (entries.length === 0) return;
3434
+ return this.writeLock(0, async () => {
3435
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
3436
+ let currentLeaf = null;
3437
+ let modified = false;
3438
+ for (const [key, value] of sorted) {
3439
+ const targetLeaf = await this.insertableNode(value);
3440
+ if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
3441
+ } else {
3442
+ if (currentLeaf !== null && modified) {
3443
+ await this._updateNode(currentLeaf);
3444
+ }
3445
+ currentLeaf = this._cloneNode(targetLeaf);
3446
+ modified = false;
3447
+ }
3448
+ const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
3449
+ modified = modified || changed;
3450
+ if (currentLeaf.values.length === this.order) {
3451
+ await this._updateNode(currentLeaf);
3452
+ let after = await this._createNode(
3453
+ true,
3454
+ [],
3455
+ [],
3456
+ currentLeaf.parent,
3457
+ null,
3458
+ null
3459
+ );
3460
+ const mid = Math.ceil(this.order / 2) - 1;
3461
+ after = this._cloneNode(after);
3462
+ after.values = currentLeaf.values.slice(mid + 1);
3463
+ after.keys = currentLeaf.keys.slice(mid + 1);
3464
+ currentLeaf.values = currentLeaf.values.slice(0, mid + 1);
3465
+ currentLeaf.keys = currentLeaf.keys.slice(0, mid + 1);
3466
+ await this._updateNode(currentLeaf);
3467
+ await this._updateNode(after);
3468
+ await this._insertInParent(currentLeaf, after.values[0], after);
3469
+ currentLeaf = null;
3470
+ modified = false;
3471
+ }
3472
+ }
3473
+ if (currentLeaf !== null && modified) {
3474
+ await this._updateNode(currentLeaf);
3475
+ }
3476
+ });
3477
+ }
3347
3478
  async _deleteEntry(node, key) {
3348
3479
  if (!node.leaf) {
3349
3480
  let keyIndex = -1;
@@ -3705,6 +3836,16 @@ var BPTreeAsync = class extends BPTreeAsyncTransaction {
3705
3836
  }
3706
3837
  });
3707
3838
  }
3839
+ async batchInsert(entries) {
3840
+ return this.writeLock(1, async () => {
3841
+ const tx = await this.createTransaction();
3842
+ await tx.batchInsert(entries);
3843
+ const result = await tx.commit();
3844
+ if (!result.success) {
3845
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
3846
+ }
3847
+ });
3848
+ }
3708
3849
  };
3709
3850
 
3710
3851
  // src/base/SerializeStrategy.ts
@@ -1660,6 +1660,39 @@ var BPTreeTransaction = class _BPTreeTransaction {
1660
1660
  }
1661
1661
  return true;
1662
1662
  }
1663
+ /**
1664
+ * Inserts a key-value pair into an already-cloned leaf node in-place.
1665
+ * Unlike _insertAtLeaf, this does NOT clone or update the node via MVCC.
1666
+ * Used by batchInsert to batch multiple insertions with a single clone/update.
1667
+ * @returns true if the leaf was modified, false if the key already exists.
1668
+ */
1669
+ _insertValueIntoLeaf(leaf, key, value) {
1670
+ if (leaf.values.length) {
1671
+ for (let i = 0, len = leaf.values.length; i < len; i++) {
1672
+ const nValue = leaf.values[i];
1673
+ if (this.comparator.isSame(value, nValue)) {
1674
+ if (leaf.keys[i].includes(key)) {
1675
+ return false;
1676
+ }
1677
+ leaf.keys[i].push(key);
1678
+ return true;
1679
+ } else if (this.comparator.isLower(value, nValue)) {
1680
+ leaf.values.splice(i, 0, value);
1681
+ leaf.keys.splice(i, 0, [key]);
1682
+ return true;
1683
+ } else if (i + 1 === leaf.values.length) {
1684
+ leaf.values.push(value);
1685
+ leaf.keys.push([key]);
1686
+ return true;
1687
+ }
1688
+ }
1689
+ } else {
1690
+ leaf.values = [value];
1691
+ leaf.keys = [[key]];
1692
+ return true;
1693
+ }
1694
+ return false;
1695
+ }
1663
1696
  _cloneNode(node) {
1664
1697
  return JSON.parse(JSON.stringify(node));
1665
1698
  }
@@ -2220,6 +2253,50 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2220
2253
  this._insertInParent(before, after.values[0], after);
2221
2254
  }
2222
2255
  }
2256
+ batchInsert(entries) {
2257
+ if (entries.length === 0) return;
2258
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
2259
+ let currentLeaf = null;
2260
+ let modified = false;
2261
+ for (const [key, value] of sorted) {
2262
+ const targetLeaf = this.insertableNode(value);
2263
+ if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
2264
+ } else {
2265
+ if (currentLeaf !== null && modified) {
2266
+ this._updateNode(currentLeaf);
2267
+ }
2268
+ currentLeaf = this._cloneNode(targetLeaf);
2269
+ modified = false;
2270
+ }
2271
+ const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
2272
+ modified = modified || changed;
2273
+ if (currentLeaf.values.length === this.order) {
2274
+ this._updateNode(currentLeaf);
2275
+ let after = this._createNode(
2276
+ true,
2277
+ [],
2278
+ [],
2279
+ currentLeaf.parent,
2280
+ null,
2281
+ null
2282
+ );
2283
+ const mid = Math.ceil(this.order / 2) - 1;
2284
+ after = this._cloneNode(after);
2285
+ after.values = currentLeaf.values.slice(mid + 1);
2286
+ after.keys = currentLeaf.keys.slice(mid + 1);
2287
+ currentLeaf.values = currentLeaf.values.slice(0, mid + 1);
2288
+ currentLeaf.keys = currentLeaf.keys.slice(0, mid + 1);
2289
+ this._updateNode(currentLeaf);
2290
+ this._updateNode(after);
2291
+ this._insertInParent(currentLeaf, after.values[0], after);
2292
+ currentLeaf = null;
2293
+ modified = false;
2294
+ }
2295
+ }
2296
+ if (currentLeaf !== null && modified) {
2297
+ this._updateNode(currentLeaf);
2298
+ }
2299
+ }
2223
2300
  _deleteEntry(node, key) {
2224
2301
  if (!node.leaf) {
2225
2302
  let keyIndex = -1;
@@ -2575,6 +2652,14 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
2575
2652
  throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2576
2653
  }
2577
2654
  }
2655
+ batchInsert(entries) {
2656
+ const tx = this.createTransaction();
2657
+ tx.batchInsert(entries);
2658
+ const result = tx.commit();
2659
+ if (!result.success) {
2660
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
2661
+ }
2662
+ }
2578
2663
  };
2579
2664
 
2580
2665
  // node_modules/ryoiki/dist/esm/index.mjs
@@ -3308,6 +3393,52 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3308
3393
  }
3309
3394
  });
3310
3395
  }
3396
+ async batchInsert(entries) {
3397
+ if (entries.length === 0) return;
3398
+ return this.writeLock(0, async () => {
3399
+ const sorted = [...entries].sort((a, b) => this.comparator.asc(a[1], b[1]));
3400
+ let currentLeaf = null;
3401
+ let modified = false;
3402
+ for (const [key, value] of sorted) {
3403
+ const targetLeaf = await this.insertableNode(value);
3404
+ if (currentLeaf !== null && currentLeaf.id === targetLeaf.id) {
3405
+ } else {
3406
+ if (currentLeaf !== null && modified) {
3407
+ await this._updateNode(currentLeaf);
3408
+ }
3409
+ currentLeaf = this._cloneNode(targetLeaf);
3410
+ modified = false;
3411
+ }
3412
+ const changed = this._insertValueIntoLeaf(currentLeaf, key, value);
3413
+ modified = modified || changed;
3414
+ if (currentLeaf.values.length === this.order) {
3415
+ await this._updateNode(currentLeaf);
3416
+ let after = await this._createNode(
3417
+ true,
3418
+ [],
3419
+ [],
3420
+ currentLeaf.parent,
3421
+ null,
3422
+ null
3423
+ );
3424
+ const mid = Math.ceil(this.order / 2) - 1;
3425
+ after = this._cloneNode(after);
3426
+ after.values = currentLeaf.values.slice(mid + 1);
3427
+ after.keys = currentLeaf.keys.slice(mid + 1);
3428
+ currentLeaf.values = currentLeaf.values.slice(0, mid + 1);
3429
+ currentLeaf.keys = currentLeaf.keys.slice(0, mid + 1);
3430
+ await this._updateNode(currentLeaf);
3431
+ await this._updateNode(after);
3432
+ await this._insertInParent(currentLeaf, after.values[0], after);
3433
+ currentLeaf = null;
3434
+ modified = false;
3435
+ }
3436
+ }
3437
+ if (currentLeaf !== null && modified) {
3438
+ await this._updateNode(currentLeaf);
3439
+ }
3440
+ });
3441
+ }
3311
3442
  async _deleteEntry(node, key) {
3312
3443
  if (!node.leaf) {
3313
3444
  let keyIndex = -1;
@@ -3669,6 +3800,16 @@ var BPTreeAsync = class extends BPTreeAsyncTransaction {
3669
3800
  }
3670
3801
  });
3671
3802
  }
3803
+ async batchInsert(entries) {
3804
+ return this.writeLock(1, async () => {
3805
+ const tx = await this.createTransaction();
3806
+ await tx.batchInsert(entries);
3807
+ const result = await tx.commit();
3808
+ if (!result.success) {
3809
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
3810
+ }
3811
+ });
3812
+ }
3672
3813
  };
3673
3814
 
3674
3815
  // src/base/SerializeStrategy.ts
@@ -11,4 +11,5 @@ export declare class BPTreeAsync<K, V> extends BPTreeAsyncTransaction<K, V> {
11
11
  createTransaction(): Promise<BPTreeAsyncTransaction<K, V>>;
12
12
  insert(key: K, value: V): Promise<void>;
13
13
  delete(key: K, value?: V): Promise<void>;
14
+ batchInsert(entries: [K, V][]): Promise<void>;
14
15
  }
@@ -11,4 +11,5 @@ export declare class BPTreeSync<K, V> extends BPTreeSyncTransaction<K, V> {
11
11
  createTransaction(): BPTreeSyncTransaction<K, V>;
12
12
  insert(key: K, value: V): void;
13
13
  delete(key: K, value?: V): void;
14
+ batchInsert(entries: [K, V][]): void;
14
15
  }
@@ -80,6 +80,13 @@ export declare abstract class BPTreeTransaction<K, V> {
80
80
  * @returns Returns true if the value satisfies the condition.
81
81
  */
82
82
  verify(nodeValue: V, condition: BPTreeCondition<V>): boolean;
83
+ /**
84
+ * Inserts a key-value pair into an already-cloned leaf node in-place.
85
+ * Unlike _insertAtLeaf, this does NOT clone or update the node via MVCC.
86
+ * Used by batchInsert to batch multiple insertions with a single clone/update.
87
+ * @returns true if the leaf was modified, false if the key already exists.
88
+ */
89
+ protected _insertValueIntoLeaf(leaf: BPTreeLeafNode<K, V>, key: K, value: V): boolean;
83
90
  protected _cloneNode<T extends BPTreeUnknownNode<K, V>>(node: T): T;
84
91
  /**
85
92
  * Selects the best driver key from a condition object.
@@ -152,6 +159,13 @@ export declare abstract class BPTreeTransaction<K, V> {
152
159
  * @param value The value of the pair.
153
160
  */
154
161
  abstract insert(key: K, value: V): Deferred<void>;
162
+ /**
163
+ * Inserts multiple key-value pairs into the tree in a single batch operation.
164
+ * Entries are sorted by value before insertion to optimize tree traversal.
165
+ * This is more efficient than calling insert() multiple times.
166
+ * @param entries Array of [key, value] pairs to insert.
167
+ */
168
+ abstract batchInsert(entries: [K, V][]): Deferred<void>;
155
169
  /**
156
170
  * Deletes the pair that matches the key and value.
157
171
  * @param key The key of the pair. This key must be unique.
@@ -42,6 +42,7 @@ export declare class BPTreeAsyncTransaction<K, V> extends BPTreeTransaction<K, V
42
42
  keys(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): Promise<Set<K>>;
43
43
  where(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): Promise<BPTreePair<K, V>>;
44
44
  insert(key: K, value: V): Promise<void>;
45
+ batchInsert(entries: [K, V][]): Promise<void>;
45
46
  protected _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>): Promise<BPTreeUnknownNode<K, V>>;
46
47
  delete(key: K, value?: V): Promise<void>;
47
48
  getHeadData(): Promise<SerializableData>;
@@ -39,6 +39,7 @@ export declare class BPTreeSyncTransaction<K, V> extends BPTreeTransaction<K, V>
39
39
  keys(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): Set<K>;
40
40
  where(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): BPTreePair<K, V>;
41
41
  insert(key: K, value: V): void;
42
+ batchInsert(entries: [K, V][]): void;
42
43
  protected _deleteEntry(node: BPTreeUnknownNode<K, V>, key: BPTreeNodeKey<K>): BPTreeUnknownNode<K, V>;
43
44
  delete(key: K, value?: V): void;
44
45
  getHeadData(): SerializableData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serializable-bptree",
3
- "version": "8.2.0",
3
+ "version": "8.3.1",
4
4
  "description": "Store the B+tree flexibly, not only in-memory.",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "main": "./dist/cjs/index.cjs",