serializable-bptree 8.1.0 → 8.1.2

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
@@ -134,7 +134,7 @@ Explore the detailed guides and concepts of `serializable-bptree`:
134
134
  - [Best Practices](./docs/BEST_PRACTICES.md): Tips for bulk insertion and performance optimization.
135
135
  - [Duplicate Value Handling](./docs/DUPLICATE_VALUES.md): Strategies for managing large amounts of duplicate data.
136
136
  - [Concurrency & Synchronization](./docs/CONCURRENCY.md): Multi-instance usage and locking mechanisms.
137
- - [Query Optimization Guide](./docs/QUERY.md#performance--optimization): How to use `ChooseDriver`, `get()`, and `verify()` for complex queries.
137
+ - [Query Optimization Guide](./docs/QUERY.md#performance--optimization): How to use `ChooseDriver` and `keys()` for complex queries.
138
138
 
139
139
  ## Quick Example: Query Optimization
140
140
 
@@ -144,19 +144,20 @@ When you have multiple indexes (e.g., an index for `id` and another for `age`),
144
144
  const query = { id: { equal: 100 }, age: { gt: 20 } }
145
145
 
146
146
  // 1. Select the best index based on condition priority
147
- const driver = BPTreeSync.ChooseDriver([
147
+ const candidates = [
148
148
  { tree: idxId, condition: query.id },
149
149
  { tree: idxAge, condition: query.age }
150
- ])
150
+ ]
151
+ const driver = BPTreeSync.ChooseDriver(candidates)
152
+ const others = candidates.filter((c) => driver.tree !== c.tree)
151
153
 
152
154
  // 2. Execute query using the selected driver
153
- for (const [pk, val] of driver.tree.whereStream(driver.condition)) {
154
- // 3. Filter other conditions using get() and verify()
155
- const age = idxAge.get(pk)
156
- if (age !== undefined && idxAge.verify(age, query.age)) {
157
- console.log(`Match found! PK: ${pk}`)
158
- }
155
+ let keys = driver.tree.keys(driver.condition)
156
+ for (const { tree, condition } of others) {
157
+ keys = tree.keys(condition, keys)
159
158
  }
159
+
160
+ console.log('Found: ', keys)
160
161
  ```
161
162
 
162
163
  ## Migration
@@ -165,6 +166,4 @@ Instructions for migrating between major versions (e.g., v8.0.0, v6.0.0) can be
165
166
 
166
167
  ## LICENSE
167
168
 
168
- ## LICENSE
169
-
170
169
  MIT LICENSE
@@ -222,6 +222,45 @@ var MVCCTransaction = class {
222
222
  }
223
223
  return { success: true, created, updated, deleted };
224
224
  }
225
+ /**
226
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
227
+ * Root transactions call this after commit to reclaim memory.
228
+ */
229
+ _cleanupDeletedCache() {
230
+ if (this.deletedCache.size === 0 && this.versionIndex.size === 0) return;
231
+ let minActiveVersion = this.version;
232
+ if (this.activeTransactions.size > 0) {
233
+ for (const tx of this.activeTransactions) {
234
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
235
+ minActiveVersion = tx.snapshotVersion;
236
+ }
237
+ }
238
+ }
239
+ if (this.deletedCache.size > 0) {
240
+ for (const [key, cachedList] of this.deletedCache) {
241
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
242
+ if (remaining.length === 0) {
243
+ this.deletedCache.delete(key);
244
+ } else {
245
+ this.deletedCache.set(key, remaining);
246
+ }
247
+ }
248
+ }
249
+ if (this.versionIndex.size > 0) {
250
+ for (const [key, versions] of this.versionIndex) {
251
+ let latestInSnapshotIdx = -1;
252
+ for (let i = versions.length - 1; i >= 0; i--) {
253
+ if (versions[i].version <= minActiveVersion) {
254
+ latestInSnapshotIdx = i;
255
+ break;
256
+ }
257
+ }
258
+ if (latestInSnapshotIdx > 0) {
259
+ versions.splice(0, latestInSnapshotIdx);
260
+ }
261
+ }
262
+ }
263
+ }
225
264
  };
226
265
  var SyncMVCCStrategy = class extends MVCCStrategy {
227
266
  };
@@ -561,25 +600,6 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
561
600
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
562
601
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
563
602
  }
564
- _cleanupDeletedCache() {
565
- if (this.deletedCache.size === 0) return;
566
- let minActiveVersion = this.version;
567
- if (this.activeTransactions.size > 0) {
568
- for (const tx of this.activeTransactions) {
569
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
570
- minActiveVersion = tx.snapshotVersion;
571
- }
572
- }
573
- }
574
- for (const [key, cachedList] of this.deletedCache) {
575
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
576
- if (remaining.length === 0) {
577
- this.deletedCache.delete(key);
578
- } else {
579
- this.deletedCache.set(key, remaining);
580
- }
581
- }
582
- }
583
603
  };
584
604
  var AsyncMVCCStrategy = class extends MVCCStrategy {
585
605
  };
@@ -1188,25 +1208,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1188
1208
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1189
1209
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
1190
1210
  }
1191
- _cleanupDeletedCache() {
1192
- if (this.deletedCache.size === 0) return;
1193
- let minActiveVersion = this.version;
1194
- if (this.activeTransactions.size > 0) {
1195
- for (const tx of this.activeTransactions) {
1196
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
1197
- minActiveVersion = tx.snapshotVersion;
1198
- }
1199
- }
1200
- }
1201
- for (const [key, cachedList] of this.deletedCache) {
1202
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
1203
- if (remaining.length === 0) {
1204
- this.deletedCache.delete(key);
1205
- } else {
1206
- this.deletedCache.set(key, remaining);
1207
- }
1208
- }
1209
- }
1210
1211
  };
1211
1212
 
1212
1213
  // node_modules/cache-entanglement/dist/esm/index.mjs
@@ -1817,6 +1818,25 @@ var BPTreeTransaction = class _BPTreeTransaction {
1817
1818
  this._cachedRegexp.clear();
1818
1819
  this.nodes.clear();
1819
1820
  }
1821
+ _binarySearchValues(values, target, usePrimary = false, upperBound = false) {
1822
+ let low = 0;
1823
+ let high = values.length;
1824
+ let found = false;
1825
+ while (low < high) {
1826
+ const mid = low + high >>> 1;
1827
+ const cmp = usePrimary ? this.comparator.primaryAsc(target, values[mid]) : this.comparator.asc(target, values[mid]);
1828
+ if (cmp === 0) {
1829
+ found = true;
1830
+ if (upperBound) low = mid + 1;
1831
+ else high = mid;
1832
+ } else if (cmp < 0) {
1833
+ high = mid;
1834
+ } else {
1835
+ low = mid + 1;
1836
+ }
1837
+ }
1838
+ return { index: low, found };
1839
+ }
1820
1840
  };
1821
1841
 
1822
1842
  // src/transaction/BPTreeSyncTransaction.ts
@@ -1978,58 +1998,24 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
1978
1998
  insertableNode(value) {
1979
1999
  let node = this.getNode(this.rootId);
1980
2000
  while (!node.leaf) {
1981
- for (let i = 0, len = node.values.length; i < len; i++) {
1982
- const nValue = node.values[i];
1983
- const k = node.keys;
1984
- if (this.comparator.isSame(value, nValue)) {
1985
- node = this.getNode(k[i + 1]);
1986
- break;
1987
- } else if (this.comparator.isLower(value, nValue)) {
1988
- node = this.getNode(k[i]);
1989
- break;
1990
- } else if (i + 1 === node.values.length) {
1991
- node = this.getNode(k[i + 1]);
1992
- break;
1993
- }
1994
- }
2001
+ const { index } = this._binarySearchValues(node.values, value, false, true);
2002
+ node = this.getNode(node.keys[index]);
1995
2003
  }
1996
2004
  return node;
1997
2005
  }
1998
2006
  insertableNodeByPrimary(value) {
1999
2007
  let node = this.getNode(this.rootId);
2000
2008
  while (!node.leaf) {
2001
- for (let i = 0, len = node.values.length; i < len; i++) {
2002
- const nValue = node.values[i];
2003
- const k = node.keys;
2004
- if (this.comparator.isPrimarySame(value, nValue)) {
2005
- node = this.getNode(k[i]);
2006
- break;
2007
- } else if (this.comparator.isPrimaryLower(value, nValue)) {
2008
- node = this.getNode(k[i]);
2009
- break;
2010
- } else if (i + 1 === node.values.length) {
2011
- node = this.getNode(k[i + 1]);
2012
- break;
2013
- }
2014
- }
2009
+ const { index } = this._binarySearchValues(node.values, value, true, false);
2010
+ node = this.getNode(node.keys[index]);
2015
2011
  }
2016
2012
  return node;
2017
2013
  }
2018
2014
  insertableRightestNodeByPrimary(value) {
2019
2015
  let node = this.getNode(this.rootId);
2020
2016
  while (!node.leaf) {
2021
- for (let i = 0, len = node.values.length; i < len; i++) {
2022
- const nValue = node.values[i];
2023
- const k = node.keys;
2024
- if (this.comparator.isPrimaryLower(value, nValue)) {
2025
- node = this.getNode(k[i]);
2026
- break;
2027
- }
2028
- if (i + 1 === node.values.length) {
2029
- node = this.getNode(k[i + 1]);
2030
- break;
2031
- }
2032
- }
2017
+ const { index } = this._binarySearchValues(node.values, value, true, true);
2018
+ node = this.getNode(node.keys[index]);
2033
2019
  }
2034
2020
  return node;
2035
2021
  }
@@ -2162,12 +2148,11 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2162
2148
  }
2163
2149
  exists(key, value) {
2164
2150
  const node = this.insertableNode(value);
2165
- for (let i = 0, len = node.values.length; i < len; i++) {
2166
- if (this.comparator.isSame(value, node.values[i])) {
2167
- const keys = node.keys[i];
2168
- if (keys.includes(key)) {
2169
- return true;
2170
- }
2151
+ const { index, found } = this._binarySearchValues(node.values, value);
2152
+ if (found) {
2153
+ const keys = node.keys[index];
2154
+ if (keys.includes(key)) {
2155
+ return true;
2171
2156
  }
2172
2157
  }
2173
2158
  return false;
@@ -2803,58 +2788,24 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2803
2788
  async insertableNode(value) {
2804
2789
  let node = await this.getNode(this.rootId);
2805
2790
  while (!node.leaf) {
2806
- for (let i = 0, len = node.values.length; i < len; i++) {
2807
- const nValue = node.values[i];
2808
- const k = node.keys;
2809
- if (this.comparator.isSame(value, nValue)) {
2810
- node = await this.getNode(k[i + 1]);
2811
- break;
2812
- } else if (this.comparator.isLower(value, nValue)) {
2813
- node = await this.getNode(k[i]);
2814
- break;
2815
- } else if (i + 1 === node.values.length) {
2816
- node = await this.getNode(k[i + 1]);
2817
- break;
2818
- }
2819
- }
2791
+ const { index } = this._binarySearchValues(node.values, value, false, true);
2792
+ node = await this.getNode(node.keys[index]);
2820
2793
  }
2821
2794
  return node;
2822
2795
  }
2823
2796
  async insertableNodeByPrimary(value) {
2824
2797
  let node = await this.getNode(this.rootId);
2825
2798
  while (!node.leaf) {
2826
- for (let i = 0, len = node.values.length; i < len; i++) {
2827
- const nValue = node.values[i];
2828
- const k = node.keys;
2829
- if (this.comparator.isPrimarySame(value, nValue)) {
2830
- node = await this.getNode(k[i]);
2831
- break;
2832
- } else if (this.comparator.isPrimaryLower(value, nValue)) {
2833
- node = await this.getNode(k[i]);
2834
- break;
2835
- } else if (i + 1 === node.values.length) {
2836
- node = await this.getNode(k[i + 1]);
2837
- break;
2838
- }
2839
- }
2799
+ const { index } = this._binarySearchValues(node.values, value, true, false);
2800
+ node = await this.getNode(node.keys[index]);
2840
2801
  }
2841
2802
  return node;
2842
2803
  }
2843
2804
  async insertableRightestNodeByPrimary(value) {
2844
2805
  let node = await this.getNode(this.rootId);
2845
2806
  while (!node.leaf) {
2846
- for (let i = 0, len = node.values.length; i < len; i++) {
2847
- const nValue = node.values[i];
2848
- const k = node.keys;
2849
- if (this.comparator.isPrimaryLower(value, nValue)) {
2850
- node = await this.getNode(k[i]);
2851
- break;
2852
- }
2853
- if (i + 1 === node.values.length) {
2854
- node = await this.getNode(k[i + 1]);
2855
- break;
2856
- }
2857
- }
2807
+ const { index } = this._binarySearchValues(node.values, value, true, true);
2808
+ node = await this.getNode(node.keys[index]);
2858
2809
  }
2859
2810
  return node;
2860
2811
  }
@@ -2907,11 +2858,21 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2907
2858
  let node = startNode;
2908
2859
  let done = false;
2909
2860
  let hasMatched = false;
2861
+ let nextNodePromise = null;
2910
2862
  while (!done) {
2911
2863
  if (endNode && node.id === endNode.id) {
2912
2864
  done = true;
2913
2865
  break;
2914
2866
  }
2867
+ if (direction === 1) {
2868
+ if (node.next && !done) {
2869
+ nextNodePromise = this.getNode(node.next);
2870
+ }
2871
+ } else {
2872
+ if (node.prev && !done) {
2873
+ nextNodePromise = this.getNode(node.prev);
2874
+ }
2875
+ }
2915
2876
  const len = node.values.length;
2916
2877
  if (direction === 1) {
2917
2878
  for (let i = 0; i < len; i++) {
@@ -2944,19 +2905,15 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2944
2905
  }
2945
2906
  }
2946
2907
  }
2947
- if (done) break;
2948
- if (direction === 1) {
2949
- if (!node.next) {
2950
- done = true;
2951
- break;
2952
- }
2953
- node = await this.getNode(node.next);
2908
+ if (done) {
2909
+ if (nextNodePromise) await nextNodePromise;
2910
+ break;
2911
+ }
2912
+ if (nextNodePromise) {
2913
+ node = await nextNodePromise;
2914
+ nextNodePromise = null;
2954
2915
  } else {
2955
- if (!node.prev) {
2956
- done = true;
2957
- break;
2958
- }
2959
- node = await this.getNode(node.prev);
2916
+ done = true;
2960
2917
  }
2961
2918
  }
2962
2919
  }
@@ -2987,12 +2944,11 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2987
2944
  }
2988
2945
  async exists(key, value) {
2989
2946
  const node = await this.insertableNode(value);
2990
- for (let i = 0, len = node.values.length; i < len; i++) {
2991
- if (this.comparator.isSame(value, node.values[i])) {
2992
- const keys = node.keys[i];
2993
- if (keys.includes(key)) {
2994
- return true;
2995
- }
2947
+ const { index, found } = this._binarySearchValues(node.values, value);
2948
+ if (found) {
2949
+ const keys = node.keys[index];
2950
+ if (keys.includes(key)) {
2951
+ return true;
2996
2952
  }
2997
2953
  }
2998
2954
  return false;
@@ -186,6 +186,45 @@ var MVCCTransaction = class {
186
186
  }
187
187
  return { success: true, created, updated, deleted };
188
188
  }
189
+ /**
190
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
191
+ * Root transactions call this after commit to reclaim memory.
192
+ */
193
+ _cleanupDeletedCache() {
194
+ if (this.deletedCache.size === 0 && this.versionIndex.size === 0) return;
195
+ let minActiveVersion = this.version;
196
+ if (this.activeTransactions.size > 0) {
197
+ for (const tx of this.activeTransactions) {
198
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
199
+ minActiveVersion = tx.snapshotVersion;
200
+ }
201
+ }
202
+ }
203
+ if (this.deletedCache.size > 0) {
204
+ for (const [key, cachedList] of this.deletedCache) {
205
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
206
+ if (remaining.length === 0) {
207
+ this.deletedCache.delete(key);
208
+ } else {
209
+ this.deletedCache.set(key, remaining);
210
+ }
211
+ }
212
+ }
213
+ if (this.versionIndex.size > 0) {
214
+ for (const [key, versions] of this.versionIndex) {
215
+ let latestInSnapshotIdx = -1;
216
+ for (let i = versions.length - 1; i >= 0; i--) {
217
+ if (versions[i].version <= minActiveVersion) {
218
+ latestInSnapshotIdx = i;
219
+ break;
220
+ }
221
+ }
222
+ if (latestInSnapshotIdx > 0) {
223
+ versions.splice(0, latestInSnapshotIdx);
224
+ }
225
+ }
226
+ }
227
+ }
189
228
  };
190
229
  var SyncMVCCStrategy = class extends MVCCStrategy {
191
230
  };
@@ -525,25 +564,6 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
525
564
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
526
565
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
527
566
  }
528
- _cleanupDeletedCache() {
529
- if (this.deletedCache.size === 0) return;
530
- let minActiveVersion = this.version;
531
- if (this.activeTransactions.size > 0) {
532
- for (const tx of this.activeTransactions) {
533
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
534
- minActiveVersion = tx.snapshotVersion;
535
- }
536
- }
537
- }
538
- for (const [key, cachedList] of this.deletedCache) {
539
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
540
- if (remaining.length === 0) {
541
- this.deletedCache.delete(key);
542
- } else {
543
- this.deletedCache.set(key, remaining);
544
- }
545
- }
546
- }
547
567
  };
548
568
  var AsyncMVCCStrategy = class extends MVCCStrategy {
549
569
  };
@@ -1152,25 +1172,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1152
1172
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1153
1173
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
1154
1174
  }
1155
- _cleanupDeletedCache() {
1156
- if (this.deletedCache.size === 0) return;
1157
- let minActiveVersion = this.version;
1158
- if (this.activeTransactions.size > 0) {
1159
- for (const tx of this.activeTransactions) {
1160
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
1161
- minActiveVersion = tx.snapshotVersion;
1162
- }
1163
- }
1164
- }
1165
- for (const [key, cachedList] of this.deletedCache) {
1166
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
1167
- if (remaining.length === 0) {
1168
- this.deletedCache.delete(key);
1169
- } else {
1170
- this.deletedCache.set(key, remaining);
1171
- }
1172
- }
1173
- }
1174
1175
  };
1175
1176
 
1176
1177
  // node_modules/cache-entanglement/dist/esm/index.mjs
@@ -1781,6 +1782,25 @@ var BPTreeTransaction = class _BPTreeTransaction {
1781
1782
  this._cachedRegexp.clear();
1782
1783
  this.nodes.clear();
1783
1784
  }
1785
+ _binarySearchValues(values, target, usePrimary = false, upperBound = false) {
1786
+ let low = 0;
1787
+ let high = values.length;
1788
+ let found = false;
1789
+ while (low < high) {
1790
+ const mid = low + high >>> 1;
1791
+ const cmp = usePrimary ? this.comparator.primaryAsc(target, values[mid]) : this.comparator.asc(target, values[mid]);
1792
+ if (cmp === 0) {
1793
+ found = true;
1794
+ if (upperBound) low = mid + 1;
1795
+ else high = mid;
1796
+ } else if (cmp < 0) {
1797
+ high = mid;
1798
+ } else {
1799
+ low = mid + 1;
1800
+ }
1801
+ }
1802
+ return { index: low, found };
1803
+ }
1784
1804
  };
1785
1805
 
1786
1806
  // src/transaction/BPTreeSyncTransaction.ts
@@ -1942,58 +1962,24 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
1942
1962
  insertableNode(value) {
1943
1963
  let node = this.getNode(this.rootId);
1944
1964
  while (!node.leaf) {
1945
- for (let i = 0, len = node.values.length; i < len; i++) {
1946
- const nValue = node.values[i];
1947
- const k = node.keys;
1948
- if (this.comparator.isSame(value, nValue)) {
1949
- node = this.getNode(k[i + 1]);
1950
- break;
1951
- } else if (this.comparator.isLower(value, nValue)) {
1952
- node = this.getNode(k[i]);
1953
- break;
1954
- } else if (i + 1 === node.values.length) {
1955
- node = this.getNode(k[i + 1]);
1956
- break;
1957
- }
1958
- }
1965
+ const { index } = this._binarySearchValues(node.values, value, false, true);
1966
+ node = this.getNode(node.keys[index]);
1959
1967
  }
1960
1968
  return node;
1961
1969
  }
1962
1970
  insertableNodeByPrimary(value) {
1963
1971
  let node = this.getNode(this.rootId);
1964
1972
  while (!node.leaf) {
1965
- for (let i = 0, len = node.values.length; i < len; i++) {
1966
- const nValue = node.values[i];
1967
- const k = node.keys;
1968
- if (this.comparator.isPrimarySame(value, nValue)) {
1969
- node = this.getNode(k[i]);
1970
- break;
1971
- } else if (this.comparator.isPrimaryLower(value, nValue)) {
1972
- node = this.getNode(k[i]);
1973
- break;
1974
- } else if (i + 1 === node.values.length) {
1975
- node = this.getNode(k[i + 1]);
1976
- break;
1977
- }
1978
- }
1973
+ const { index } = this._binarySearchValues(node.values, value, true, false);
1974
+ node = this.getNode(node.keys[index]);
1979
1975
  }
1980
1976
  return node;
1981
1977
  }
1982
1978
  insertableRightestNodeByPrimary(value) {
1983
1979
  let node = this.getNode(this.rootId);
1984
1980
  while (!node.leaf) {
1985
- for (let i = 0, len = node.values.length; i < len; i++) {
1986
- const nValue = node.values[i];
1987
- const k = node.keys;
1988
- if (this.comparator.isPrimaryLower(value, nValue)) {
1989
- node = this.getNode(k[i]);
1990
- break;
1991
- }
1992
- if (i + 1 === node.values.length) {
1993
- node = this.getNode(k[i + 1]);
1994
- break;
1995
- }
1996
- }
1981
+ const { index } = this._binarySearchValues(node.values, value, true, true);
1982
+ node = this.getNode(node.keys[index]);
1997
1983
  }
1998
1984
  return node;
1999
1985
  }
@@ -2126,12 +2112,11 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2126
2112
  }
2127
2113
  exists(key, value) {
2128
2114
  const node = this.insertableNode(value);
2129
- for (let i = 0, len = node.values.length; i < len; i++) {
2130
- if (this.comparator.isSame(value, node.values[i])) {
2131
- const keys = node.keys[i];
2132
- if (keys.includes(key)) {
2133
- return true;
2134
- }
2115
+ const { index, found } = this._binarySearchValues(node.values, value);
2116
+ if (found) {
2117
+ const keys = node.keys[index];
2118
+ if (keys.includes(key)) {
2119
+ return true;
2135
2120
  }
2136
2121
  }
2137
2122
  return false;
@@ -2767,58 +2752,24 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2767
2752
  async insertableNode(value) {
2768
2753
  let node = await this.getNode(this.rootId);
2769
2754
  while (!node.leaf) {
2770
- for (let i = 0, len = node.values.length; i < len; i++) {
2771
- const nValue = node.values[i];
2772
- const k = node.keys;
2773
- if (this.comparator.isSame(value, nValue)) {
2774
- node = await this.getNode(k[i + 1]);
2775
- break;
2776
- } else if (this.comparator.isLower(value, nValue)) {
2777
- node = await this.getNode(k[i]);
2778
- break;
2779
- } else if (i + 1 === node.values.length) {
2780
- node = await this.getNode(k[i + 1]);
2781
- break;
2782
- }
2783
- }
2755
+ const { index } = this._binarySearchValues(node.values, value, false, true);
2756
+ node = await this.getNode(node.keys[index]);
2784
2757
  }
2785
2758
  return node;
2786
2759
  }
2787
2760
  async insertableNodeByPrimary(value) {
2788
2761
  let node = await this.getNode(this.rootId);
2789
2762
  while (!node.leaf) {
2790
- for (let i = 0, len = node.values.length; i < len; i++) {
2791
- const nValue = node.values[i];
2792
- const k = node.keys;
2793
- if (this.comparator.isPrimarySame(value, nValue)) {
2794
- node = await this.getNode(k[i]);
2795
- break;
2796
- } else if (this.comparator.isPrimaryLower(value, nValue)) {
2797
- node = await this.getNode(k[i]);
2798
- break;
2799
- } else if (i + 1 === node.values.length) {
2800
- node = await this.getNode(k[i + 1]);
2801
- break;
2802
- }
2803
- }
2763
+ const { index } = this._binarySearchValues(node.values, value, true, false);
2764
+ node = await this.getNode(node.keys[index]);
2804
2765
  }
2805
2766
  return node;
2806
2767
  }
2807
2768
  async insertableRightestNodeByPrimary(value) {
2808
2769
  let node = await this.getNode(this.rootId);
2809
2770
  while (!node.leaf) {
2810
- for (let i = 0, len = node.values.length; i < len; i++) {
2811
- const nValue = node.values[i];
2812
- const k = node.keys;
2813
- if (this.comparator.isPrimaryLower(value, nValue)) {
2814
- node = await this.getNode(k[i]);
2815
- break;
2816
- }
2817
- if (i + 1 === node.values.length) {
2818
- node = await this.getNode(k[i + 1]);
2819
- break;
2820
- }
2821
- }
2771
+ const { index } = this._binarySearchValues(node.values, value, true, true);
2772
+ node = await this.getNode(node.keys[index]);
2822
2773
  }
2823
2774
  return node;
2824
2775
  }
@@ -2871,11 +2822,21 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2871
2822
  let node = startNode;
2872
2823
  let done = false;
2873
2824
  let hasMatched = false;
2825
+ let nextNodePromise = null;
2874
2826
  while (!done) {
2875
2827
  if (endNode && node.id === endNode.id) {
2876
2828
  done = true;
2877
2829
  break;
2878
2830
  }
2831
+ if (direction === 1) {
2832
+ if (node.next && !done) {
2833
+ nextNodePromise = this.getNode(node.next);
2834
+ }
2835
+ } else {
2836
+ if (node.prev && !done) {
2837
+ nextNodePromise = this.getNode(node.prev);
2838
+ }
2839
+ }
2879
2840
  const len = node.values.length;
2880
2841
  if (direction === 1) {
2881
2842
  for (let i = 0; i < len; i++) {
@@ -2908,19 +2869,15 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2908
2869
  }
2909
2870
  }
2910
2871
  }
2911
- if (done) break;
2912
- if (direction === 1) {
2913
- if (!node.next) {
2914
- done = true;
2915
- break;
2916
- }
2917
- node = await this.getNode(node.next);
2872
+ if (done) {
2873
+ if (nextNodePromise) await nextNodePromise;
2874
+ break;
2875
+ }
2876
+ if (nextNodePromise) {
2877
+ node = await nextNodePromise;
2878
+ nextNodePromise = null;
2918
2879
  } else {
2919
- if (!node.prev) {
2920
- done = true;
2921
- break;
2922
- }
2923
- node = await this.getNode(node.prev);
2880
+ done = true;
2924
2881
  }
2925
2882
  }
2926
2883
  }
@@ -2951,12 +2908,11 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
2951
2908
  }
2952
2909
  async exists(key, value) {
2953
2910
  const node = await this.insertableNode(value);
2954
- for (let i = 0, len = node.values.length; i < len; i++) {
2955
- if (this.comparator.isSame(value, node.values[i])) {
2956
- const keys = node.keys[i];
2957
- if (keys.includes(key)) {
2958
- return true;
2959
- }
2911
+ const { index, found } = this._binarySearchValues(node.values, value);
2912
+ if (found) {
2913
+ const keys = node.keys[index];
2914
+ if (keys.includes(key)) {
2915
+ return true;
2960
2916
  }
2961
2917
  }
2962
2918
  return false;
@@ -182,4 +182,8 @@ export declare abstract class BPTreeTransaction<K, V> {
182
182
  * This method is useful for freeing up memory when the tree is no longer needed.
183
183
  */
184
184
  clear(): void;
185
+ protected _binarySearchValues(values: V[], target: V, usePrimary?: boolean, upperBound?: boolean): {
186
+ index: number;
187
+ found: boolean;
188
+ };
185
189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serializable-bptree",
3
- "version": "8.1.0",
3
+ "version": "8.1.2",
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",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "cache-entanglement": "^1.7.1",
47
- "mvcc-api": "^1.2.9",
47
+ "mvcc-api": "^1.2.11",
48
48
  "ryoiki": "^1.2.0"
49
49
  }
50
50
  }