jsql-neo 5.5.0 → 6.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.
Files changed (81) hide show
  1. package/LICENSE +202 -202
  2. package/README.md +8155 -8097
  3. package/bin/jsql +320 -320
  4. package/bin/jsql-server +90 -90
  5. package/bin/jsql-server-control.js +177 -177
  6. package/index.d.ts +412 -385
  7. package/index.js +128 -124
  8. package/lib/ast.js +214 -214
  9. package/lib/btree.js +478 -413
  10. package/lib/cache.js +328 -328
  11. package/lib/client.js +125 -125
  12. package/lib/database.js +1741 -1744
  13. package/lib/date-types.js +182 -182
  14. package/lib/errors.js +89 -89
  15. package/lib/jsql_format.js +497 -497
  16. package/lib/migrate.js +254 -254
  17. package/lib/mod.js +292 -292
  18. package/lib/mongo_server.js +607 -607
  19. package/lib/multiserver.js +140 -140
  20. package/lib/mysql_compat.js +413 -413
  21. package/lib/mysql_server.js +1237 -1237
  22. package/lib/native_client.js +722 -732
  23. package/lib/nedb_compat.js +506 -506
  24. package/lib/pg_server.js +864 -864
  25. package/lib/plugin.js +259 -35
  26. package/lib/plugins/audit.js +37 -0
  27. package/lib/plugins/index.js +72 -0
  28. package/lib/plugins/logger.js +35 -0
  29. package/lib/plugins/metrics.js +38 -0
  30. package/lib/plugins/timestamps.js +57 -0
  31. package/lib/plugins/validation.js +128 -0
  32. package/lib/prepared.js +37 -0
  33. package/lib/query.js +834 -815
  34. package/lib/redis_server.js +571 -571
  35. package/lib/sql.js +4053 -3460
  36. package/lib/sqlite_compat.js +323 -323
  37. package/lib/sqlite_worker.js +264 -264
  38. package/lib/table.js +1351 -1304
  39. package/lib/tui.js +502 -502
  40. package/lib/wasm.js +1 -1
  41. package/lib/wasm_client.js +715 -718
  42. package/lib/web_ui.js +257 -257
  43. package/native/jsql-neo-native.node +0 -0
  44. package/nativesrc/jsql-neo-core/Cargo.lock +462 -455
  45. package/nativesrc/jsql-neo-core/Cargo.toml +26 -24
  46. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +492 -455
  47. package/nativesrc/jsql-neo-core/src/engine/memory.rs +191 -168
  48. package/nativesrc/jsql-neo-core/src/engine/mod.rs +74 -62
  49. package/nativesrc/jsql-neo-core/src/engine/table.rs +996 -700
  50. package/nativesrc/jsql-neo-core/src/lib.rs +2 -2
  51. package/nativesrc/jsql-neo-core/src/storage/mod.rs +2 -2
  52. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +1 -1
  53. package/nativesrc/jsql-neo-core/src/storage/wal.rs +298 -85
  54. package/nativesrc/jsql-neo-core/src/types.rs +93 -93
  55. package/nativesrc/jsql-neo-native/Cargo.lock +613 -606
  56. package/nativesrc/jsql-neo-native/Cargo.toml +24 -16
  57. package/nativesrc/jsql-neo-native/build.rs +3 -3
  58. package/nativesrc/jsql-neo-native/package.json +7 -7
  59. package/nativesrc/jsql-neo-native/src/lib.rs +281 -269
  60. package/nativesrc/jsql-neo-wasm/Cargo.lock +472 -465
  61. package/nativesrc/jsql-neo-wasm/Cargo.toml +22 -15
  62. package/nativesrc/jsql-neo-wasm/src/lib.rs +463 -447
  63. package/package.json +87 -87
  64. package/postinstall.js +158 -158
  65. package/test/btree.test.js +144 -128
  66. package/test/coverage.js +187 -187
  67. package/test/join.test.js +110 -110
  68. package/test/native.test.js +158 -158
  69. package/test/query-opt.test.js +109 -0
  70. package/test/regress-5.1.0.js +271 -271
  71. package/test/smoke.js +58 -58
  72. package/test/sql-parser.test.js +450 -273
  73. package/test/wasm.test.js +110 -110
  74. package/wasm/browser.d.ts +89 -89
  75. package/wasm/browser.mjs +591 -569
  76. package/wasm/browser_bg.mjs +618 -597
  77. package/wasm/jsql_neo_wasm.d.ts +44 -38
  78. package/wasm/jsql_neo_wasm.js +641 -520
  79. package/wasm/jsql_neo_wasm_bg.wasm +0 -0
  80. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +26 -26
  81. package/wasm/package.json +11 -11
