dataply 0.0.24-alpha.10 → 0.0.24-alpha.12

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/dist/cjs/index.js CHANGED
@@ -126,39 +126,125 @@ var StringComparator = class extends ValueComparator {
126
126
  var MVCCStrategy = class {
127
127
  };
128
128
  var LRUMap = class {
129
- cache = /* @__PURE__ */ new Map();
130
129
  capacity;
130
+ map;
131
+ head = null;
132
+ tail = null;
133
+ /**
134
+ * Creates an instance of LRUMap.
135
+ * @param capacity The maximum number of items the cache can hold.
136
+ */
131
137
  constructor(capacity) {
132
138
  this.capacity = capacity;
139
+ this.map = /* @__PURE__ */ new Map();
133
140
  }
134
- get(key) {
135
- if (!this.cache.has(key)) return void 0;
136
- const value = this.cache.get(key);
137
- this.cache.delete(key);
138
- this.cache.set(key, value);
139
- return value;
141
+ /**
142
+ * Promotes a node to the head of the linked list (marks as most recently used).
143
+ * @param node The node to promote.
144
+ */
145
+ promote(node) {
146
+ this.extract(node);
147
+ this.prepend(node);
148
+ }
149
+ /**
150
+ * Disconnects a node from the doubly linked list.
151
+ * @param node The node to extract.
152
+ */
153
+ extract(node) {
154
+ if (node.prev) node.prev.next = node.next;
155
+ else this.head = node.next;
156
+ if (node.next) node.next.prev = node.prev;
157
+ else this.tail = node.prev;
158
+ node.prev = null;
159
+ node.next = null;
160
+ }
161
+ /**
162
+ * Inserts a node at the head of the doubly linked list.
163
+ * @param node The node to prepend.
164
+ */
165
+ prepend(node) {
166
+ node.next = this.head;
167
+ if (this.head) this.head.prev = node;
168
+ this.head = node;
169
+ if (!this.tail) this.tail = node;
140
170
  }
171
+ /**
172
+ * Stores or updates a value by key.
173
+ * If the capacity is exceeded, the least recently used item (tail) is removed.
174
+ * @param key The key to store.
175
+ * @param value The value to store.
176
+ */
141
177
  set(key, value) {
142
- if (this.cache.has(key)) {
143
- this.cache.delete(key);
144
- } else if (this.cache.size >= this.capacity) {
145
- const oldestKey = this.cache.keys().next().value;
146
- if (oldestKey !== void 0) this.cache.delete(oldestKey);
178
+ const existing = this.map.get(key);
179
+ if (existing) {
180
+ existing.value = value;
181
+ this.promote(existing);
182
+ return;
147
183
  }
148
- this.cache.set(key, value);
149
- return this;
184
+ const newNode = { key, value, prev: null, next: null };
185
+ this.map.set(key, newNode);
186
+ this.prepend(newNode);
187
+ if (this.map.size > this.capacity && this.tail) {
188
+ this.map.delete(this.tail.key);
189
+ this.extract(this.tail);
190
+ }
191
+ }
192
+ /**
193
+ * Retrieves a value by key.
194
+ * Accessing the item moves it to the "most recently used" position.
195
+ * @param key The key to look for.
196
+ * @returns The value associated with the key, or undefined if not found.
197
+ */
198
+ get(key) {
199
+ const node = this.map.get(key);
200
+ if (!node) return void 0;
201
+ this.promote(node);
202
+ return node.value;
150
203
  }
204
+ /**
205
+ * Checks if a key exists in the cache without changing its access order.
206
+ * @param key The key to check.
207
+ * @returns True if the key exists, false otherwise.
208
+ */
151
209
  has(key) {
152
- return this.cache.has(key);
210
+ return this.map.has(key);
153
211
  }
212
+ /**
213
+ * Removes a key and its associated value from the cache.
214
+ * @param key The key to remove.
215
+ * @returns True if the key was found and removed, false otherwise.
216
+ */
154
217
  delete(key) {
155
- return this.cache.delete(key);
218
+ const node = this.map.get(key);
219
+ if (!node) return false;
220
+ this.extract(node);
221
+ this.map.delete(key);
222
+ return true;
156
223
  }
157
- clear() {
158
- this.cache.clear();
224
+ /**
225
+ * Returns an iterator of keys in the order of most recently used to least recently used.
226
+ * @returns An iterable iterator of keys.
227
+ */
228
+ *keys() {
229
+ let current = this.head;
230
+ while (current) {
231
+ yield current.key;
232
+ current = current.next;
233
+ }
159
234
  }
235
+ /**
236
+ * Returns the current number of items in the cache.
237
+ */
160
238
  get size() {
161
- return this.cache.size;
239
+ return this.map.size;
240
+ }
241
+ /**
242
+ * Clears all items from the cache.
243
+ */
244
+ clear() {
245
+ this.map.clear();
246
+ this.head = null;
247
+ this.tail = null;
162
248
  }
163
249
  };
164
250
  var MVCCTransaction = class {
@@ -1552,7 +1638,7 @@ var BPTreeTransaction = class _BPTreeTransaction {
1552
1638
  searchConfigs = {
1553
1639
  gt: {
1554
1640
  asc: {
1555
- start: (tx, v) => tx.insertableNodeByPrimary(v[0]),
1641
+ start: (tx, v) => tx.insertableRightestNodeByPrimary(v[0]),
1556
1642
  end: () => null,
1557
1643
  direction: 1,
1558
1644
  earlyTerminate: false
@@ -1650,7 +1736,7 @@ var BPTreeTransaction = class _BPTreeTransaction {
1650
1736
  },
1651
1737
  primaryGt: {
1652
1738
  asc: {
1653
- start: (tx, v) => tx.insertableRightestEndNodeByPrimary(v[0]),
1739
+ start: (tx, v) => tx.insertableRightestNodeByPrimary(v[0]),
1654
1740
  end: () => null,
1655
1741
  direction: 1,
1656
1742
  earlyTerminate: false
@@ -1712,7 +1798,7 @@ var BPTreeTransaction = class _BPTreeTransaction {
1712
1798
  earlyTerminate: true
1713
1799
  },
1714
1800
  desc: {
1715
- start: (tx, v) => tx.insertableRightestEndNodeByPrimary(v[0]),
1801
+ start: (tx, v) => tx.insertableRightestNodeByPrimary(v[0]),
1716
1802
  end: (tx, v) => tx.insertableEndNode(v[0], -1),
1717
1803
  direction: -1,
1718
1804
  earlyTerminate: true
@@ -1740,7 +1826,7 @@ var BPTreeTransaction = class _BPTreeTransaction {
1740
1826
  earlyTerminate: false
1741
1827
  },
1742
1828
  desc: {
1743
- start: (tx, v) => tx.insertableRightestEndNodeByPrimary(tx.highestPrimaryValue(v)),
1829
+ start: (tx, v) => tx.insertableRightestNodeByPrimary(tx.highestPrimaryValue(v)),
1744
1830
  end: (tx, v) => tx.insertableEndNode(tx.lowestPrimaryValue(v), -1),
1745
1831
  direction: -1,
1746
1832
  earlyTerminate: false
@@ -1897,30 +1983,62 @@ var BPTreeTransaction = class _BPTreeTransaction {
1897
1983
  return JSON.parse(JSON.stringify(node));
1898
1984
  }
1899
1985
  /**
1900
- * Selects the best driver key from a condition object.
1901
- * The driver key determines the starting point and traversal direction for queries.
1902
- *
1986
+ * Resolves the best start/end configuration by independently examining
1987
+ * all conditions. Selects the tightest lower bound for start and the
1988
+ * tightest upper bound for end (in asc; reversed for desc).
1989
+ *
1903
1990
  * @param condition The condition to analyze.
1904
- * @returns The best driver key or null if no valid key found.
1905
- */
1906
- getDriverKey(condition) {
1907
- if ("primaryEqual" in condition) return "primaryEqual";
1908
- if ("equal" in condition) return "equal";
1909
- if ("gt" in condition) return "gt";
1910
- if ("gte" in condition) return "gte";
1911
- if ("lt" in condition) return "lt";
1912
- if ("lte" in condition) return "lte";
1913
- if ("primaryGt" in condition) return "primaryGt";
1914
- if ("primaryGte" in condition) return "primaryGte";
1915
- if ("primaryLt" in condition) return "primaryLt";
1916
- if ("primaryLte" in condition) return "primaryLte";
1917
- if ("like" in condition) return "like";
1918
- if ("notEqual" in condition) return "notEqual";
1919
- if ("primaryNotEqual" in condition) return "primaryNotEqual";
1920
- if ("or" in condition) return "or";
1921
- if ("primaryOr" in condition) return "primaryOr";
1922
- return null;
1923
- }
1991
+ * @param order The sort order ('asc' or 'desc').
1992
+ * @returns The resolved start/end keys, values, and traversal direction.
1993
+ */
1994
+ resolveStartEndConfigs(condition, order) {
1995
+ const direction = order === "asc" ? 1 : -1;
1996
+ const startCandidates = order === "asc" ? _BPTreeTransaction._lowerBoundKeys : _BPTreeTransaction._upperBoundKeys;
1997
+ const endCandidates = order === "asc" ? _BPTreeTransaction._upperBoundKeys : _BPTreeTransaction._lowerBoundKeys;
1998
+ let startKey = null;
1999
+ let endKey = null;
2000
+ let startValues = [];
2001
+ let endValues = [];
2002
+ for (const key of startCandidates) {
2003
+ if (key in condition) {
2004
+ startKey = key;
2005
+ startValues = this.ensureValues(condition[key]);
2006
+ break;
2007
+ }
2008
+ }
2009
+ for (const key of endCandidates) {
2010
+ if (key in condition) {
2011
+ endKey = key;
2012
+ endValues = this.ensureValues(condition[key]);
2013
+ break;
2014
+ }
2015
+ }
2016
+ return { startKey, endKey, startValues, endValues, direction };
2017
+ }
2018
+ // Lower bound providers, ordered by selectivity (tightest first)
2019
+ // Used for asc start / desc end
2020
+ static _lowerBoundKeys = [
2021
+ "primaryEqual",
2022
+ "equal",
2023
+ "primaryGt",
2024
+ "gt",
2025
+ "primaryGte",
2026
+ "gte",
2027
+ "primaryOr",
2028
+ "or"
2029
+ ];
2030
+ // Upper bound providers, ordered by selectivity (tightest first)
2031
+ // Used for asc end / desc start
2032
+ static _upperBoundKeys = [
2033
+ "primaryEqual",
2034
+ "equal",
2035
+ "primaryLt",
2036
+ "lt",
2037
+ "primaryLte",
2038
+ "lte",
2039
+ "primaryOr",
2040
+ "or"
2041
+ ];
1924
2042
  constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
1925
2043
  this.rootTx = rootTx === null ? this : rootTx;
1926
2044
  this.mvccRoot = mvccRoot;
@@ -2226,13 +2344,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2226
2344
  }
2227
2345
  return node;
2228
2346
  }
2229
- *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
2347
+ *getPairsGenerator(startNode, endNode, direction) {
2230
2348
  let node = startNode;
2231
- let done = false;
2232
- let hasMatched = false;
2233
- while (!done) {
2349
+ while (true) {
2234
2350
  if (endNode && node.id === endNode.id) {
2235
- done = true;
2236
2351
  break;
2237
2352
  }
2238
2353
  const len = node.values.length;
@@ -2240,14 +2355,8 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2240
2355
  for (let i = 0; i < len; i++) {
2241
2356
  const nValue = node.values[i];
2242
2357
  const keys = node.keys[i];
2243
- if (comparator(nValue, value)) {
2244
- hasMatched = true;
2245
- for (let j = 0; j < keys.length; j++) {
2246
- yield [keys[j], nValue];
2247
- }
2248
- } else if (earlyTerminate && hasMatched) {
2249
- done = true;
2250
- break;
2358
+ for (let j = 0, kLen = keys.length; j < kLen; j++) {
2359
+ yield [keys[j], nValue];
2251
2360
  }
2252
2361
  }
2253
2362
  } else {
@@ -2255,30 +2364,17 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2255
2364
  while (i--) {
2256
2365
  const nValue = node.values[i];
2257
2366
  const keys = node.keys[i];
2258
- if (comparator(nValue, value)) {
2259
- hasMatched = true;
2260
- let j = keys.length;
2261
- while (j--) {
2262
- yield [keys[j], nValue];
2263
- }
2264
- } else if (earlyTerminate && hasMatched) {
2265
- done = true;
2266
- break;
2367
+ let j = keys.length;
2368
+ while (j--) {
2369
+ yield [keys[j], nValue];
2267
2370
  }
2268
2371
  }
2269
2372
  }
2270
- if (done) break;
2271
2373
  if (direction === 1) {
2272
- if (!node.next) {
2273
- done = true;
2274
- break;
2275
- }
2374
+ if (!node.next) break;
2276
2375
  node = this.getNode(node.next);
2277
2376
  } else {
2278
- if (!node.prev) {
2279
- done = true;
2280
- break;
2281
- }
2377
+ if (!node.prev) break;
2282
2378
  node = this.getNode(node.prev);
2283
2379
  }
2284
2380
  }
@@ -2367,49 +2463,36 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2367
2463
  }
2368
2464
  *whereStream(condition, options) {
2369
2465
  const { filterValues, limit, order = "asc" } = options ?? {};
2370
- const driverKey = this.getDriverKey(condition);
2371
- if (!driverKey) return;
2372
- const value = condition[driverKey];
2373
- const v = this.ensureValues(value);
2374
- const config = this.searchConfigs[driverKey][order];
2375
- let startNode = config.start(this, v);
2376
- let endNode = config.end(this, v);
2377
- const direction = config.direction;
2378
- const earlyTerminate = config.earlyTerminate;
2379
- if (order === "desc" && !startNode) {
2380
- startNode = this.rightestNode();
2381
- }
2382
- if (order === "asc" && !startNode) {
2383
- startNode = this.leftestNode();
2466
+ const conditionKeys = Object.keys(condition);
2467
+ if (conditionKeys.length === 0) return;
2468
+ const resolved = this.resolveStartEndConfigs(condition, order);
2469
+ const direction = resolved.direction;
2470
+ let startNode;
2471
+ if (resolved.startKey) {
2472
+ const startConfig = this.searchConfigs[resolved.startKey][order];
2473
+ startNode = startConfig.start(this, resolved.startValues);
2474
+ } else {
2475
+ startNode = order === "asc" ? this.leftestNode() : this.rightestNode();
2476
+ }
2477
+ let endNode = null;
2478
+ if (resolved.endKey) {
2479
+ const endConfig = this.searchConfigs[resolved.endKey][order];
2480
+ endNode = endConfig.end(this, resolved.endValues);
2384
2481
  }
2385
2482
  if (!startNode) return;
2386
- const comparator = this.verifierMap[driverKey];
2387
2483
  const generator = this.getPairsGenerator(
2388
- value,
2389
2484
  startNode,
2390
2485
  endNode,
2391
- comparator,
2392
- direction,
2393
- earlyTerminate
2486
+ direction
2394
2487
  );
2395
2488
  let count = 0;
2396
2489
  const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
2397
2490
  for (const pair of generator) {
2398
- const [k, v2] = pair;
2491
+ const [k, v] = pair;
2399
2492
  if (intersection && !intersection.has(k)) {
2400
2493
  continue;
2401
2494
  }
2402
- let isMatch = true;
2403
- for (const key in condition) {
2404
- if (key === driverKey) continue;
2405
- const verify = this.verifierMap[key];
2406
- const condValue = condition[key];
2407
- if (!verify(v2, condValue)) {
2408
- isMatch = false;
2409
- break;
2410
- }
2411
- }
2412
- if (isMatch) {
2495
+ if (this.verify(v, condition)) {
2413
2496
  yield pair;
2414
2497
  count++;
2415
2498
  if (limit !== void 0 && count >= limit) {
@@ -3354,22 +3437,19 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3354
3437
  }
3355
3438
  return node;
3356
3439
  }
3357
- async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
3440
+ async *getPairsGenerator(startNode, endNode, direction) {
3358
3441
  let node = startNode;
3359
- let done = false;
3360
- let hasMatched = false;
3361
3442
  let nextNodePromise = null;
3362
- while (!done) {
3443
+ while (true) {
3363
3444
  if (endNode && node.id === endNode.id) {
3364
- done = true;
3365
3445
  break;
3366
3446
  }
3367
3447
  if (direction === 1) {
3368
- if (node.next && !done) {
3448
+ if (node.next) {
3369
3449
  nextNodePromise = this.getNode(node.next);
3370
3450
  }
3371
3451
  } else {
3372
- if (node.prev && !done) {
3452
+ if (node.prev) {
3373
3453
  nextNodePromise = this.getNode(node.prev);
3374
3454
  }
3375
3455
  }
@@ -3378,14 +3458,8 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3378
3458
  for (let i = 0; i < len; i++) {
3379
3459
  const nValue = node.values[i];
3380
3460
  const keys = node.keys[i];
3381
- if (comparator(nValue, value)) {
3382
- hasMatched = true;
3383
- for (let j = 0; j < keys.length; j++) {
3384
- yield [keys[j], nValue];
3385
- }
3386
- } else if (earlyTerminate && hasMatched) {
3387
- done = true;
3388
- break;
3461
+ for (let j = 0, kLen = keys.length; j < kLen; j++) {
3462
+ yield [keys[j], nValue];
3389
3463
  }
3390
3464
  }
3391
3465
  } else {
@@ -3393,27 +3467,17 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3393
3467
  while (i--) {
3394
3468
  const nValue = node.values[i];
3395
3469
  const keys = node.keys[i];
3396
- if (comparator(nValue, value)) {
3397
- hasMatched = true;
3398
- let j = keys.length;
3399
- while (j--) {
3400
- yield [keys[j], nValue];
3401
- }
3402
- } else if (earlyTerminate && hasMatched) {
3403
- done = true;
3404
- break;
3470
+ let j = keys.length;
3471
+ while (j--) {
3472
+ yield [keys[j], nValue];
3405
3473
  }
3406
3474
  }
3407
3475
  }
3408
- if (done) {
3409
- if (nextNodePromise) await nextNodePromise;
3410
- break;
3411
- }
3412
3476
  if (nextNodePromise) {
3413
3477
  node = await nextNodePromise;
3414
3478
  nextNodePromise = null;
3415
3479
  } else {
3416
- done = true;
3480
+ break;
3417
3481
  }
3418
3482
  }
3419
3483
  }
@@ -3501,49 +3565,36 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3501
3565
  }
3502
3566
  async *whereStream(condition, options) {
3503
3567
  const { filterValues, limit, order = "asc" } = options ?? {};
3504
- const driverKey = this.getDriverKey(condition);
3505
- if (!driverKey) return;
3506
- const value = condition[driverKey];
3507
- const v = this.ensureValues(value);
3508
- const config = this.searchConfigs[driverKey][order];
3509
- let startNode = await config.start(this, v);
3510
- let endNode = await config.end(this, v);
3511
- const direction = config.direction;
3512
- const earlyTerminate = config.earlyTerminate;
3513
- if (order === "desc" && !startNode) {
3514
- startNode = await this.rightestNode();
3515
- }
3516
- if (order === "asc" && !startNode) {
3517
- startNode = await this.leftestNode();
3568
+ const conditionKeys = Object.keys(condition);
3569
+ if (conditionKeys.length === 0) return;
3570
+ const resolved = this.resolveStartEndConfigs(condition, order);
3571
+ const direction = resolved.direction;
3572
+ let startNode;
3573
+ if (resolved.startKey) {
3574
+ const startConfig = this.searchConfigs[resolved.startKey][order];
3575
+ startNode = await startConfig.start(this, resolved.startValues);
3576
+ } else {
3577
+ startNode = order === "asc" ? await this.leftestNode() : await this.rightestNode();
3578
+ }
3579
+ let endNode = null;
3580
+ if (resolved.endKey) {
3581
+ const endConfig = this.searchConfigs[resolved.endKey][order];
3582
+ endNode = await endConfig.end(this, resolved.endValues);
3518
3583
  }
3519
3584
  if (!startNode) return;
3520
- const comparator = this.verifierMap[driverKey];
3521
3585
  const generator = this.getPairsGenerator(
3522
- value,
3523
3586
  startNode,
3524
3587
  endNode,
3525
- comparator,
3526
- direction,
3527
- earlyTerminate
3588
+ direction
3528
3589
  );
3529
3590
  let count = 0;
3530
3591
  const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
3531
3592
  for await (const pair of generator) {
3532
- const [k, v2] = pair;
3593
+ const [k, v] = pair;
3533
3594
  if (intersection && !intersection.has(k)) {
3534
3595
  continue;
3535
3596
  }
3536
- let isMatch = true;
3537
- for (const key in condition) {
3538
- if (key === driverKey) continue;
3539
- const verify = this.verifierMap[key];
3540
- const condValue = condition[key];
3541
- if (!verify(v2, condValue)) {
3542
- isMatch = false;
3543
- break;
3544
- }
3545
- }
3546
- if (isMatch) {
3597
+ if (this.verify(v, condition)) {
3547
3598
  yield pair;
3548
3599
  count++;
3549
3600
  if (limit !== void 0 && count >= limit) {
@@ -4844,39 +4895,125 @@ var InvertedWeakMap = class {
4844
4895
  var MVCCStrategy2 = class {
4845
4896
  };
4846
4897
  var LRUMap3 = class {
4847
- cache = /* @__PURE__ */ new Map();
4848
4898
  capacity;
4899
+ map;
4900
+ head = null;
4901
+ tail = null;
4902
+ /**
4903
+ * Creates an instance of LRUMap.
4904
+ * @param capacity The maximum number of items the cache can hold.
4905
+ */
4849
4906
  constructor(capacity) {
4850
4907
  this.capacity = capacity;
4908
+ this.map = /* @__PURE__ */ new Map();
4851
4909
  }
4852
- get(key) {
4853
- if (!this.cache.has(key)) return void 0;
4854
- const value = this.cache.get(key);
4855
- this.cache.delete(key);
4856
- this.cache.set(key, value);
4857
- return value;
4910
+ /**
4911
+ * Promotes a node to the head of the linked list (marks as most recently used).
4912
+ * @param node The node to promote.
4913
+ */
4914
+ promote(node) {
4915
+ this.extract(node);
4916
+ this.prepend(node);
4858
4917
  }
4918
+ /**
4919
+ * Disconnects a node from the doubly linked list.
4920
+ * @param node The node to extract.
4921
+ */
4922
+ extract(node) {
4923
+ if (node.prev) node.prev.next = node.next;
4924
+ else this.head = node.next;
4925
+ if (node.next) node.next.prev = node.prev;
4926
+ else this.tail = node.prev;
4927
+ node.prev = null;
4928
+ node.next = null;
4929
+ }
4930
+ /**
4931
+ * Inserts a node at the head of the doubly linked list.
4932
+ * @param node The node to prepend.
4933
+ */
4934
+ prepend(node) {
4935
+ node.next = this.head;
4936
+ if (this.head) this.head.prev = node;
4937
+ this.head = node;
4938
+ if (!this.tail) this.tail = node;
4939
+ }
4940
+ /**
4941
+ * Stores or updates a value by key.
4942
+ * If the capacity is exceeded, the least recently used item (tail) is removed.
4943
+ * @param key The key to store.
4944
+ * @param value The value to store.
4945
+ */
4859
4946
  set(key, value) {
4860
- if (this.cache.has(key)) {
4861
- this.cache.delete(key);
4862
- } else if (this.cache.size >= this.capacity) {
4863
- const oldestKey = this.cache.keys().next().value;
4864
- if (oldestKey !== void 0) this.cache.delete(oldestKey);
4947
+ const existing = this.map.get(key);
4948
+ if (existing) {
4949
+ existing.value = value;
4950
+ this.promote(existing);
4951
+ return;
4865
4952
  }
4866
- this.cache.set(key, value);
4867
- return this;
4953
+ const newNode = { key, value, prev: null, next: null };
4954
+ this.map.set(key, newNode);
4955
+ this.prepend(newNode);
4956
+ if (this.map.size > this.capacity && this.tail) {
4957
+ this.map.delete(this.tail.key);
4958
+ this.extract(this.tail);
4959
+ }
4960
+ }
4961
+ /**
4962
+ * Retrieves a value by key.
4963
+ * Accessing the item moves it to the "most recently used" position.
4964
+ * @param key The key to look for.
4965
+ * @returns The value associated with the key, or undefined if not found.
4966
+ */
4967
+ get(key) {
4968
+ const node = this.map.get(key);
4969
+ if (!node) return void 0;
4970
+ this.promote(node);
4971
+ return node.value;
4868
4972
  }
4973
+ /**
4974
+ * Checks if a key exists in the cache without changing its access order.
4975
+ * @param key The key to check.
4976
+ * @returns True if the key exists, false otherwise.
4977
+ */
4869
4978
  has(key) {
4870
- return this.cache.has(key);
4979
+ return this.map.has(key);
4871
4980
  }
4981
+ /**
4982
+ * Removes a key and its associated value from the cache.
4983
+ * @param key The key to remove.
4984
+ * @returns True if the key was found and removed, false otherwise.
4985
+ */
4872
4986
  delete(key) {
4873
- return this.cache.delete(key);
4987
+ const node = this.map.get(key);
4988
+ if (!node) return false;
4989
+ this.extract(node);
4990
+ this.map.delete(key);
4991
+ return true;
4874
4992
  }
4875
- clear() {
4876
- this.cache.clear();
4993
+ /**
4994
+ * Returns an iterator of keys in the order of most recently used to least recently used.
4995
+ * @returns An iterable iterator of keys.
4996
+ */
4997
+ *keys() {
4998
+ let current = this.head;
4999
+ while (current) {
5000
+ yield current.key;
5001
+ current = current.next;
5002
+ }
4877
5003
  }
5004
+ /**
5005
+ * Returns the current number of items in the cache.
5006
+ */
4878
5007
  get size() {
4879
- return this.cache.size;
5008
+ return this.map.size;
5009
+ }
5010
+ /**
5011
+ * Clears all items from the cache.
5012
+ */
5013
+ clear() {
5014
+ this.map.clear();
5015
+ this.head = null;
5016
+ this.tail = null;
4880
5017
  }
4881
5018
  };
4882
5019
  var MVCCTransaction2 = class {
@@ -6333,21 +6470,68 @@ function crc32(buf) {
6333
6470
  }
6334
6471
 
6335
6472
  // src/utils/array.ts
6336
- function getMinMaxValue(array) {
6337
- let i = 0;
6338
- let min = Infinity;
6339
- let max = -Infinity;
6340
- let len = array.length;
6341
- while (i < len) {
6342
- if (array[i] < min) {
6343
- min = array[i];
6344
- }
6345
- if (array[i] > max) {
6346
- max = array[i];
6473
+ function calcThreshold(sortedGaps, n) {
6474
+ const gLen = sortedGaps.length;
6475
+ if (gLen === 0) return 0;
6476
+ const median = sortedGaps[Math.floor(gLen * 0.5)];
6477
+ const q1 = sortedGaps[Math.floor(gLen * 0.25)];
6478
+ const q3 = sortedGaps[Math.floor(gLen * 0.75)];
6479
+ const iqr = q3 - q1;
6480
+ const logN = Math.max(1, Math.log10(n));
6481
+ if (iqr > 0) {
6482
+ const threshold2 = q3 + iqr * 1.5 * logN;
6483
+ const minJump = Math.max(median * 5, 20);
6484
+ return Math.max(threshold2, minJump);
6485
+ }
6486
+ const baseGap = median > 0 ? median : 1;
6487
+ const p90 = sortedGaps[Math.floor(gLen * 0.9)];
6488
+ if (p90 > baseGap) {
6489
+ const threshold2 = baseGap + (p90 - baseGap) * 0.5 * logN;
6490
+ return Math.max(threshold2, baseGap * 5, 20);
6491
+ }
6492
+ let mean = 0;
6493
+ for (let i = 0; i < gLen; i++) mean += sortedGaps[i];
6494
+ mean /= gLen;
6495
+ let variance = 0;
6496
+ for (let i = 0; i < gLen; i++) {
6497
+ const d = sortedGaps[i] - mean;
6498
+ variance += d * d;
6499
+ }
6500
+ const stddev = Math.sqrt(variance / gLen);
6501
+ if (stddev === 0) {
6502
+ return baseGap * 2;
6503
+ }
6504
+ const threshold = mean + stddev * logN;
6505
+ return Math.max(threshold, baseGap * 5, 20);
6506
+ }
6507
+ function clusterNumbers(numbers, gapMultiplier) {
6508
+ const n = numbers.length;
6509
+ if (n === 0) return [];
6510
+ if (n === 1) return [new Float64Array([numbers[0]])];
6511
+ const sorted = (numbers instanceof Float64Array ? numbers.slice() : Float64Array.from(numbers)).sort();
6512
+ const gaps = new Float64Array(n - 1);
6513
+ for (let i = 0, len = n - 1; i < len; i++) {
6514
+ gaps[i] = sorted[i + 1] - sorted[i];
6515
+ }
6516
+ const sortedGaps = gaps.slice().sort();
6517
+ let threshold;
6518
+ if (gapMultiplier !== void 0) {
6519
+ const q3 = sortedGaps[Math.floor((n - 1) * 0.75)];
6520
+ const iqr = q3 - sortedGaps[Math.floor((n - 1) * 0.25)];
6521
+ threshold = q3 + iqr * gapMultiplier;
6522
+ } else {
6523
+ threshold = calcThreshold(sortedGaps, n);
6524
+ }
6525
+ const clusters = [];
6526
+ let clusterStart = 0;
6527
+ for (let i = 0, len = n - 1; i < len; i++) {
6528
+ if (gaps[i] > threshold) {
6529
+ clusters.push(sorted.subarray(clusterStart, i + 1));
6530
+ clusterStart = i + 1;
6347
6531
  }
6348
- i++;
6349
6532
  }
6350
- return [min, max];
6533
+ clusters.push(sorted.subarray(clusterStart));
6534
+ return clusters;
6351
6535
  }
6352
6536
 
6353
6537
  // src/core/Row.ts
@@ -9311,14 +9495,30 @@ var RowTableEngine = class {
9311
9495
  for (let i = 0, len = pks.length; i < len; i++) {
9312
9496
  pkIndexMap.set(pks[i], i);
9313
9497
  }
9314
- const [minPk, maxPk] = getMinMaxValue(pks);
9315
9498
  const pkRidPairs = new Array(pks.length).fill(null);
9316
9499
  const btx = await this.getBPTreeTransaction(tx);
9317
- const stream = btx.whereStream({ gte: minPk, lte: maxPk });
9318
- for await (const [rid, pk] of stream) {
9319
- const index = pkIndexMap.get(pk);
9320
- if (index !== void 0) {
9321
- pkRidPairs[index] = { pk, rid, index };
9500
+ const clusters = clusterNumbers(pks);
9501
+ for (let i = 0, len = clusters.length; i < len; i++) {
9502
+ const cluster = clusters[i];
9503
+ const minPk = cluster[0];
9504
+ const maxPk = cluster[cluster.length - 1];
9505
+ if (minPk === maxPk) {
9506
+ const keys = await btx.keys({ equal: minPk });
9507
+ if (keys.size > 0) {
9508
+ const rid = keys.values().next().value;
9509
+ const index = pkIndexMap.get(minPk);
9510
+ if (index !== void 0) {
9511
+ pkRidPairs[index] = { pk: minPk, rid, index };
9512
+ }
9513
+ }
9514
+ continue;
9515
+ }
9516
+ const stream = btx.whereStream({ gte: minPk, lte: maxPk });
9517
+ for await (const [rid, pk] of stream) {
9518
+ const index = pkIndexMap.get(pk);
9519
+ if (index !== void 0) {
9520
+ pkRidPairs[index] = { pk, rid, index };
9521
+ }
9322
9522
  }
9323
9523
  }
9324
9524
  return this.fetchRowsByRids(pkRidPairs, tx);
@@ -2,4 +2,11 @@ type SupportedNumberArray = number[] | Uint8Array | Uint16Array | Uint32Array |
2
2
  export declare function getMinValue(array: SupportedNumberArray): number;
3
3
  export declare function getMaxValue(array: SupportedNumberArray): number;
4
4
  export declare function getMinMaxValue(array: SupportedNumberArray): [number, number];
5
+ /**
6
+ * Sorts the input array and splits it into clusters based on the gaps between consecutive elements.
7
+ * @param numbers Array of numbers to cluster
8
+ * @param gapMultiplier Multiplier for the gap threshold (default is calculated automatically)
9
+ * @returns Array of clusters
10
+ */
11
+ export declare function clusterNumbers(numbers: number[] | Float64Array, gapMultiplier?: number): Float64Array[];
5
12
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.24-alpha.10",
3
+ "version": "0.0.24-alpha.12",
4
4
  "description": "A lightweight storage engine for Node.js with support for MVCC, WAL.",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",
@@ -47,8 +47,8 @@
47
47
  "dependencies": {
48
48
  "cache-entanglement": "^1.7.1",
49
49
  "hookall": "^2.2.0",
50
- "mvcc-api": "^1.3.4",
50
+ "mvcc-api": "^1.3.5",
51
51
  "ryoiki": "^1.2.0",
52
- "serializable-bptree": "^8.3.3"
52
+ "serializable-bptree": "^8.3.4"
53
53
  }
54
54
  }