serializable-bptree 3.0.0 → 3.1.0

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
@@ -129,11 +129,45 @@ class AgeComparator extends ValueComparator<MyObject> {
129
129
  asc(a: MyObject, b: MyObject): number {
130
130
  return a.age - b.age
131
131
  }
132
+
133
+ match(value: MyObject): string {
134
+ return value.age
135
+ }
132
136
  }
133
137
  ```
134
138
 
139
+ #### asc
140
+
135
141
  The **asc** method should return values in ascending order. If the return value is negative, it means that the parameter **a** is smaller than **b**. If the return value is positive, it means that **a** is greater than **b**. If the return value is **0**, it indicates that **a** and **b** are of the same size.
136
142
 
143
+ #### match
144
+
145
+ The `match` method is used for the **LIKE** operator. This method specifies which value to test against a regular expression. For example, if you have a tree with values of the structure `{ country: string, capital: number }`, and you want to perform a **LIKE** operation based on the **capital** value, the method should return **value.capital**. In this case, you **CANNOT** perform a **LIKE** operation based on the **country** attribute. The returned value must be a string.
146
+
147
+ ```typescript
148
+ interface MyObject {
149
+ country: string
150
+ capital: string
151
+ }
152
+
153
+ class CompositeComparator extends ValueComparator<MyObject> {
154
+ ...
155
+ match(value: MyObject): string {
156
+ return value.capital
157
+ }
158
+ }
159
+ ```
160
+
161
+ For a tree with simple structure, without complex nesting, returning the value directly would be sufficient.
162
+
163
+ ```typescript
164
+ class StringComparator extends ValueComparator<string> {
165
+ match(value: string): string {
166
+ return value
167
+ }
168
+ }
169
+ ```
170
+
137
171
  ### Serialize strategy
138
172
 
139
173
  A B+tree instance is made up of numerous nodes. You would want to store this value when such nodes are created or updated. Let's assume you want to save it to a file.
@@ -371,7 +405,7 @@ The implementation method for asynchronous operations is not significantly diffe
371
405
 
372
406
  The serializable-bptree minimizes file I/O by storing loaded nodes in-memory. This approach works well in situations where there is a 1:1 relationship between the remote storage and the client. However, in a 1:n scenario, where multiple clients read from and write to a single remote storage, data inconsistency between the remote storage and the clients can occur.
373
407
 
374
- To solve this issue, it's necessary to update the cached nodes. The forceUpdate method was created for this purpose. It allows for the update of specific nodes, but when updating a node ID, a signal must be sent to all clients connected to the remote storage, informing them that the node has been updated. Clients must receive this signal and call the forceUpdate method to update the node. This logic goes beyond the scope of the library, so it must be implemented directly.
408
+ To solve this problem, it's necessary to update the cached nodes. The forceUpdate method was created for this purpose. It fetches the node data cached in the tree instance again. To use this feature, when you save data to the remote storage, you must send a signal to all clients connected to that remote storage indicating that the node has been updated. Clients must receive this signal and configure logic to call the **forceUpdate** method; however, this goes beyond the scope of the library, so you must implement it yourself.
375
409
 
376
410
  ### Concurrency Issue in Asynchronous Trees
377
411
 
package/dist/cjs/index.js CHANGED
@@ -32,8 +32,48 @@ __export(src_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(src_exports);
34
34
 
35
+ // src/base/ValueComparator.ts
36
+ var ValueComparator = class {
37
+ isLower(value, than) {
38
+ return this.asc(value, than) < 0;
39
+ }
40
+ isSame(value, than) {
41
+ return this.asc(value, than) === 0;
42
+ }
43
+ isHigher(value, than) {
44
+ return this.asc(value, than) > 0;
45
+ }
46
+ };
47
+ var NumericComparator = class extends ValueComparator {
48
+ asc(a, b) {
49
+ return a - b;
50
+ }
51
+ match(value) {
52
+ return value.toString();
53
+ }
54
+ };
55
+ var StringComparator = class extends ValueComparator {
56
+ asc(a, b) {
57
+ return a.localeCompare(b);
58
+ }
59
+ match(value) {
60
+ return value;
61
+ }
62
+ };
63
+
64
+ // src/utils/CacheStorage.ts
65
+ var CacheStorage = class extends Map {
66
+ ensure(key, generator) {
67
+ if (!this.has(key)) {
68
+ this.set(key, generator());
69
+ }
70
+ return this.get(key);
71
+ }
72
+ };
73
+
35
74
  // src/base/BPTree.ts
36
75
  var BPTree = class {
76
+ _regexpCache;
37
77
  strategy;
38
78
  comparator;
39
79
  nodes;
@@ -51,11 +91,13 @@ var BPTree = class {
51
91
  equal: (nv, v) => this.comparator.isSame(nv, v),
52
92
  notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
53
93
  like: (nv, v) => {
54
- const nodeValue = nv.toString();
55
- const value = v.toString();
56
- const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
57
- const regex = new RegExp(`^${pattern}$`, "i");
58
- return regex.test(nodeValue);
94
+ const nodeValue = this.comparator.match(nv);
95
+ const value = this.comparator.match(v);
96
+ const regexp = this._regexpCache.ensure(value, () => {
97
+ const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
98
+ return new RegExp(`^${pattern}$`, "i");
99
+ });
100
+ return regexp.test(nodeValue);
59
101
  }
60
102
  };
61
103
  verifierStartNode = {
@@ -76,7 +118,7 @@ var BPTree = class {
76
118
  notEqual: 1,
77
119
  like: 1
78
120
  };
79
- verifierFullSearch = {
121
+ verifierFullScan = {
80
122
  gt: false,
81
123
  gte: false,
82
124
  lt: false,
@@ -96,6 +138,7 @@ var BPTree = class {
96
138
  };
97
139
  }
98
140
  constructor(strategy, comparator) {
141
+ this._regexpCache = new CacheStorage();
99
142
  this._headBuffer = null;
100
143
  this._nodeCreateBuffer = /* @__PURE__ */ new Map();
101
144
  this._nodeUpdateBuffer = /* @__PURE__ */ new Map();
@@ -594,9 +637,9 @@ var BPTreeSync = class extends BPTree {
594
637
  const value = condition[key];
595
638
  const startNode = this.verifierStartNode[key](value);
596
639
  const direction = this.verifierDirection[key];
597
- const fullSearch = this.verifierFullSearch[key];
640
+ const fullScan = this.verifierFullScan[key];
598
641
  const comparator = this.verifierMap[key];
599
- const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
642
+ const pairs = this.getPairs(value, startNode, fullScan, comparator, direction);
600
643
  if (result === null) {
601
644
  result = pairs.map((pair) => pair.key);
602
645
  } else {
@@ -612,9 +655,9 @@ var BPTreeSync = class extends BPTree {
612
655
  const value = condition[key];
613
656
  const startNode = this.verifierStartNode[key](value);
614
657
  const direction = this.verifierDirection[key];
615
- const fullSearch = this.verifierFullSearch[key];
658
+ const fullScan = this.verifierFullScan[key];
616
659
  const comparator = this.verifierMap[key];
617
- const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
660
+ const pairs = this.getPairs(value, startNode, fullScan, comparator, direction);
618
661
  if (result === null) {
619
662
  result = pairs;
620
663
  } else {
@@ -700,13 +743,14 @@ var BPTreeSync = class extends BPTree {
700
743
  this.bufferForHeadUpdate(this.headState);
701
744
  this.commitHeadBuffer();
702
745
  }
703
- forceUpdate(nodeId = null) {
704
- const ids = nodeId === null ? [...this.nodes.keys()] : [nodeId];
705
- for (const id of ids) {
706
- this.nodes.delete(id);
707
- this.getNode(id);
746
+ forceUpdate() {
747
+ const keys = [...this.nodes.keys()];
748
+ this.nodes.clear();
749
+ this.init();
750
+ for (const key of keys) {
751
+ this.getNode(key);
708
752
  }
709
- return ids.length;
753
+ return keys.length;
710
754
  }
711
755
  };
712
756
 
@@ -1128,9 +1172,9 @@ var BPTreeAsync = class extends BPTree {
1128
1172
  const value = condition[key];
1129
1173
  const startNode = await this.verifierStartNode[key](value);
1130
1174
  const direction = this.verifierDirection[key];
1131
- const fullSearch = this.verifierFullSearch[key];
1175
+ const fullScan = this.verifierFullScan[key];
1132
1176
  const comparator = this.verifierMap[key];
1133
- const pairs = await this.getPairs(value, startNode, fullSearch, comparator, direction);
1177
+ const pairs = await this.getPairs(value, startNode, fullScan, comparator, direction);
1134
1178
  if (result === null) {
1135
1179
  result = pairs.map((pair) => pair.key);
1136
1180
  } else {
@@ -1146,9 +1190,9 @@ var BPTreeAsync = class extends BPTree {
1146
1190
  const value = condition[key];
1147
1191
  const startNode = await this.verifierStartNode[key](value);
1148
1192
  const direction = this.verifierDirection[key];
1149
- const fullSearch = this.verifierFullSearch[key];
1193
+ const fullScan = this.verifierFullScan[key];
1150
1194
  const comparator = this.verifierMap[key];
1151
- const pairs = await this.getPairs(value, startNode, fullSearch, comparator, direction);
1195
+ const pairs = await this.getPairs(value, startNode, fullScan, comparator, direction);
1152
1196
  if (result === null) {
1153
1197
  result = pairs;
1154
1198
  } else {
@@ -1234,13 +1278,14 @@ var BPTreeAsync = class extends BPTree {
1234
1278
  this.bufferForHeadUpdate(this.headState);
1235
1279
  await this.commitHeadBuffer();
1236
1280
  }
1237
- async forceUpdate(nodeId = null) {
1238
- const ids = nodeId === null ? [...this.nodes.keys()] : [nodeId];
1239
- for (const id of ids) {
1240
- this.nodes.delete(id);
1241
- await this.getNode(id);
1281
+ async forceUpdate() {
1282
+ const keys = [...this.nodes.keys()];
1283
+ this.nodes.clear();
1284
+ await this.init();
1285
+ for (const key of keys) {
1286
+ await this.getNode(key);
1242
1287
  }
1243
- return ids.length;
1288
+ return keys.length;
1244
1289
  }
1245
1290
  };
1246
1291
 
@@ -1315,26 +1360,3 @@ var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
1315
1360
  this.data.head = head;
1316
1361
  }
1317
1362
  };
1318
-
1319
- // src/ValueComparator.ts
1320
- var ValueComparator = class {
1321
- isLower(value, than) {
1322
- return this.asc(value, than) < 0;
1323
- }
1324
- isSame(value, than) {
1325
- return this.asc(value, than) === 0;
1326
- }
1327
- isHigher(value, than) {
1328
- return this.asc(value, than) > 0;
1329
- }
1330
- };
1331
- var NumericComparator = class extends ValueComparator {
1332
- asc(a, b) {
1333
- return a - b;
1334
- }
1335
- };
1336
- var StringComparator = class extends ValueComparator {
1337
- asc(a, b) {
1338
- return a.localeCompare(b);
1339
- }
1340
- };
package/dist/esm/index.js CHANGED
@@ -1,5 +1,45 @@
1
+ // src/base/ValueComparator.ts
2
+ var ValueComparator = class {
3
+ isLower(value, than) {
4
+ return this.asc(value, than) < 0;
5
+ }
6
+ isSame(value, than) {
7
+ return this.asc(value, than) === 0;
8
+ }
9
+ isHigher(value, than) {
10
+ return this.asc(value, than) > 0;
11
+ }
12
+ };
13
+ var NumericComparator = class extends ValueComparator {
14
+ asc(a, b) {
15
+ return a - b;
16
+ }
17
+ match(value) {
18
+ return value.toString();
19
+ }
20
+ };
21
+ var StringComparator = class extends ValueComparator {
22
+ asc(a, b) {
23
+ return a.localeCompare(b);
24
+ }
25
+ match(value) {
26
+ return value;
27
+ }
28
+ };
29
+
30
+ // src/utils/CacheStorage.ts
31
+ var CacheStorage = class extends Map {
32
+ ensure(key, generator) {
33
+ if (!this.has(key)) {
34
+ this.set(key, generator());
35
+ }
36
+ return this.get(key);
37
+ }
38
+ };
39
+
1
40
  // src/base/BPTree.ts
2
41
  var BPTree = class {
42
+ _regexpCache;
3
43
  strategy;
4
44
  comparator;
5
45
  nodes;
@@ -17,11 +57,13 @@ var BPTree = class {
17
57
  equal: (nv, v) => this.comparator.isSame(nv, v),
18
58
  notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
19
59
  like: (nv, v) => {
20
- const nodeValue = nv.toString();
21
- const value = v.toString();
22
- const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
23
- const regex = new RegExp(`^${pattern}$`, "i");
24
- return regex.test(nodeValue);
60
+ const nodeValue = this.comparator.match(nv);
61
+ const value = this.comparator.match(v);
62
+ const regexp = this._regexpCache.ensure(value, () => {
63
+ const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
64
+ return new RegExp(`^${pattern}$`, "i");
65
+ });
66
+ return regexp.test(nodeValue);
25
67
  }
26
68
  };
27
69
  verifierStartNode = {
@@ -42,7 +84,7 @@ var BPTree = class {
42
84
  notEqual: 1,
43
85
  like: 1
44
86
  };
45
- verifierFullSearch = {
87
+ verifierFullScan = {
46
88
  gt: false,
47
89
  gte: false,
48
90
  lt: false,
@@ -62,6 +104,7 @@ var BPTree = class {
62
104
  };
63
105
  }
64
106
  constructor(strategy, comparator) {
107
+ this._regexpCache = new CacheStorage();
65
108
  this._headBuffer = null;
66
109
  this._nodeCreateBuffer = /* @__PURE__ */ new Map();
67
110
  this._nodeUpdateBuffer = /* @__PURE__ */ new Map();
@@ -560,9 +603,9 @@ var BPTreeSync = class extends BPTree {
560
603
  const value = condition[key];
561
604
  const startNode = this.verifierStartNode[key](value);
562
605
  const direction = this.verifierDirection[key];
563
- const fullSearch = this.verifierFullSearch[key];
606
+ const fullScan = this.verifierFullScan[key];
564
607
  const comparator = this.verifierMap[key];
565
- const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
608
+ const pairs = this.getPairs(value, startNode, fullScan, comparator, direction);
566
609
  if (result === null) {
567
610
  result = pairs.map((pair) => pair.key);
568
611
  } else {
@@ -578,9 +621,9 @@ var BPTreeSync = class extends BPTree {
578
621
  const value = condition[key];
579
622
  const startNode = this.verifierStartNode[key](value);
580
623
  const direction = this.verifierDirection[key];
581
- const fullSearch = this.verifierFullSearch[key];
624
+ const fullScan = this.verifierFullScan[key];
582
625
  const comparator = this.verifierMap[key];
583
- const pairs = this.getPairs(value, startNode, fullSearch, comparator, direction);
626
+ const pairs = this.getPairs(value, startNode, fullScan, comparator, direction);
584
627
  if (result === null) {
585
628
  result = pairs;
586
629
  } else {
@@ -666,13 +709,14 @@ var BPTreeSync = class extends BPTree {
666
709
  this.bufferForHeadUpdate(this.headState);
667
710
  this.commitHeadBuffer();
668
711
  }
669
- forceUpdate(nodeId = null) {
670
- const ids = nodeId === null ? [...this.nodes.keys()] : [nodeId];
671
- for (const id of ids) {
672
- this.nodes.delete(id);
673
- this.getNode(id);
712
+ forceUpdate() {
713
+ const keys = [...this.nodes.keys()];
714
+ this.nodes.clear();
715
+ this.init();
716
+ for (const key of keys) {
717
+ this.getNode(key);
674
718
  }
675
- return ids.length;
719
+ return keys.length;
676
720
  }
677
721
  };
678
722
 
@@ -1094,9 +1138,9 @@ var BPTreeAsync = class extends BPTree {
1094
1138
  const value = condition[key];
1095
1139
  const startNode = await this.verifierStartNode[key](value);
1096
1140
  const direction = this.verifierDirection[key];
1097
- const fullSearch = this.verifierFullSearch[key];
1141
+ const fullScan = this.verifierFullScan[key];
1098
1142
  const comparator = this.verifierMap[key];
1099
- const pairs = await this.getPairs(value, startNode, fullSearch, comparator, direction);
1143
+ const pairs = await this.getPairs(value, startNode, fullScan, comparator, direction);
1100
1144
  if (result === null) {
1101
1145
  result = pairs.map((pair) => pair.key);
1102
1146
  } else {
@@ -1112,9 +1156,9 @@ var BPTreeAsync = class extends BPTree {
1112
1156
  const value = condition[key];
1113
1157
  const startNode = await this.verifierStartNode[key](value);
1114
1158
  const direction = this.verifierDirection[key];
1115
- const fullSearch = this.verifierFullSearch[key];
1159
+ const fullScan = this.verifierFullScan[key];
1116
1160
  const comparator = this.verifierMap[key];
1117
- const pairs = await this.getPairs(value, startNode, fullSearch, comparator, direction);
1161
+ const pairs = await this.getPairs(value, startNode, fullScan, comparator, direction);
1118
1162
  if (result === null) {
1119
1163
  result = pairs;
1120
1164
  } else {
@@ -1200,13 +1244,14 @@ var BPTreeAsync = class extends BPTree {
1200
1244
  this.bufferForHeadUpdate(this.headState);
1201
1245
  await this.commitHeadBuffer();
1202
1246
  }
1203
- async forceUpdate(nodeId = null) {
1204
- const ids = nodeId === null ? [...this.nodes.keys()] : [nodeId];
1205
- for (const id of ids) {
1206
- this.nodes.delete(id);
1207
- await this.getNode(id);
1247
+ async forceUpdate() {
1248
+ const keys = [...this.nodes.keys()];
1249
+ this.nodes.clear();
1250
+ await this.init();
1251
+ for (const key of keys) {
1252
+ await this.getNode(key);
1208
1253
  }
1209
- return ids.length;
1254
+ return keys.length;
1210
1255
  }
1211
1256
  };
1212
1257
 
@@ -1281,29 +1326,6 @@ var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
1281
1326
  this.data.head = head;
1282
1327
  }
1283
1328
  };
1284
-
1285
- // src/ValueComparator.ts
1286
- var ValueComparator = class {
1287
- isLower(value, than) {
1288
- return this.asc(value, than) < 0;
1289
- }
1290
- isSame(value, than) {
1291
- return this.asc(value, than) === 0;
1292
- }
1293
- isHigher(value, than) {
1294
- return this.asc(value, than) > 0;
1295
- }
1296
- };
1297
- var NumericComparator = class extends ValueComparator {
1298
- asc(a, b) {
1299
- return a - b;
1300
- }
1301
- };
1302
- var StringComparator = class extends ValueComparator {
1303
- asc(a, b) {
1304
- return a.localeCompare(b);
1305
- }
1306
- };
1307
1329
  export {
1308
1330
  BPTreeAsync,
1309
1331
  BPTreeSync,
@@ -1,7 +1,7 @@
1
1
  import type { Json } from './utils/types';
2
- import { BPTree, BPTreeLeafNode, BPTreePair, BPTreeNodeKey, BPTreeUnknownNode } from './base/BPTree';
2
+ import { BPTree, BPTreeCondition, BPTreeLeafNode, BPTreePair, BPTreeNodeKey, BPTreeUnknownNode } from './base/BPTree';
3
3
  import { SerializeStrategyAsync } from './SerializeStrategyAsync';
4
- import { ValueComparator } from './ValueComparator';
4
+ import { ValueComparator } from './base/ValueComparator';
5
5
  export declare class BPTreeAsync<K, V> extends BPTree<K, V> {
6
6
  protected readonly strategy: SerializeStrategyAsync<K, V>;
7
7
  constructor(strategy: SerializeStrategyAsync<K, V>, comparator: ValueComparator<V>);
@@ -19,27 +19,11 @@ export declare class BPTreeAsync<K, V> extends BPTree<K, V> {
19
19
  protected commitHeadBuffer(): Promise<void>;
20
20
  protected commitNodeCreateBuffer(): Promise<void>;
21
21
  protected commitNodeUpdateBuffer(): Promise<void>;
22
- keys(condition: Partial<{
23
- gt: V;
24
- lt: V;
25
- gte: V;
26
- lte: V;
27
- equal: V;
28
- notEqual: V;
29
- like: V;
30
- }>): Promise<Set<K>>;
31
- where(condition: Partial<{
32
- gt: V;
33
- lt: V;
34
- gte: V;
35
- lte: V;
36
- equal: V;
37
- notEqual: V;
38
- like: V;
39
- }>): Promise<BPTreePair<K, V>[]>;
22
+ keys(condition: BPTreeCondition<V>): Promise<Set<K>>;
23
+ where(condition: BPTreeCondition<V>): Promise<BPTreePair<K, V>[]>;
40
24
  insert(key: K, value: V): Promise<void>;
41
25
  delete(key: K, value: V): Promise<void>;
42
26
  exists(key: K, value: V): Promise<boolean>;
43
27
  setHeadData(data: Record<string, Json>): Promise<void>;
44
- forceUpdate(nodeId?: number | null): Promise<number>;
28
+ forceUpdate(): Promise<number>;
45
29
  }
@@ -1,7 +1,7 @@
1
1
  import type { Json } from './utils/types';
2
- import { BPTree, BPTreeLeafNode, BPTreePair, BPTreeNodeKey, BPTreeUnknownNode } from './base/BPTree';
2
+ import { BPTree, BPTreeCondition, BPTreeLeafNode, BPTreePair, BPTreeNodeKey, BPTreeUnknownNode } from './base/BPTree';
3
3
  import { SerializeStrategySync } from './SerializeStrategySync';
4
- import { ValueComparator } from './ValueComparator';
4
+ import { ValueComparator } from './base/ValueComparator';
5
5
  export declare class BPTreeSync<K, V> extends BPTree<K, V> {
6
6
  protected readonly strategy: SerializeStrategySync<K, V>;
7
7
  constructor(strategy: SerializeStrategySync<K, V>, comparator: ValueComparator<V>);
@@ -20,27 +20,11 @@ export declare class BPTreeSync<K, V> extends BPTree<K, V> {
20
20
  protected commitHeadBuffer(): void;
21
21
  protected commitNodeCreateBuffer(): void;
22
22
  protected commitNodeUpdateBuffer(): void;
23
- keys(condition: Partial<{
24
- gt: V;
25
- lt: V;
26
- gte: V;
27
- lte: V;
28
- equal: V;
29
- notEqual: V;
30
- like: V;
31
- }>): Set<K>;
32
- where(condition: Partial<{
33
- gt: V;
34
- lt: V;
35
- gte: V;
36
- lte: V;
37
- equal: V;
38
- notEqual: V;
39
- like: V;
40
- }>): BPTreePair<K, V>[];
23
+ keys(condition: BPTreeCondition<V>): Set<K>;
24
+ where(condition: BPTreeCondition<V>): BPTreePair<K, V>[];
41
25
  insert(key: K, value: V): void;
42
26
  delete(key: K, value: V): void;
43
27
  exists(key: K, value: V): boolean;
44
28
  setHeadData(data: Record<string, Json>): void;
45
- forceUpdate(nodeId?: number | null): number;
29
+ forceUpdate(): number;
46
30
  }
@@ -1,5 +1,5 @@
1
1
  import type { Json } from '../utils/types';
2
- import { ValueComparator } from '../ValueComparator';
2
+ import { ValueComparator } from './ValueComparator';
3
3
  import { SerializeStrategy, SerializeStrategyHead } from './SerializeStrategy';
4
4
  type Sync<T> = T;
5
5
  type Async<T> = Promise<T>;
@@ -7,19 +7,19 @@ type Deferred<T> = Sync<T> | Async<T>;
7
7
  export type BPTreeNodeKey<K> = number | K;
8
8
  export type BPTreeCondition<V> = Partial<{
9
9
  /** Searches for pairs greater than the given value. */
10
- gt: V;
10
+ gt: Partial<V>;
11
11
  /** Searches for pairs less than the given value. */
12
- lt: V;
12
+ lt: Partial<V>;
13
13
  /** Searches for pairs greater than or equal to the given value. */
14
- gte: V;
14
+ gte: Partial<V>;
15
15
  /** Searches for pairs less than or equal to the given value. */
16
- lte: V;
16
+ lte: Partial<V>;
17
17
  /** "Searches for pairs equal to the given value. */
18
- equal: V;
18
+ equal: Partial<V>;
19
19
  /** Searches for pairs not equal to the given value. */
20
- notEqual: V;
20
+ notEqual: Partial<V>;
21
21
  /** Searches for values matching the given pattern. '%' matches zero or more characters, and '_' matches exactly one character. */
22
- like: V;
22
+ like: Partial<V>;
23
23
  }>;
24
24
  export type BPTreePair<K, V> = {
25
25
  key: K;
@@ -44,6 +44,7 @@ export interface BPTreeLeafNode<K, V> extends BPTreeNode<K, V> {
44
44
  keys: K[][];
45
45
  }
46
46
  export declare abstract class BPTree<K, V> {
47
+ private readonly _regexpCache;
47
48
  protected readonly strategy: SerializeStrategy<K, V>;
48
49
  protected readonly comparator: ValueComparator<V>;
49
50
  protected readonly nodes: Map<number, BPTreeUnknownNode<K, V>>;
@@ -56,7 +57,7 @@ export declare abstract class BPTree<K, V> {
56
57
  protected readonly verifierMap: Record<keyof BPTreeCondition<V>, (nodeValue: V, value: V) => boolean>;
57
58
  protected readonly verifierStartNode: Record<keyof BPTreeCondition<V>, (value: V) => Deferred<BPTreeLeafNode<K, V>>>;
58
59
  protected readonly verifierDirection: Record<keyof BPTreeCondition<V>, -1 | 1>;
59
- protected readonly verifierFullSearch: Record<keyof BPTreeCondition<V>, boolean>;
60
+ protected readonly verifierFullScan: Record<keyof BPTreeCondition<V>, boolean>;
60
61
  protected get headState(): SerializeStrategyHead;
61
62
  protected constructor(strategy: SerializeStrategy<K, V>, comparator: ValueComparator<V>);
62
63
  protected abstract _getPairsRightToLeft(value: V, startNode: BPTreeLeafNode<K, V>, fullSearch: boolean, comparator: (nodeValue: V, value: V) => boolean): Deferred<BPTreePair<K, V>[]>;
@@ -120,7 +121,6 @@ export declare abstract class BPTree<K, V> {
120
121
  /**
121
122
  * This method deletes nodes cached in-memory and caches new nodes from the stored nodes.
122
123
  * Typically, there's no need to use this method, but it can be used to synchronize data in scenarios where the remote storage and the client are in a 1:n relationship.
123
- * @param nodeId The node ID to update. If no parameters are passed, it updates all currently cached nodes.
124
124
  * @returns The return value is the total number of nodes updated.
125
125
  */
126
126
  abstract forceUpdate(nodeId: number): Deferred<number>;
@@ -0,0 +1,56 @@
1
+ export declare abstract class ValueComparator<V> {
2
+ /**
3
+ * Implement an algorithm that sorts values in ascending order.
4
+ * If it returns a negative number, a is less than b. If it returns 0, the two values are equal. If it returns a positive number, a is greater than b.
5
+ * @param a Value a.
6
+ * @param b Value b.
7
+ */
8
+ abstract asc(a: V, b: V): number;
9
+ /**
10
+ * The `match` method is used for the **LIKE** operator.
11
+ * This method specifies which value to test against a regular expression.
12
+ *
13
+ * For example, if you have a tree with values of the structure `{ country: string, capital: number }`,
14
+ * and you want to perform a **LIKE** operation based on the **capital** value, the method should return **value.capital**.
15
+ * In this case, you **CANNOT** perform a **LIKE** operation based on the **country** attribute.
16
+ * The returned value must be a string.
17
+ *
18
+ * ```
19
+ * interface MyObject {
20
+ * country: string
21
+ * capital: string
22
+ * }
23
+ *
24
+ * class CompositeComparator extends ValueComparator<MyObject> {
25
+ * match(value: MyObject): string {
26
+ * return value.capital
27
+ * }
28
+ * }
29
+ * ```
30
+ *
31
+ * For a tree with simple structure, without complex nesting, returning the value directly would be sufficient.
32
+ *
33
+ * ```
34
+ * class StringComparator extends ValueComparator<string> {
35
+ * match(value: string): string {
36
+ * return value
37
+ * }
38
+ * }
39
+ * ```
40
+ *
41
+ * @param value The inserted value.
42
+ * @returns The value to test against a regular expression.
43
+ */
44
+ abstract match(value: V): string;
45
+ isLower(value: V, than: V): boolean;
46
+ isSame(value: V, than: V): boolean;
47
+ isHigher(value: V, than: V): boolean;
48
+ }
49
+ export declare class NumericComparator extends ValueComparator<number> {
50
+ asc(a: number, b: number): number;
51
+ match(value: number): string;
52
+ }
53
+ export declare class StringComparator extends ValueComparator<string> {
54
+ asc(a: string, b: string): number;
55
+ match(value: string): string;
56
+ }
@@ -1,7 +1,7 @@
1
1
  export type { BPTreeNode } from './base/BPTree';
2
2
  export type { SerializeStrategyHead } from './base/SerializeStrategy';
3
+ export { ValueComparator, NumericComparator, StringComparator } from './base/ValueComparator';
3
4
  export { BPTreeSync } from './BPTreeSync';
4
5
  export { BPTreeAsync } from './BPTreeAsync';
5
6
  export { SerializeStrategySync, InMemoryStoreStrategySync } from './SerializeStrategySync';
6
7
  export { SerializeStrategyAsync, InMemoryStoreStrategyAsync } from './SerializeStrategyAsync';
7
- export { ValueComparator, NumericComparator, StringComparator } from './ValueComparator';
@@ -0,0 +1,3 @@
1
+ export declare class CacheStorage<K, V> extends Map<K, V> {
2
+ ensure(key: K, generator: () => V): V;
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serializable-bptree",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Store the B+tree flexibly, not only in-memory.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -1,18 +0,0 @@
1
- export declare abstract class ValueComparator<V> {
2
- /**
3
- * Implement an algorithm that sorts values in ascending order.
4
- * If it returns a negative number, a is less than b. If it returns 0, the two values are equal. If it returns a positive number, a is greater than b.
5
- * @param a Value a.
6
- * @param b Value b.
7
- */
8
- abstract asc(a: V, b: V): number;
9
- isLower(value: V, than: V): boolean;
10
- isSame(value: V, than: V): boolean;
11
- isHigher(value: V, than: V): boolean;
12
- }
13
- export declare class NumericComparator extends ValueComparator<number> {
14
- asc(a: number, b: number): number;
15
- }
16
- export declare class StringComparator extends ValueComparator<string> {
17
- asc(a: string, b: string): number;
18
- }