serializable-bptree 1.0.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 +237 -0
- package/dist/cjs/index.js +764 -0
- package/dist/esm/index.js +741 -0
- package/dist/typings/BPTree.d.ts +91 -0
- package/dist/typings/SerializeStrategy.d.ts +55 -0
- package/dist/typings/ValueComparator.d.ts +18 -0
- package/dist/typings/index.d.ts +3 -0
- package/dist/typings/utils/BinarySearch.d.ts +9 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# serializable-bptree
|
|
2
|
+
|
|
3
|
+
[](https://www.jsdelivr.com/package/npm/serialiable-bptree)
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
This is a B+tree that's totally okay with duplicate values. If you need to keep track of the B+ tree's state, don't just leave it in memory - make sure you write it down.
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
|
10
|
+
import { BPTree, SerializeStrategy, NumericComparator } from 'serializable-bptree'
|
|
11
|
+
|
|
12
|
+
class FileStoreStrategy extends SerializeStrategy<K, V> {
|
|
13
|
+
id(): number {
|
|
14
|
+
const random = Math.ceil(Math.random()*1000000)
|
|
15
|
+
return random
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
read(id: number): BPTreeNode<K, V> {
|
|
19
|
+
const raw = readFileSync(id.toString(), 'utf8')
|
|
20
|
+
return JSON.parse(raw)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
write(id: number, node: BPTreeNode<K, V>): void {
|
|
24
|
+
const stringify = JSON.stringify(node)
|
|
25
|
+
writeFileSync(id.toString(), stringify, 'utf8')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
readHead(): SerializeStrategyHead|null {
|
|
29
|
+
if (!existsSync('head')) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
const raw = readFileSync('head', 'utf8')
|
|
33
|
+
return JSON.parse(raw)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
writeHead(head: SerializeStrategyHead): void {
|
|
37
|
+
const stringify = JSON.stringify(head)
|
|
38
|
+
writeFileSync('head', stringify, 'utf8')
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const order = 5
|
|
43
|
+
const tree = new BPTree(
|
|
44
|
+
new SerializeStrategy(order),
|
|
45
|
+
new NumericComparator()
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
tree.insert('a', 1)
|
|
49
|
+
tree.insert('b', 2)
|
|
50
|
+
tree.insert('c', 3)
|
|
51
|
+
|
|
52
|
+
tree.delete('b', 2)
|
|
53
|
+
|
|
54
|
+
tree.where({ equal: 1 }) // [{ key: 'a', value: 1 }]
|
|
55
|
+
tree.where({ gt: 1 }) // [{ key: 'c', value: 3 }]
|
|
56
|
+
tree.where({ lt: 2 }) // [{ key: 'a', value: 1 }]
|
|
57
|
+
tree.where({ gt: 0, lt: 4 }) // [{ key: 'a', value: 1 }, { key: 'c', value: 3 }]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Why use a `serializable-bptree`?
|
|
61
|
+
|
|
62
|
+
Firstly, in most cases, there is no need to use a B+tree in JavaScript. This is because there is a great alternative, the Map object. Nonetheless, if you need to retrieve values in a sorted order, a B+tree can be a good solution. These cases are often related to databases, and you may want to store this state not just in memory, but on a remote server or in a file. In this case, `serializable-bptree` can help you.
|
|
63
|
+
|
|
64
|
+
## How to use
|
|
65
|
+
|
|
66
|
+
### Node.js (cjs)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm i serializable-bptree
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { BPTree } from 'serializable-bptree'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Browser (esm)
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<script type="module">
|
|
80
|
+
import { BPTree } from 'https://cdn.jsdelivr.net/npm/serializable-bptree@1.x.x/dist/esm/index.min.js'
|
|
81
|
+
</script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## conceptualization
|
|
85
|
+
|
|
86
|
+
### Value comparator
|
|
87
|
+
|
|
88
|
+
B+tree needs to keep values in sorted order. Therefore, a process to compare the sizes of values is needed, and that role is played by the `ValueComparator`.
|
|
89
|
+
|
|
90
|
+
Commonly used numerical and string comparisons are natively supported by the `serializable-bptree` library. Use it as follows:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { NumericComparator, StringComparator } from 'serializable-bptree'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
However, you may want to sort complex objects other than numbers and strings. For example, if you want to sort by the `age` property order of an object, you need to create a new class that inherits from the `ValueComparator` class. Use it as follows:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { ValueComparator } from 'serializable-bptree'
|
|
100
|
+
|
|
101
|
+
interface MyObject {
|
|
102
|
+
age: number
|
|
103
|
+
name: string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class AgeComparator {
|
|
107
|
+
asc(a: MyObject, b: MyObject): number {
|
|
108
|
+
return a.age - b.age
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Serialize strategy
|
|
114
|
+
|
|
115
|
+
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.
|
|
116
|
+
|
|
117
|
+
You need to construct a logic for input/output from the file by inheriting the SerializeStrategy class. Look at the class structure below:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { SerializeStrategy } from 'serializable-bptree'
|
|
121
|
+
|
|
122
|
+
class MyFileIOStrategy extends SerializeStrategy {
|
|
123
|
+
id(): number
|
|
124
|
+
read(id: number): BPTreeNode<K, V>
|
|
125
|
+
write(id: number, node: BPTreeNode<K, V>): void
|
|
126
|
+
readHead(): SerializeStrategyHead|null
|
|
127
|
+
writeHead(head: SerializeStrategyHead): void
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
What does this method mean? And why do we need to construct such a method?
|
|
132
|
+
|
|
133
|
+
#### id(): `number`
|
|
134
|
+
|
|
135
|
+
When a node is created in the B+tree, the node needs a unique value to represent itself. This is the `node.id` attribute, and you can specify this attribute yourself. For example, it could be implemented like this.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
id(): number {
|
|
139
|
+
const current = before + 1
|
|
140
|
+
before = current
|
|
141
|
+
return current
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Or, you could use file input/output to save and load the value of the `before` variable.
|
|
146
|
+
|
|
147
|
+
#### read(id: `number`): `BPTreeNode<K, V>`
|
|
148
|
+
|
|
149
|
+
This is a method to load the saved value as a tree instance. If you have previously saved the node as a file, you should use this method to convert it back to JavaScript JSON format and return it.
|
|
150
|
+
|
|
151
|
+
Please refer to the example below:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
read(id: number): BPTreeNode<K, V> {
|
|
155
|
+
const filePath = `./my-store/${id.toString()}`
|
|
156
|
+
const raw = fs.readFileSync(filePath, 'utf8')
|
|
157
|
+
return JSON.parse(raw)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This method is called only once when loading a node from a tree instance.
|
|
162
|
+
|
|
163
|
+
#### write(id: `number`, node: `BPTreeNode<K, V>`): `void`
|
|
164
|
+
|
|
165
|
+
This method is called when there are changes in the internal nodes due to the insert or delete operations of the tree instance. In other words, it's a necessary method for synchronizing the in-memory nodes into a file.
|
|
166
|
+
|
|
167
|
+
Since this method is called frequently, be mindful of performance. There are ways to optimize it using a write-back caching technique.
|
|
168
|
+
|
|
169
|
+
Please refer to the example below:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
let queue = 0
|
|
173
|
+
function writeBack(id: number, node: BPTreeNode<K, V>, timer: number) {
|
|
174
|
+
clearTimeout(queue)
|
|
175
|
+
queue = setTimeout(() => {
|
|
176
|
+
const filePath = `./my-store/${id.toString()}`
|
|
177
|
+
const stringify = JSON.stringify(node)
|
|
178
|
+
writeFileSync(filePath, stringify, 'utf8')
|
|
179
|
+
}, timer)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
...
|
|
183
|
+
write(id: number, node: BPTreeNode<K, V>): void {
|
|
184
|
+
const writeBackInterval = 100
|
|
185
|
+
writeBack(id, node, writeBackInterval)
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
This kind of delay writing should ideally occur within a few milliseconds. If this is not feasible, consider other approaches.
|
|
190
|
+
|
|
191
|
+
#### readHead(): `SerializeStrategyHead`|`null`
|
|
192
|
+
|
|
193
|
+
This method is called only once when the tree is created. It's a method to restore the saved tree information. If it's the first tree creation and there are no saved tree information, return `null`.
|
|
194
|
+
|
|
195
|
+
This method should return the value stored in the `writeHead` method.
|
|
196
|
+
|
|
197
|
+
#### writeHead(head: `SerializeStrategyHead`): `void`
|
|
198
|
+
|
|
199
|
+
This method is called whenever the head information of the tree changes, typically when the root node changes.
|
|
200
|
+
|
|
201
|
+
As a parameter, it receives the header information of the tree. This value should be serialized and stored. Later, the `readHead` method should convert this serialized value into a json format and return it.
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
### The Default `ValueComparator` and `SerializeStrategy`
|
|
205
|
+
|
|
206
|
+
To utilize `serializable-bptree`, you need to implement certain functions. However, a few basic helper classes are provided by default.
|
|
207
|
+
|
|
208
|
+
#### ValueComparator
|
|
209
|
+
|
|
210
|
+
* `NumericComparator`
|
|
211
|
+
* `StringComparator`
|
|
212
|
+
|
|
213
|
+
If the values being inserted into the tree are numeric, please use the `NumericComparator` class.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { NumericComparator } from 'serializable-bptree'
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
If the values being inserted into the tree can be strings, you can use the `StringComparator` class in this case.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { StringComparator } from 'serializable-bptree'
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### SerializeStrategy
|
|
226
|
+
|
|
227
|
+
* `InMemoryStoreStrategy`
|
|
228
|
+
|
|
229
|
+
As of now, the only class supported by default is the `InMemoryStoreStrategy`. This class is suitable for use when you prefer to operate the tree solely in-memory, similar to a typical B+ tree.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { InMemoryStoreStrategy } from 'serializable-bptree'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## LICENSE
|
|
236
|
+
|
|
237
|
+
MIT LICENSE
|