package/lib/btree.js CHANGED
@@ -1,414 +1,479 @@
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._minKeys = Math.floor(order / 2) - 1;
28
- this._minDegree = Math.ceil(order / 2);
29
- }
30
-
31
- get size() { return this._size; }
32
-
33
- // ============================================================
34
- // 插入
35
- // ============================================================
36
-
37
- insert(key, rowIndex) {
38
- const root = this._root;
39
-
40
- // 根节点满 → 分裂
41
- if (root.keys.length === this._order - 1) {
42
- const newRoot = new BTreeNode(false);
43
- newRoot.children.push(root);
44
- this._splitChild(newRoot, 0);
45
- this._root = newRoot;
46
- this._insertNonFull(newRoot, key, rowIndex);
47
- } else {
48
- this._insertNonFull(root, key, rowIndex);
49
- }
50
- this._size++;
51
- }
52
-
53
- _insertNonFull(node, key, rowIndex) {
54
- if (node.leaf) {
55
- // 查找插入位置
56
- let i = node.keys.length - 1;
57
- while (i >= 0 && key < node.keys[i]) i--;
58
- i++;
59
-
60
- if (i > 0 && node.keys[i - 1] === key) {
61
- if (this._unique) {
62
- this._size--;
63
- return;
64
- }
65
- if (!node.values[i - 1].includes(rowIndex)) {
66
- node.values[i - 1].push(rowIndex);
67
- }
68
- this._size--;
69
- return;
70
- }
71
-
72
- node.keys.splice(i, 0, key);
73
- node.values.splice(i, 0, [rowIndex]);
74
- } else {
75
- let i = this._route(node, key);
76
- if (node.children[i] && node.children[i].keys.length === this._order - 1) {
77
- this._splitChild(node, i);
78
- i = this._route(node, key);
79
- }
80
- this._insertNonFull(node.children[i], key, rowIndex);
81
- }
82
- }
83
-
84
- _splitChild(parent, index) {
85
- const child = parent.children[index];
86
- const newChild = new BTreeNode(child.leaf);
87
- const mid = Math.floor((this._order - 1) / 2);
88
-
89
- const midKey = child.keys[mid];
90
- const midVal = child.values[mid];
91
-
92
- if (child.leaf) {
93
- // 叶子:右半移到新节点;分隔键保留在左叶(它是真实数据键),父节点只存副本
94
- newChild.keys = child.keys.splice(mid + 1);
95
- newChild.values = child.values.splice(mid + 1);
96
- newChild.next = child.next;
97
- child.next = newChild;
98
- } else {
99
- // 内部:分隔键提升到父节点并从子节点移除,保持 children = keys + 1
100
- newChild.keys = child.keys.splice(mid + 1);
101
- newChild.values = child.values.splice(mid + 1);
102
- child.keys.splice(mid, 1);
103
- child.values.splice(mid, 1);
104
- newChild.children = child.children.splice(mid + 1);
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
- * 内部节点路由:返回 key 应下降到的子节点下标。
118
- * 命中分隔键副本时,根据左子树最大键判定真实数据所在子树
119
- * (分隔键副本可能在左子树作为最大值,或在右子树作为最小值)。
120
- */
121
- _route(node, key) {
122
- let i = 0;
123
- while (i < node.keys.length && key > node.keys[i]) i++;
124
- if (i < node.keys.length && node.keys[i] === key) {
125
- return this._getMax(node.children[i]).key === key ? i : i + 1;
126
- }
127
- return i;
128
- }
129
-
130
- /**
131
- * 精确查找(始终下探到叶子取真实数据;内部分隔键只是叶子的副本,仅用于导航)
132
- * @returns {number[]} 行号数组;未找到时返回空数组
133
- */
134
- search(key) {
135
- let node = this._root;
136
- while (!node.leaf) node = node.children[this._route(node, key)];
137
- const i = node.keys.indexOf(key);
138
- return i === -1 ? [] : node.values[i];
139
- }
140
-
141
- /**
142
- * 范围查询 [min, max]
143
- */
144
- range(min, max) {
145
- const result = [];
146
- let node = this._findLeaf(min);
147
-
148
- while (node) {
149
- for (let i = 0; i < node.keys.length; i++) {
150
- const k = node.keys[i];
151
- if (k > max) return result;
152
- if (k >= min) {
153
- result.push(...node.values[i]);
154
- }
155
- }
156
- node = node.next;
157
- }
158
- return result;
159
- }
160
-
161
- /**
162
- * 大于查询(严格开区间,不含 min 本身)
163
- */
164
- greaterThan(min) {
165
- const result = [];
166
- let node = this._findLeaf(min);
167
- while (node) {
168
- for (let i = 0; i < node.keys.length; i++) {
169
- if (node.keys[i] > min) result.push(...node.values[i]);
170
- }
171
- node = node.next;
172
- }
173
- return result;
174
- }
175
-
176
- /**
177
- * 小于查询(严格开区间,不含 max 本身)
178
- */
179
- lessThan(max) {
180
- const result = [];
181
- let node = this._root;
182
- while (!node.leaf) node = node.children[0];
183
- while (node) {
184
- for (let i = 0; i < node.keys.length; i++) {
185
- if (node.keys[i] < max) result.push(...node.values[i]);
186
- else return result;
187
- }
188
- node = node.next;
189
- }
190
- return result;
191
- }
192
-
193
- _findLeaf(key) {
194
- let node = this._root;
195
- while (!node.leaf) {
196
- let i = 0;
197
- while (i < node.keys.length && key > node.keys[i]) i++;
198
- node = node.children[i];
199
- }
200
- return node;
201
- }
202
-
203
- // ============================================================
204
- // 删除
205
- // ============================================================
206
-
207
- remove(key, rowIndex) {
208
- if (!this._root) return false;
209
- const removed = this._removeFromNode(this._root, key, rowIndex);
210
- if (removed) this._size--;
211
- if (this._root.keys.length === 0 && !this._root.leaf && this._root.children.length > 0) {
212
- this._root = this._root.children[0];
213
- }
214
- return removed;
215
- }
216
-
217
- _removeFromNode(node, key, rowIndex) {
218
- if (node.leaf) {
219
- const i = node.keys.indexOf(key);
220
- if (i === -1) return false;
221
- if (!node.values[i].includes(rowIndex)) return false;
222
- const vals = node.values[i].filter(v => v !== rowIndex);
223
- if (vals.length === 0) {
224
- node.keys.splice(i, 1);
225
- node.values.splice(i, 1);
226
- return true;
227
- }
228
- node.values[i] = vals;
229
- return false;
230
- }
231
-
232
- let i = this._route(node, key);
233
-
234
- // 下降前确保子节点有足够键,避免欠满子节点
235
- const child = node.children[i];
236
- if (child.keys.length <= this._minKeys) {
237
- i = this._rebalanceChild(node, i);
238
- }
239
- return this._removeFromNode(node.children[i], key, rowIndex);
240
- }
241
-
242
- _rebalanceChild(parent, i) {
243
- if (i > 0 && parent.children[i - 1].keys.length > this._minKeys) {
244
- this._borrowFromLeft(parent, i);
245
- return i;
246
- }
247
- if (i + 1 < parent.children.length && parent.children[i + 1].keys.length > this._minKeys) {
248
- this._borrowFromRight(parent, i);
249
- return i;
250
- }
251
- if (i > 0) {
252
- this._mergeChildren(parent, i - 1);
253
- return i - 1;
254
- }
255
- this._mergeChildren(parent, i);
256
- return i;
257
- }
258
-
259
- _borrowFromLeft(parent, i) {
260
- const left = parent.children[i - 1];
261
- const right = parent.children[i];
262
- if (right.leaf) {
263
- // 叶子节点:分隔键是叶子的副本,用被移动的键而非旧分隔键,避免重复
264
- const k = left.keys.pop();
265
- const v = left.values.pop();
266
- right.keys.unshift(k);
267
- right.values.unshift(v);
268
- parent.keys[i - 1] = k;
269
- parent.values[i - 1] = v;
270
- } else {
271
- right.keys.unshift(parent.keys[i - 1]);
272
- right.values.unshift(parent.values[i - 1]);
273
- parent.keys[i - 1] = left.keys.pop();
274
- parent.values[i - 1] = left.values.pop();
275
- right.children.unshift(left.children.pop());
276
- }
277
- }
278
-
279
- _borrowFromRight(parent, i) {
280
- const left = parent.children[i];
281
- const right = parent.children[i + 1];
282
- if (left.leaf) {
283
- // 叶子节点:用被移动的键而非旧分隔键,避免重复
284
- const k = right.keys.shift();
285
- const v = right.values.shift();
286
- left.keys.push(k);
287
- left.values.push(v);
288
- parent.keys[i] = k;
289
- parent.values[i] = v;
290
- } else {
291
- left.keys.push(parent.keys[i]);
292
- left.values.push(parent.values[i]);
293
- parent.keys[i] = right.keys.shift();
294
- parent.values[i] = right.values.shift();
295
- left.children.push(right.children.shift());
296
- }
297
- }
298
-
299
- _getMax(node) {
300
- while (!node.leaf) node = node.children[node.children.length - 1];
301
- const i = node.keys.length - 1;
302
- return { key: node.keys[i], value: node.values[i] };
303
- }
304
-
305
- _mergeChildren(parent, index) {
306
- const left = parent.children[index];
307
- const right = parent.children[index + 1];
308
- const midKey = parent.keys[index];
309
- const midVal = parent.values[index];
310
-
311
- // 叶子节点:分隔键是叶子的副本(已在 left 或 right 中),不能重复压入
312
- if (!left.leaf) {
313
- left.keys.push(midKey);
314
- left.values.push(midVal);
315
- }
316
- left.keys.push(...right.keys);
317
- left.values.push(...right.values);
318
- if (!left.leaf) left.children.push(...right.children);
319
- if (left.leaf) left.next = right.next;
320
-
321
- parent.keys.splice(index, 1);
322
- parent.values.splice(index, 1);
323
- parent.children.splice(index + 1, 1);
324
- }
325
-
326
- // ============================================================
327
- // 遍历
328
- // ============================================================
329
-
330
- /**
331
- * 获取所有索引条目(仅叶子数据,分隔键为内部副本,不重复计入)
332
- */
333
- entries() {
334
- const result = [];
335
- let node = this._root;
336
- while (node && !node.leaf) node = node.children[0];
337
- while (node) {
338
- for (let i = 0; i < node.keys.length; i++) {
339
- result.push({ key: node.keys[i], value: node.values[i] });
340
- }
341
- node = node.next;
342
- }
343
- return result;
344
- }
345
-
346
- /**
347
- * 清空索引
348
- */
349
- clear() {
350
- this._root = new BTreeNode();
351
- this._size = 0;
352
- }
353
-
354
- /**
355
- * 重建索引(从数据行批量构建)
356
- */
357
- rebuild(rows, getKey) {
358
- this.clear();
359
- for (let i = 0; i < rows.length; i++) {
360
- const key = getKey(rows[i]);
361
- if (key !== undefined && key !== null) {
362
- this.insert(key, i);
363
- }
364
- }
365
- }
366
-
367
- /**
368
- * 批量加载:从已排序的 [key, rowIndex] 数组底部向上构建
369
- * O(n) 时间,适合大数据量批量构建
370
- */
371
- bulkLoad(sortedPairs) {
372
- if (sortedPairs.length === 0) { this.clear(); return; }
373
- const order = this._order;
374
- // 第一层:构建叶子节点
375
- const leaves = [];
376
- let i = 0;
377
- while (i < sortedPairs.length) {
378
- const node = new BTreeNode(true);
379
- const end = Math.min(i + order - 1, sortedPairs.length);
380
- for (; i < end; i++) {
381
- const [key, val] = sortedPairs[i];
382
- node.keys.push(key);
383
- node.values.push(Array.isArray(val) ? val : [val]);
384
- }
385
- leaves.push(node);
386
- }
387
- for (let i = 0; i < leaves.length - 1; i++) leaves[i].next = leaves[i + 1];
388
- if (leaves.length === 1) { this._root = leaves[0]; this._size = sortedPairs.length; return; }
389
- // 逐层向上构建内部节点
390
- let currentLevel = leaves;
391
- while (currentLevel.length > 1) {
392
- const parents = [];
393
- let j = 0;
394
- while (j < currentLevel.length) {
395
- const node = new BTreeNode(false);
396
- const end = Math.min(j + order, currentLevel.length);
397
- for (; j < end; j++) {
398
- if (node.children.length > 0) {
399
- const child = currentLevel[j];
400
- node.keys.push(child.keys[0]);
401
- node.values.push(child.values[0]);
402
- }
403
- node.children.push(currentLevel[j]);
404
- }
405
- parents.push(node);
406
- }
407
- currentLevel = parents;
408
- }
409
- this._root = currentLevel[0];
410
- this._size = sortedPairs.length;
411
- }
412
- }
413
-
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._minKeys = Math.floor(order / 2) - 1;
28
+ this._minDegree = Math.ceil(order / 2);
29
+ }
30
+
31
+ get size() { return this._size; }
32
+
33
+ // ============================================================
34
+ // 插入
35
+ // ============================================================
36
+
37
+ insert(key, rowIndex) {
38
+ const root = this._root;
39
+
40
+ // 根节点满 → 分裂
41
+ if (root.keys.length === this._order - 1) {
42
+ const newRoot = new BTreeNode(false);
43
+ newRoot.children.push(root);
44
+ this._splitChild(newRoot, 0);
45
+ this._root = newRoot;
46
+ this._insertNonFull(newRoot, key, rowIndex);
47
+ } else {
48
+ this._insertNonFull(root, key, rowIndex);
49
+ }
50
+ this._size++;
51
+ }
52
+
53
+ _lowerBound(keys, key) {
54
+ let lo = 0, hi = keys.length;
55
+ while (lo < hi) {
56
+ const mid = (lo + hi) >> 1;
57
+ if (keys[mid] < key) lo = mid + 1;
58
+ else hi = mid;
59
+ }
60
+ return lo;
61
+ }
62
+
63
+ _insertNonFull(node, key, rowIndex) {
64
+ if (node.leaf) {
65
+ const i = this._lowerBound(node.keys, key);
66
+
67
+ if (i < node.keys.length && node.keys[i] === key) {
68
+ if (this._unique) {
69
+ this._size--;
70
+ return;
71
+ }
72
+ if (!node.values[i].includes(rowIndex)) {
73
+ node.values[i].push(rowIndex);
74
+ }
75
+ this._size--;
76
+ return;
77
+ }
78
+
79
+ node.keys.splice(i, 0, key);
80
+ node.values.splice(i, 0, [rowIndex]);
81
+ } else {
82
+ let i = this._route(node, key);
83
+ if (node.children[i] && node.children[i].keys.length === this._order - 1) {
84
+ this._splitChild(node, i);
85
+ i = this._route(node, key);
86
+ }
87
+ this._insertNonFull(node.children[i], key, rowIndex);
88
+ }
89
+ }
90
+
91
+ _splitChild(parent, index) {
92
+ const child = parent.children[index];
93
+ const newChild = new BTreeNode(child.leaf);
94
+ const mid = Math.floor((this._order - 1) / 2);
95
+
96
+ const midKey = child.keys[mid];
97
+ const midVal = child.values[mid];
98
+
99
+ if (child.leaf) {
100
+ // 叶子:右半移到新节点;分隔键保留在左叶(它是真实数据键),父节点只存副本
101
+ newChild.keys = child.keys.splice(mid + 1);
102
+ newChild.values = child.values.splice(mid + 1);
103
+ newChild.next = child.next;
104
+ child.next = newChild;
105
+ } else {
106
+ // 内部:分隔键提升到父节点并从子节点移除,保持 children = keys + 1
107
+ newChild.keys = child.keys.splice(mid + 1);
108
+ newChild.values = child.values.splice(mid + 1);
109
+ child.keys.splice(mid, 1);
110
+ child.values.splice(mid, 1);
111
+ newChild.children = child.children.splice(mid + 1);
112
+ }
113
+
114
+ parent.keys.splice(index, 0, midKey);
115
+ parent.values.splice(index, 0, midVal);
116
+ parent.children.splice(index + 1, 0, newChild);
117
+ }
118
+
119
+ // ============================================================
120
+ // 查找
121
+ // ============================================================
122
+
123
+ /**
124
+ * 内部节点路由:返回 key 应下降到的子节点下标。
125
+ * 命中分隔键副本时,根据左子树最大键判定真实数据所在子树
126
+ * (分隔键副本可能在左子树作为最大值,或在右子树作为最小值)。
127
+ */
128
+ _route(node, key) {
129
+ let i = this._lowerBound(node.keys, key);
130
+ if (i < node.keys.length && node.keys[i] === key) {
131
+ return this._getMax(node.children[i]).key === key ? i : i + 1;
132
+ }
133
+ return i;
134
+ }
135
+
136
+ /**
137
+ * 精确查找(始终下探到叶子取真实数据;内部分隔键只是叶子的副本,仅用于导航)
138
+ * @returns {number[]} 行号数组;未找到时返回空数组
139
+ */
140
+ search(key) {
141
+ let node = this._root;
142
+ while (!node.leaf) node = node.children[this._route(node, key)];
143
+ const i = this._lowerBound(node.keys, key);
144
+ return (i < node.keys.length && node.keys[i] === key) ? node.values[i] : [];
145
+ }
146
+
147
+ searchMany(keys) {
148
+ if (!keys || keys.length === 0) return [];
149
+ const out = [];
150
+ const seen = new Set();
151
+ for (let i = 0; i < keys.length; i++) {
152
+ const hits = this.search(keys[i]);
153
+ for (let j = 0; j < hits.length; j++) {
154
+ const idx = hits[j];
155
+ if (!seen.has(idx)) {
156
+ seen.add(idx);
157
+ out.push(idx);
158
+ }
159
+ }
160
+ }
161
+ return out;
162
+ }
163
+
164
+ prefix(prefix) {
165
+ if (prefix === undefined || prefix === null) return [];
166
+ const p = String(prefix);
167
+ const result = [];
168
+ let node = this._findLeaf(p);
169
+ while (node) {
170
+ for (let i = 0; i < node.keys.length; i++) {
171
+ const k = node.keys[i];
172
+ const s = k === undefined || k === null ? '' : String(k);
173
+ if (s < p) continue;
174
+ if (!s.startsWith(p)) return result;
175
+ result.push(...node.values[i]);
176
+ }
177
+ node = node.next;
178
+ }
179
+ return result;
180
+ }
181
+
182
+ /**
183
+ * 范围查询 [min, max]
184
+ */
185
+ range(min, max) {
186
+ const result = [];
187
+ let node = this._findLeaf(min);
188
+
189
+ while (node) {
190
+ for (let i = 0; i < node.keys.length; i++) {
191
+ const k = node.keys[i];
192
+ if (k > max) return result;
193
+ if (k >= min) {
194
+ result.push(...node.values[i]);
195
+ }
196
+ }
197
+ node = node.next;
198
+ }
199
+ return result;
200
+ }
201
+
202
+ /**
203
+ * 大于查询(严格开区间,不含 min 本身)
204
+ */
205
+ greaterThan(min) {
206
+ const result = [];
207
+ let node = this._findLeaf(min);
208
+ while (node) {
209
+ for (let i = 0; i < node.keys.length; i++) {
210
+ if (node.keys[i] > min) result.push(...node.values[i]);
211
+ }
212
+ node = node.next;
213
+ }
214
+ return result;
215
+ }
216
+
217
+ greaterThanEqual(min) {
218
+ const result = [];
219
+ let node = this._findLeaf(min);
220
+ while (node) {
221
+ for (let i = 0; i < node.keys.length; i++) {
222
+ if (node.keys[i] >= min) result.push(...node.values[i]);
223
+ }
224
+ node = node.next;
225
+ }
226
+ return result;
227
+ }
228
+
229
+ /**
230
+ * 小于查询(严格开区间,不含 max 本身)
231
+ */
232
+ lessThan(max) {
233
+ const result = [];
234
+ let node = this._root;
235
+ while (!node.leaf) node = node.children[0];
236
+ while (node) {
237
+ for (let i = 0; i < node.keys.length; i++) {
238
+ if (node.keys[i] < max) result.push(...node.values[i]);
239
+ else return result;
240
+ }
241
+ node = node.next;
242
+ }
243
+ return result;
244
+ }
245
+
246
+ lessThanEqual(max) {
247
+ const result = [];
248
+ let node = this._root;
249
+ while (!node.leaf) node = node.children[0];
250
+ while (node) {
251
+ for (let i = 0; i < node.keys.length; i++) {
252
+ if (node.keys[i] <= max) result.push(...node.values[i]);
253
+ else return result;
254
+ }
255
+ node = node.next;
256
+ }
257
+ return result;
258
+ }
259
+
260
+ _findLeaf(key) {
261
+ let node = this._root;
262
+ while (!node.leaf) {
263
+ node = node.children[this._lowerBound(node.keys, key)];
264
+ }
265
+ return node;
266
+ }
267
+
268
+ // ============================================================
269
+ // 删除
270
+ // ============================================================
271
+
272
+ remove(key, rowIndex) {
273
+ if (!this._root) return false;
274
+ const removed = this._removeFromNode(this._root, key, rowIndex);
275
+ if (removed) this._size--;
276
+ if (this._root.keys.length === 0 && !this._root.leaf && this._root.children.length > 0) {
277
+ this._root = this._root.children[0];
278
+ }
279
+ return removed;
280
+ }
281
+
282
+ _removeFromNode(node, key, rowIndex) {
283
+ if (node.leaf) {
284
+ const i = this._lowerBound(node.keys, key);
285
+ if (i >= node.keys.length || node.keys[i] !== key) return false;
286
+ if (!node.values[i].includes(rowIndex)) return false;
287
+ const vals = node.values[i].filter(v => v !== rowIndex);
288
+ if (vals.length === 0) {
289
+ node.keys.splice(i, 1);
290
+ node.values.splice(i, 1);
291
+ return true;
292
+ }
293
+ node.values[i] = vals;
294
+ return false;
295
+ }
296
+
297
+ let i = this._route(node, key);
298
+
299
+ // 下降前确保子节点有足够键,避免欠满子节点
300
+ const child = node.children[i];
301
+ if (child.keys.length <= this._minKeys) {
302
+ i = this._rebalanceChild(node, i);
303
+ }
304
+ return this._removeFromNode(node.children[i], key, rowIndex);
305
+ }
306
+
307
+ _rebalanceChild(parent, i) {
308
+ if (i > 0 && parent.children[i - 1].keys.length > this._minKeys) {
309
+ this._borrowFromLeft(parent, i);
310
+ return i;
311
+ }
312
+ if (i + 1 < parent.children.length && parent.children[i + 1].keys.length > this._minKeys) {
313
+ this._borrowFromRight(parent, i);
314
+ return i;
315
+ }
316
+ if (i > 0) {
317
+ this._mergeChildren(parent, i - 1);
318
+ return i - 1;
319
+ }
320
+ this._mergeChildren(parent, i);
321
+ return i;
322
+ }
323
+
324
+ _borrowFromLeft(parent, i) {
325
+ const left = parent.children[i - 1];
326
+ const right = parent.children[i];
327
+ if (right.leaf) {
328
+ // 叶子节点:分隔键是叶子的副本,用被移动的键而非旧分隔键,避免重复
329
+ const k = left.keys.pop();
330
+ const v = left.values.pop();
331
+ right.keys.unshift(k);
332
+ right.values.unshift(v);
333
+ parent.keys[i - 1] = k;
334
+ parent.values[i - 1] = v;
335
+ } else {
336
+ right.keys.unshift(parent.keys[i - 1]);
337
+ right.values.unshift(parent.values[i - 1]);
338
+ parent.keys[i - 1] = left.keys.pop();
339
+ parent.values[i - 1] = left.values.pop();
340
+ right.children.unshift(left.children.pop());
341
+ }
342
+ }
343
+
344
+ _borrowFromRight(parent, i) {
345
+ const left = parent.children[i];
346
+ const right = parent.children[i + 1];
347
+ if (left.leaf) {
348
+ // 叶子节点:用被移动的键而非旧分隔键,避免重复
349
+ const k = right.keys.shift();
350
+ const v = right.values.shift();
351
+ left.keys.push(k);
352
+ left.values.push(v);
353
+ parent.keys[i] = k;
354
+ parent.values[i] = v;
355
+ } else {
356
+ left.keys.push(parent.keys[i]);
357
+ left.values.push(parent.values[i]);
358
+ parent.keys[i] = right.keys.shift();
359
+ parent.values[i] = right.values.shift();
360
+ left.children.push(right.children.shift());
361
+ }
362
+ }
363
+
364
+ _getMax(node) {
365
+ while (!node.leaf) node = node.children[node.children.length - 1];
366
+ const i = node.keys.length - 1;
367
+ return { key: node.keys[i], value: node.values[i] };
368
+ }
369
+
370
+ _mergeChildren(parent, index) {
371
+ const left = parent.children[index];
372
+ const right = parent.children[index + 1];
373
+ const midKey = parent.keys[index];
374
+ const midVal = parent.values[index];
375
+
376
+ // 叶子节点:分隔键是叶子的副本(已在 left 或 right 中),不能重复压入
377
+ if (!left.leaf) {
378
+ left.keys.push(midKey);
379
+ left.values.push(midVal);
380
+ }
381
+ left.keys.push(...right.keys);
382
+ left.values.push(...right.values);
383
+ if (!left.leaf) left.children.push(...right.children);
384
+ if (left.leaf) left.next = right.next;
385
+
386
+ parent.keys.splice(index, 1);
387
+ parent.values.splice(index, 1);
388
+ parent.children.splice(index + 1, 1);
389
+ }
390
+
391
+ // ============================================================
392
+ // 遍历
393
+ // ============================================================
394
+
395
+ /**
396
+ * 获取所有索引条目(仅叶子数据,分隔键为内部副本,不重复计入)
397
+ */
398
+ entries() {
399
+ const result = [];
400
+ let node = this._root;
401
+ while (node && !node.leaf) node = node.children[0];
402
+ while (node) {
403
+ for (let i = 0; i < node.keys.length; i++) {
404
+ result.push({ key: node.keys[i], value: node.values[i] });
405
+ }
406
+ node = node.next;
407
+ }
408
+ return result;
409
+ }
410
+
411
+ /**
412
+ * 清空索引
413
+ */
414
+ clear() {
415
+ this._root = new BTreeNode();
416
+ this._size = 0;
417
+ }
418
+
419
+ /**
420
+ * 重建索引(从数据行批量构建)
421
+ */
422
+ rebuild(rows, getKey) {
423
+ this.clear();
424
+ for (let i = 0; i < rows.length; i++) {
425
+ const key = getKey(rows[i]);
426
+ if (key !== undefined && key !== null) {
427
+ this.insert(key, i);
428
+ }
429
+ }
430
+ }
431
+
432
+ /**
433
+ * 批量加载:从已排序的 [key, rowIndex] 数组底部向上构建
434
+ * O(n) 时间,适合大数据量批量构建
435
+ */
436
+ bulkLoad(sortedPairs) {
437
+ if (sortedPairs.length === 0) { this.clear(); return; }
438
+ const order = this._order;
439
+ // 第一层:构建叶子节点
440
+ const leaves = [];
441
+ let i = 0;
442
+ while (i < sortedPairs.length) {
443
+ const node = new BTreeNode(true);
444
+ const end = Math.min(i + order - 1, sortedPairs.length);
445
+ for (; i < end; i++) {
446
+ const [key, val] = sortedPairs[i];
447
+ node.keys.push(key);
448
+ node.values.push(Array.isArray(val) ? val : [val]);
449
+ }
450
+ leaves.push(node);
451
+ }
452
+ for (let i = 0; i < leaves.length - 1; i++) leaves[i].next = leaves[i + 1];
453
+ if (leaves.length === 1) { this._root = leaves[0]; this._size = sortedPairs.length; return; }
454
+ // 逐层向上构建内部节点
455
+ let currentLevel = leaves;
456
+ while (currentLevel.length > 1) {
457
+ const parents = [];
458
+ let j = 0;
459
+ while (j < currentLevel.length) {
460
+ const node = new BTreeNode(false);
461
+ const end = Math.min(j + order, currentLevel.length);
462
+ for (; j < end; j++) {
463
+ if (node.children.length > 0) {
464
+ const child = currentLevel[j];
465
+ node.keys.push(child.keys[0]);
466
+ node.values.push(child.values[0]);
467
+ }
468
+ node.children.push(currentLevel[j]);
469
+ }
470
+ parents.push(node);
471
+ }
472
+ currentLevel = parents;
473
+ }
474
+ this._root = currentLevel[0];
475
+ this._size = sortedPairs.length;
476
+ }
477
+ }
478
+
414
479
  module.exports = BTree;