jsql-neo 1.2.1 → 2.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 ADDED
@@ -0,0 +1,33 @@
1
+ ```
2
+
3
+
4
+ JJJJJJJJJJJ SSSSSSSSSSSSSSS QQQQQQQQQ LLLLLLLLLLL
5
+ J:::::::::J SS:::::::::::::::S QQ:::::::::QQ L:::::::::L
6
+ J:::::::::JS:::::SSSSSS::::::S QQ:::::::::::::QQ L:::::::::L
7
+ JJ:::::::JJS:::::S SSSSSSSQ:::::::QQQ:::::::QLL:::::::LL
8
+ J:::::J S:::::S Q::::::O Q::::::Q L:::::L nnnn nnnnnnnn eeeeeeeeeeee ooooooooooo
9
+ J:::::J S:::::S Q:::::O Q:::::Q L:::::L n:::nn::::::::nn ee::::::::::::ee oo:::::::::::oo
10
+ J:::::J S::::SSSS Q:::::O Q:::::Q L:::::L n::::::::::::::nn e::::::eeeee:::::eeo:::::::::::::::o
11
+ J:::::j SS::::::SSSSS Q:::::O Q:::::Q L:::::L --------------- nn:::::::::::::::ne::::::e e:::::eo:::::ooooo:::::o
12
+ J:::::J SSS::::::::SS Q:::::O Q:::::Q L:::::L -:::::::::::::- n:::::nnnn:::::ne:::::::eeeee::::::eo::::o o::::o
13
+ JJJJJJJ J:::::J SSSSSS::::S Q:::::O Q:::::Q L:::::L --------------- n::::n n::::ne:::::::::::::::::e o::::o o::::o
14
+ J:::::J J:::::J S:::::SQ:::::O QQQQ:::::Q L:::::L n::::n n::::ne::::::eeeeeeeeeee o::::o o::::o
15
+ J::::::J J::::::J S:::::SQ::::::O Q::::::::Q L:::::L LLLLLL n::::n n::::ne:::::::e o::::o o::::o
16
+ J:::::::JJJ:::::::J SSSSSSS S:::::SQ:::::::QQ::::::::QLL:::::::LLLLLLLLL:::::L n::::n n::::ne::::::::e o:::::ooooo:::::o
17
+ JJ:::::::::::::JJ S::::::SSSSSS:::::S QQ::::::::::::::Q L::::::::::::::::::::::L n::::n n::::n e::::::::eeeeeeee o:::::::::::::::o
18
+ JJ:::::::::JJ S:::::::::::::::SS QQ:::::::::::Q L::::::::::::::::::::::L n::::n n::::n ee:::::::::::::e oo:::::::::::oo
19
+ JJJJJJJJJ SSSSSSSSSSSSSSS QQQQQQQQ::::QQLLLLLLLLLLLLLLLLLLLLLLLL nnnnnn nnnnnn eeeeeeeeeeeeee ooooooooooo
20
+ Q:::::Q
21
+ QQQQQQ
22
+
23
+
24
+
25
+
26
+
27
+ ```
28
+ ```
29
+ .----. .----. .-. . .-..----..----. .----..-. .-. .-. .-..----..-. .-..-..----..-. .-.
30
+ | {} }/ {} \| |/ \| || {_ | {} } | {} }\ \/ / | | | || {_ \ \/ / | || {_ \ \/ /
31
+ | .--' \ /| .'. || {__ | .-. \ | {} } } { \ \_/ /| {__ / /\ \ | || | } {
32
+ `-' `----' `-' `-'`----'`-' `-' `----' `--' `---' `----'`-' `-'`-'`-' `--'
33
+ ```
package/index.js CHANGED
@@ -1,26 +1,18 @@
1
1
  // © Vexify 2026 All Rights Reserved.
2
2
  /**
3
- * JSQL — Pure JavaScript Embedded Database
3
+ * JSQL-NEO — Pure JavaScript Embedded Database
4
+ * v2.0.0 — B-Tree 索引、WAL、哈希 JOIN、错误码体系、事务隔离
4
5
  *
5
6
  * @example
6
7
  * const jsql = require('jsql-neo');
7
- * const db = new jsql.Database('mydb.json');
8
- *
9
- * db.createTable('users', {
10
- * id: { type: 'integer', primaryKey: true, autoIncrement: true },
11
- * name: { type: 'string', required: true },
12
- * age: { type: 'integer' }
13
- * });
14
- *
15
- * db.users.insert({ name: 'Alice', age: 25 });
16
- * db.users.insert({ name: 'Bob', age: 30 });
17
- *
18
- * const adults = db.users.find({ age: { $gte: 18 } });
19
- * const sorted = db.users.where({ age: { $gte: 18 } }).orderBy('age', 'desc').limit(10).get();
8
+ * const db = new jsql.Database('mydb.json', { wal: true, fileLock: true });
20
9
  */
21
10
 
22
11
  module.exports = {
23
12
  Database: require('./lib/database'),
24
13
  Table: require('./lib/table'),
25
- Query: require('./lib/query')
14
+ Query: require('./lib/query'),
15
+ BTree: require('./lib/btree'),
16
+ JSQL_Error: require('./lib/errors').JSQL_Error,
17
+ ErrorCodes: require('./lib/errors').ErrorCodes
26
18
  };
package/lib/btree.js ADDED
@@ -0,0 +1,326 @@
1
+ // © Vexify 2026 All Rights Reserved.
2
+ /**
3
+ * JSQL B-Tree 索引引擎
4
+ * O(log n) 查找/插入/删除,支持范围查询和复合键
5
+ */
6
+
7
+ class BTreeNode {
8
+ constructor(leaf = true) {
9
+ this.keys = []; // 索引键值
10
+ this.values = []; // 对应的行号数组
11
+ this.children = []; // 子节点
12
+ this.leaf = leaf;
13
+ this.next = null; // 叶子节点链表(范围查询用)
14
+ }
15
+ }
16
+
17
+ class BTree {
18
+ /**
19
+ * @param {number} order - B-Tree 阶数,默认 64
20
+ * @param {boolean} unique - 是否唯一索引
21
+ */
22
+ constructor(order = 64, unique = false) {
23
+ this._order = order;
24
+ this._unique = unique;
25
+ this._root = new BTreeNode();
26
+ this._size = 0;
27
+ this._minDegree = Math.ceil(order / 2);
28
+ }
29
+
30
+ get size() { return this._size; }
31
+
32
+ // ============================================================
33
+ // 插入
34
+ // ============================================================
35
+
36
+ insert(key, rowIndex) {
37
+ const root = this._root;
38
+
39
+ // 根节点满 → 分裂
40
+ if (root.keys.length === this._order - 1) {
41
+ const newRoot = new BTreeNode(false);
42
+ newRoot.children.push(root);
43
+ this._splitChild(newRoot, 0);
44
+ this._root = newRoot;
45
+ this._insertNonFull(newRoot, key, rowIndex);
46
+ } else {
47
+ this._insertNonFull(root, key, rowIndex);
48
+ }
49
+ this._size++;
50
+ }
51
+
52
+ _insertNonFull(node, key, rowIndex) {
53
+ let i = node.keys.length - 1;
54
+
55
+ if (node.leaf) {
56
+ // 查找插入位置
57
+ while (i >= 0 && key < node.keys[i]) i--;
58
+ i++;
59
+
60
+ if (this._unique && i > 0 && node.keys[i - 1] === key) {
61
+ // 唯一索引:追加行号
62
+ if (!node.values[i - 1].includes(rowIndex)) {
63
+ node.values[i - 1].push(rowIndex);
64
+ }
65
+ this._size--;
66
+ return;
67
+ }
68
+
69
+ node.keys.splice(i, 0, key);
70
+ node.values.splice(i, 0, [rowIndex]);
71
+ } else {
72
+ while (i >= 0 && key < node.keys[i]) i--;
73
+ i++;
74
+
75
+ if (node.children[i].keys.length === this._order - 1) {
76
+ this._splitChild(node, i);
77
+ if (key > node.keys[i]) i++;
78
+ }
79
+ this._insertNonFull(node.children[i], key, rowIndex);
80
+ }
81
+ }
82
+
83
+ _splitChild(parent, index) {
84
+ const child = parent.children[index];
85
+ const newChild = new BTreeNode(child.leaf);
86
+ const mid = Math.floor((this._order - 1) / 2);
87
+
88
+ // 右半部分移到新节点
89
+ newChild.keys = child.keys.splice(mid + 1);
90
+ newChild.values = child.values.splice(mid + 1);
91
+
92
+ // 中间键提升到父节点
93
+ const midKey = child.keys[mid];
94
+ const midVal = child.values[mid];
95
+
96
+ // 非叶子节点:移动子节点
97
+ if (!child.leaf) {
98
+ newChild.children = child.children.splice(mid + 1);
99
+ }
100
+
101
+ // 叶子节点:维护链表
102
+ if (child.leaf) {
103
+ newChild.next = child.next;
104
+ child.next = newChild;
105
+ }
106
+
107
+ parent.keys.splice(index, 0, midKey);
108
+ parent.values.splice(index, 0, midVal);
109
+ parent.children.splice(index + 1, 0, newChild);
110
+ }
111
+
112
+ // ============================================================
113
+ // 查找
114
+ // ============================================================
115
+
116
+ /**
117
+ * 精确查找
118
+ * @returns {number[]} 行号数组
119
+ */
120
+ search(key) {
121
+ let node = this._root;
122
+ while (node) {
123
+ let i = 0;
124
+ while (i < node.keys.length && key > node.keys[i]) i++;
125
+
126
+ if (i < node.keys.length && key === node.keys[i]) {
127
+ return node.values[i];
128
+ }
129
+
130
+ if (node.leaf) break;
131
+ node = node.children[i];
132
+ }
133
+ return [];
134
+ }
135
+
136
+ /**
137
+ * 范围查询 [min, max]
138
+ */
139
+ range(min, max) {
140
+ const result = [];
141
+ let node = this._findLeaf(min);
142
+
143
+ while (node) {
144
+ for (let i = 0; i < node.keys.length; i++) {
145
+ const k = node.keys[i];
146
+ if (k > max) return result;
147
+ if (k >= min) {
148
+ result.push(...node.values[i]);
149
+ }
150
+ }
151
+ node = node.next;
152
+ }
153
+ return result;
154
+ }
155
+
156
+ /**
157
+ * 大于查询
158
+ */
159
+ greaterThan(min) {
160
+ return this.range(min, Infinity);
161
+ }
162
+
163
+ /**
164
+ * 小于查询
165
+ */
166
+ lessThan(max) {
167
+ return this.range(-Infinity, max);
168
+ }
169
+
170
+ _findLeaf(key) {
171
+ let node = this._root;
172
+ while (!node.leaf) {
173
+ let i = 0;
174
+ while (i < node.keys.length && key > node.keys[i]) i++;
175
+ node = node.children[i];
176
+ }
177
+ return node;
178
+ }
179
+
180
+ // ============================================================
181
+ // 删除
182
+ // ============================================================
183
+
184
+ remove(key, rowIndex) {
185
+ if (!this._root) return false;
186
+ const removed = this._removeFromNode(this._root, key, rowIndex);
187
+ if (removed) this._size--;
188
+ if (this._root.keys.length === 0 && !this._root.leaf && this._root.children.length > 0) {
189
+ this._root = this._root.children[0];
190
+ }
191
+ return removed;
192
+ }
193
+
194
+ _removeFromNode(node, key, rowIndex) {
195
+ let i = 0;
196
+ while (i < node.keys.length && key > node.keys[i]) i++;
197
+
198
+ if (node.leaf) {
199
+ if (i < node.keys.length && node.keys[i] === key) {
200
+ const vals = node.values[i].filter(v => v !== rowIndex);
201
+ if (vals.length === 0) {
202
+ node.keys.splice(i, 1);
203
+ node.values.splice(i, 1);
204
+ } else {
205
+ node.values[i] = vals;
206
+ }
207
+ return true;
208
+ }
209
+ return false;
210
+ }
211
+
212
+ if (i < node.keys.length && node.keys[i] === key) {
213
+ return this._removeFromInternal(node, i, rowIndex);
214
+ }
215
+
216
+ return this._removeFromNode(node.children[i], key, rowIndex);
217
+ }
218
+
219
+ _removeFromInternal(node, index, rowIndex) {
220
+ const vals = node.values[index].filter(v => v !== rowIndex);
221
+ if (vals.length > 0) {
222
+ node.values[index] = vals;
223
+ return true;
224
+ }
225
+
226
+ const leftChild = node.children[index];
227
+ if (leftChild.keys.length >= this._minDegree) {
228
+ const pred = this._getMax(leftChild);
229
+ node.keys[index] = pred.key;
230
+ node.values[index] = pred.value;
231
+ this._removeFromNode(leftChild, pred.key, rowIndex);
232
+ return true;
233
+ }
234
+
235
+ const rightChild = node.children[index + 1];
236
+ if (rightChild.keys.length >= this._minDegree) {
237
+ const succ = this._getMin(rightChild);
238
+ node.keys[index] = succ.key;
239
+ node.values[index] = succ.value;
240
+ this._removeFromNode(rightChild, succ.key, rowIndex);
241
+ return true;
242
+ }
243
+
244
+ this._mergeChildren(node, index);
245
+ return this._removeFromNode(node.children[index], node.keys[index], rowIndex);
246
+ }
247
+
248
+ _getMax(node) {
249
+ while (!node.leaf) node = node.children[node.children.length - 1];
250
+ const i = node.keys.length - 1;
251
+ return { key: node.keys[i], value: node.values[i] };
252
+ }
253
+
254
+ _getMin(node) {
255
+ while (!node.leaf) node = node.children[0];
256
+ return { key: node.keys[0], value: node.values[0] };
257
+ }
258
+
259
+ _mergeChildren(parent, index) {
260
+ const left = parent.children[index];
261
+ const right = parent.children[index + 1];
262
+ const midKey = parent.keys[index];
263
+ const midVal = parent.values[index];
264
+
265
+ left.keys.push(midKey);
266
+ left.values.push(midVal);
267
+ left.keys.push(...right.keys);
268
+ left.values.push(...right.values);
269
+ if (!left.leaf) left.children.push(...right.children);
270
+ if (left.leaf) left.next = right.next;
271
+
272
+ parent.keys.splice(index, 1);
273
+ parent.values.splice(index, 1);
274
+ parent.children.splice(index + 1, 1);
275
+ }
276
+
277
+ // ============================================================
278
+ // 遍历
279
+ // ============================================================
280
+
281
+ /**
282
+ * 获取所有索引条目
283
+ */
284
+ entries() {
285
+ const result = [];
286
+ this._traverse(this._root, result);
287
+ return result;
288
+ }
289
+
290
+ _traverse(node, result) {
291
+ if (node.leaf) {
292
+ for (let i = 0; i < node.keys.length; i++) {
293
+ result.push({ key: node.keys[i], value: node.values[i] });
294
+ }
295
+ } else {
296
+ for (let i = 0; i < node.keys.length; i++) {
297
+ this._traverse(node.children[i], result);
298
+ result.push({ key: node.keys[i], value: node.values[i] });
299
+ }
300
+ this._traverse(node.children[node.children.length - 1], result);
301
+ }
302
+ }
303
+
304
+ /**
305
+ * 清空索引
306
+ */
307
+ clear() {
308
+ this._root = new BTreeNode();
309
+ this._size = 0;
310
+ }
311
+
312
+ /**
313
+ * 重建索引(从数据行批量构建)
314
+ */
315
+ rebuild(rows, getKey) {
316
+ this.clear();
317
+ for (let i = 0; i < rows.length; i++) {
318
+ const key = getKey(rows[i]);
319
+ if (key !== undefined && key !== null) {
320
+ this.insert(key, i);
321
+ }
322
+ }
323
+ }
324
+ }
325
+
326
+ module.exports = BTree;