Source: bplustree.js

  1. // @flow
  2. type Tree = { t: string; k: number[]; v: any[]; n?: ?number };
  3. type CmpFn = ((a: any, b: any) => number);
  4. /** Class representing a B+ Tree. */
  5. class BPlusTree {
  6. order: number;
  7. debug: boolean;
  8. cmpFn: CmpFn;
  9. minKeys: number;
  10. maxKeys: number;
  11. numKeys: number;
  12. numVals: number;
  13. tree: Tree;
  14. /**
  15. * @param {Object} options
  16. * @param {number} [options.order=6] - The tree order (or branching factor or node capacity)
  17. * @param {boolean} [options.debug=false] - Check tree invariants after each insert / remove
  18. * @param {string} [options.cmpFn=numericComparison] - Comparison function to use
  19. */
  20. constructor({
  21. order = 6,
  22. debug = false,
  23. cmpFn = ((a: any, b: any): number => ((a < b) ? -1 : ((a > b) ? 1 : 0))) // eslint-disable-line
  24. }: { order?: number; debug?: boolean; cmpFn?: CmpFn } = {}) {
  25. this.order = order;
  26. this.debug = debug;
  27. this.cmpFn = cmpFn;
  28. if (this.order % 2 !== 0 || this.order < 4) {
  29. throw new Error('order must be even and greater than 4');
  30. }
  31. this.minKeys = Math.ceil(this.order / 2);
  32. this.maxKeys = this.order - 1;
  33. this.numKeys = 0;
  34. this.numVals = 0;
  35. this.tree = { t: 'leaf', k: [], v: [], n: null };
  36. }
  37. /**
  38. * Get a {k1: v1, k2: v2, ...} object representing the stored data
  39. * @param {Object} options
  40. * @param {BPTree.tree} [options.root=this.tree] - Tree to check
  41. * @param {boolean} [options.getKeys=false] - Instead of an object, get a list of all keys
  42. * @param {boolean} [options.getValues=false] - Instead of an object, get a list of all values
  43. * @param {boolean} [options.descending=false] - Get it reversed (only works if options.getKeys or options.getValues)
  44. * @return {{keys: values}|Keys[]|Values[]}
  45. */
  46. repr({ root, getKeys = false, getValues = false, descending = false }:
  47. { root?: Tree; getKeys?: boolean; getValues?: boolean; descending?: boolean } = {}): Object | any[] {
  48. const tree = root || this.tree;
  49. let resultArray = [];
  50. const resultObject = {};
  51. function walk(node: Tree) {
  52. if (node.t === 'branch') {
  53. const kids = node.v;
  54. for (let i = 0, kl = kids.length; i < kl; i++) {
  55. walk(kids[i]);
  56. }
  57. } else if (node.t === 'leaf') {
  58. for (let i = 0, nkl = node.k.length; i < nkl; i++) {
  59. if (getKeys) {
  60. resultArray.push(node.k[i]);
  61. } else if (getValues) {
  62. resultArray.push(node.v[i]);
  63. } else {
  64. resultObject[node.k[i]] = node.v[i];
  65. }
  66. }
  67. }
  68. }
  69. walk(tree);
  70. // if (Array.isArra)
  71. if (resultArray.length && resultArray[0].length) {
  72. resultArray = Array.prototype.concat.apply([], resultArray);
  73. }
  74. if (resultArray.length && descending) {
  75. return resultArray.reverse();
  76. }
  77. if (resultArray.length) {
  78. return resultArray;
  79. }
  80. return resultObject;
  81. }
  82. /**
  83. * Get all values between keys `lowerBound` and `upperBound`
  84. * @param {number} lowerBound
  85. * @param {number} upperBound
  86. * @param {Object} options
  87. * @param {boolean} [options.descending=false] - Get it reversed (only works if options.keys or options.values)
  88. * @return {Values[]} A flat array of values, or empty array.
  89. */
  90. fetchRange(lowerBound: number, upperBound: number, { descending = false }: { descending: boolean } = {}): any[] {
  91. const hi = upperBound;
  92. let lo = lowerBound;
  93. let result = [];
  94. let leaf = this.fetch(lo, { getLeaf: true });
  95. if (!leaf) { // look for a new lower bound, which is quite slow
  96. // check if lo is bigger than highest key in tree
  97. leaf = this.tree;
  98. while (leaf.t === 'branch') {
  99. leaf = leaf.v[leaf.v.length - 1];
  100. }
  101. if (this.cmpFn(lo, leaf.k[leaf.k.length - 1]) === 1) {
  102. return [];
  103. }
  104. // ok, now this is REALLY suboptimal (and ugly)
  105. const keys = this.repr({ getKeys: true });
  106. for (let i = 0; i < this.numKeys; i++) {
  107. if (this.cmpFn(keys[i], lo) === 1) {
  108. lo = keys[i];
  109. leaf = this.fetch(lo, { getLeaf: true });
  110. break;
  111. }
  112. }
  113. }
  114. let index = leaf.k.indexOf(lo);
  115. while (leaf.k[index] <= hi) {
  116. if (this.cmpFn(leaf.k[index], hi) === 0) {
  117. // if key at current index is upper bound, concat all vals and stop
  118. result.push(leaf.v[index]);
  119. break;
  120. }
  121. if (this.cmpFn(leaf.k[leaf.k.length - 1], hi) === 0) {
  122. // if last key is upper bound, concat all vals and stop
  123. result.push(leaf.v.slice(index));
  124. break;
  125. } else if (this.cmpFn(leaf.k[leaf.k.length - 1], hi) === -1) {
  126. // if last key is smaller than upper bound, fetch next leaf and iterate
  127. result.push(leaf.v.slice(index));
  128. if (leaf.n !== null) {
  129. leaf = this.fetch(leaf.n, { getLeaf: true });
  130. index = 0;
  131. } else {
  132. break;
  133. }
  134. } else {
  135. // if last key is bigger than upper bound, concat until upper bound
  136. let i = index;
  137. for (; leaf.k[i] <= hi; i++);
  138. result.push(leaf.v.slice(0, i));
  139. break;
  140. }
  141. }
  142. if (Array.isArray(result[0])) {
  143. result = Array.prototype.concat.apply([], Array.prototype.concat.apply([], result));
  144. } else {
  145. result = Array.prototype.concat.apply([], result);
  146. }
  147. if (descending) {
  148. result.reverse();
  149. }
  150. return result;
  151. }
  152. /**
  153. * Tree values generator. It will start generating values from a certain key
  154. * until the end of the tree OR until `target` is found OR until `limit`
  155. * is reached OR until there are no elements anymore.
  156. *
  157. * In other words:
  158. *
  159. * - if `target` is found before `limit`, it'll stop
  160. * - if `limit` is reached before `target` was found, it'll stop
  161. * - `target` and `limit` are both optional: use none of them, one of them, or both
  162. * - `keyNotFound` is optional, it lets you decide which key to prefer when the key is not found
  163. *
  164. * @param {Object} options
  165. * @param {Key} [options.key] - Key at which to start generating.
  166. * @param {boolean} [options.target] - Stop generating when this value is found.
  167. * @param {number} [options.limit] - Generate at max this number of values.
  168. * @param {string} [options.keyNotFound] - See `notFound` of `BPlusTree.fetch`
  169. * @return {Generator} - e.g. `{ value: { k: 6, v: ['f', 'g'] }, done: false }`
  170. */
  171. * values({ key, target, limit = Infinity, keyNotFound }:
  172. { key: any; target: any; limit: number; keyNotFound: ?string } = {}): Generator<any, ?Object, void> {
  173. let returned = 0;
  174. let leaf;
  175. let _key = key;
  176. let _keyNotFound = keyNotFound;
  177. if (typeof _key === 'undefined') {
  178. _key = -Infinity;
  179. _keyNotFound = 'right';
  180. }
  181. leaf = this.fetch(_key, { getLeaf: true, notFound: _keyNotFound });
  182. if (!leaf) {
  183. return undefined;
  184. }
  185. while (true) { // eslint-disable-line no-constant-condition
  186. let index = leaf.k.indexOf(_key);
  187. if (index === -1) {
  188. index = 0;
  189. }
  190. for (let i = index; i < leaf.v.length; i++) {
  191. const length = leaf.v[i].length;
  192. returned += length;
  193. if (returned >= limit) {
  194. const remainder = length - (returned - limit);
  195. if (remainder >= 0) {
  196. return { k: leaf.k[i], v: leaf.v[i].slice(0, remainder) };
  197. }
  198. }
  199. if (target === leaf.v[i][0]) {
  200. return { k: leaf.k[i], v: leaf.v[i] };
  201. }
  202. if (leaf.n === null && i + 1 === leaf.v.length) {
  203. return { k: leaf.k[i], v: leaf.v[i] };
  204. }
  205. yield { k: leaf.k[i], v: leaf.v[i] };
  206. }
  207. if (leaf.n !== null) {
  208. leaf = this.fetch(leaf.n, { getLeaf: true, notFound: _keyNotFound });
  209. } else {
  210. break;
  211. }
  212. }
  213. return undefined;
  214. }
  215. /**
  216. * Get tree depth (or height)
  217. * @param {Object} options
  218. * @param {BPTree.tree} [options.root=this.tree] - Tree to use
  219. * @return {number} Computed depth
  220. */
  221. depth({ root }: { root: Tree } = {}): number {
  222. let tree = root || this.tree;
  223. let d = 0;
  224. while (tree.t === 'branch') {
  225. tree = tree.v[0];
  226. d += 1;
  227. }
  228. return d;
  229. }
  230. /**
  231. * Check tree invariants
  232. * @param {Object} options
  233. * @param {BPTree.tree} [options.root=this.tree] - Tree to check
  234. * @return {boolean} Returns `true` or throws an `Error()`
  235. */
  236. check({ root }: { root: Tree } = {}): boolean {
  237. const _root = root || this.tree;
  238. const depth = this.depth({ root: _root });
  239. function assert(expr: boolean, msg: string) {
  240. if (!expr) {
  241. throw new Error(msg);
  242. }
  243. }
  244. function checking(self: BPlusTree, currentNode: Tree, currentDepth: number, lo: any[], hi: any[]): boolean {
  245. const node = currentNode;
  246. const keysLength = node.k.length;
  247. assert(keysLength <= self.maxKeys, 'Overflowed node');
  248. for (let i = 0, kl = keysLength - 1; i < kl; i++) {
  249. assert(self.cmpFn(node.k[i], node.k[i + 1]) === -1, 'Disordered or duplicate key');
  250. }
  251. assert(lo.length === 0 || self.cmpFn(lo[0], node.k[0]) < 1, 'lo error');
  252. assert(hi.length === 0 || self.cmpFn(node.k[keysLength - 1], hi[0]) === -1, 'hi error');
  253. if (node.t === 'branch') {
  254. const kids = node.v;
  255. const kidsLength = kids.length;
  256. if (currentDepth === 0) {
  257. assert(kidsLength >= 2, 'Underpopulated root');
  258. } else {
  259. assert(kidsLength >= self.minKeys, 'Underpopulated branch');
  260. }
  261. assert(keysLength === kidsLength - 1, 'keys and kids don\'t correspond');
  262. for (let i = 0; i < kidsLength; i++) {
  263. const newLo = (i === 0 ? lo : [node.k[i - 1]]);
  264. const newHi = (i === keysLength ? hi : [node.k[i]]);
  265. checking(self, kids[i], currentDepth + 1, newLo, newHi);
  266. }
  267. } else if (node.t === 'leaf') {
  268. const v = node.v;
  269. assert(currentDepth === depth, 'Leaves at different depths');
  270. assert(keysLength === v.length, 'keys and values don\'t correspond');
  271. if (currentDepth > 0) {
  272. assert(v.length >= self.minKeys, 'Underpopulated leaf');
  273. }
  274. } else {
  275. assert(false, 'Bad type');
  276. }
  277. return true;
  278. }
  279. return checking(this, _root, 0, [], []);
  280. }
  281. /**
  282. * Fetch the value(s) stored at `key`.
  283. *
  284. * - `getLeaf` returns the whole leaf
  285. * - `getPath` returns the path from the root to this leaf
  286. * - when defined, `notFound` can be either 'left' or 'right', it controls which key to return when it wasn't found
  287. *
  288. * @param {Key} key
  289. * @param {Object} options
  290. * @param {BPTree.tree} [options.root=this.tree] - Tree to search in
  291. * @param {boolean} [options.getLeaf=false] - Return the leaf containing the value(s)
  292. * @param {boolean} [options.getPath=false] - Return {val: value(s), leaf: leaf, path: pathFromRootToLeaf}
  293. * @param {string} [options.notFound] - either 'left' or 'right' - Return what was found left or right from key which doesn't exist
  294. * @return {Value|Value[]|Leaf|Object|Boolean}
  295. */
  296. fetch(key: any, { root, getLeaf = false, getPath = false, notFound }:
  297. { root?: Tree; getLeaf?: boolean; getPath?: boolean; notFound?: ?string } = {}): any {
  298. let node = root || this.tree;
  299. let index;
  300. const path = [];
  301. while (node.t === 'branch') {
  302. index = 0;
  303. let found = false;
  304. for (let kl = node.k.length; index < kl; index++) {
  305. if (this.cmpFn(node.k[index], key) === 1) {
  306. found = true;
  307. break;
  308. }
  309. }
  310. if (!found) {
  311. index = node.v.length - 1;
  312. }
  313. node = node.v[index];
  314. path.push(index);
  315. }
  316. for (let j = 0, kl = node.k.length; j < kl; j++) {
  317. const cmp = this.cmpFn(key, node.k[j]);
  318. if (cmp === 0) {
  319. const val = node.v[j];
  320. if (getPath) {
  321. return { val, leaf: node, path };
  322. }
  323. if (getLeaf) {
  324. return node;
  325. }
  326. return val;
  327. } else if (notFound) {
  328. if (notFound === 'right' && !(cmp < 0)) {
  329. return this.fetch(node.n, { root, getLeaf, getPath });
  330. }
  331. node = this._get(path);
  332. let val;
  333. if (notFound === 'left' && !(cmp < 0)) {
  334. val = node.v[node.v.length - 1];
  335. } else if (notFound === 'right') {
  336. val = node.v[0];
  337. }
  338. if (val) {
  339. if (getPath) {
  340. return { val, leaf: node, path };
  341. }
  342. if (getLeaf) {
  343. return node;
  344. }
  345. return val;
  346. }
  347. } else if (this.cmpFn(node.k[j], key) === 1) {
  348. break; // just to finish quicker; not needed for correctness
  349. }
  350. }
  351. return false;
  352. }
  353. _doStore(key: any, value: any) {
  354. const path = [];
  355. let node = this.tree;
  356. // Find the leaf node for key, and the path down to it.
  357. while (node.t === 'branch') {
  358. let i = 0;
  359. let found = false;
  360. for (let nkl = node.k.length; i < nkl; i++) {
  361. if (this.cmpFn(key, node.k[i]) === -1) {
  362. found = true;
  363. break;
  364. }
  365. }
  366. if (!found) {
  367. i = node.k.length;
  368. }
  369. path.push({ t: node.t, k: node.k, v: node.v, i });
  370. node = node.v[i];
  371. }
  372. // Find the index for key in the leaf node.
  373. let i = 0;
  374. let found = false;
  375. const nkl = node.k.length;
  376. for (; i < nkl; i++) {
  377. if (this.cmpFn(key, node.k[i]) === 0) {
  378. // key isn't actually new, so the structure goes unchanged.
  379. node.v[i].push(value);
  380. return;
  381. } else if (this.cmpFn(key, node.k[i]) === -1) {
  382. found = true;
  383. break;
  384. }
  385. }
  386. if (!found) {
  387. i = nkl;
  388. }
  389. // We'll have to insert it in the leaf at i. If there's room, just do it:
  390. node.k.splice(i, 0, key);
  391. node.v.splice(i, 0, [value]);
  392. this.numKeys += 1;
  393. this.numVals += 1;
  394. if (node.k.length < this.order) {
  395. return;
  396. }
  397. // Otherwise split the now-overpacked leaf...
  398. const mid = Math.floor(this.order / 2);
  399. let tween = node.k[mid];
  400. let left = { t: 'leaf', k: node.k.slice(0, mid), v: node.v.slice(0, mid), n: node.k[mid] };
  401. let right = { t: 'leaf', k: node.k.slice(mid), v: node.v.slice(mid), n: node.n };
  402. // ...and propagate the split back up the path.
  403. while (path.length) {
  404. node = path.pop();
  405. node.k.splice(node.i, 0, tween);
  406. node.v[node.i] = left;
  407. node.v.splice(node.i + 1, 0, right);
  408. if (node.k.length < this.maxKeys) {
  409. return;
  410. }
  411. tween = node.k[mid - 1];
  412. left = { t: 'branch', k: node.k.slice(0, mid - 1), v: node.v.slice(0, mid), n: node.k[mid] };
  413. right = { t: 'branch', k: node.k.slice(mid), v: node.v.slice(mid), n: null };
  414. }
  415. // If we got here, we need a new root.
  416. this.tree = { t: 'branch', k: [tween], v: [left, right], n: null };
  417. }
  418. /**
  419. * Insert value at key key
  420. * @param {Key} key
  421. * @param {Value} value
  422. * @return {boolean} true
  423. */
  424. store(key: any, value: any): boolean {
  425. this._doStore(key, value);
  426. if (this.debug) {
  427. this.check();
  428. }
  429. return true;
  430. }
  431. _get(path: number[], node?: Tree): Object {
  432. let object = node || this.tree;
  433. let index = 0;
  434. const length = path.length;
  435. while (object && index < length) {
  436. object = object.v[path[index++]];
  437. }
  438. return object;
  439. }
  440. _genGetKeyFn(driller: (tree: Tree) => any, depth: number): (tree: Tree) => any {
  441. if (depth === 0) {
  442. return (o: Tree): any => driller(o).k[0];
  443. }
  444. return this._genGetKeyFn((o: Tree): any => driller(o).v[0], depth - 1);
  445. }
  446. _getFirstKeyFn(depth: number): (tree: Tree) => any {
  447. const fn = [
  448. (o: Tree): any => o,
  449. (o: Tree): any => o.v[0],
  450. (o: Tree): any => o.v[0].v[0],
  451. (o: Tree): any => o.v[0].v[0].v[0],
  452. (o: Tree): any => o.v[0].v[0].v[0].v[0],
  453. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0],
  454. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0],
  455. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  456. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  457. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  458. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  459. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  460. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  461. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  462. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  463. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  464. (o: Tree): any => o.v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0].v[0],
  465. ];
  466. const length = fn.length;
  467. return (depth < length - 1 && ((o: Tree): any => fn[depth](o).k[0])) || this._genGetKeyFn(fn[length - 1], (depth - length) + 1);
  468. }
  469. _fixKeys(): any {
  470. const result = [];
  471. function walk(node: Tree, depth: number, path: any) {
  472. if (node.t === 'branch') {
  473. const kids = node.v;
  474. for (let i = 0, kl = kids.length; i < kl; i++) {
  475. if (kids[i].t === 'branch') {
  476. const newPath = path.slice(0, depth).concat([i]);
  477. result.push(newPath);
  478. walk(kids[i], depth + 1, newPath);
  479. }
  480. }
  481. }
  482. }
  483. walk(this.tree, 0, []);
  484. result.sort((a, b) => (a.length > b.length) ? -1 : ((a.length < b.length) ? 1 : 0)); // eslint-disable-line
  485. result.forEach((path: any) => {
  486. const sub = this._get(path);
  487. sub.k = sub.v.slice(1).map(this._getFirstKeyFn(result[0].length - path.length));
  488. });
  489. if (this.tree.t !== 'leaf') {
  490. this.tree.k = this.tree.v.slice(1).map(this._getFirstKeyFn(result.length ? result[0].length : 0));
  491. }
  492. return result;
  493. }
  494. _removeKey(key: any, val: any): boolean | Object {
  495. const fetched = this.fetch(key, { getPath: true });
  496. if (!fetched) {
  497. return false;
  498. }
  499. const keyIndex = fetched.leaf.k.indexOf(key);
  500. const valIndex = fetched.leaf.v[keyIndex].indexOf(val);
  501. // key does not contain val
  502. if (val !== undefined && valIndex === -1) {
  503. return false;
  504. }
  505. const valCount = fetched.leaf.v[keyIndex].length;
  506. let removed;
  507. // we only have one val, remove it together with its key
  508. if (valCount === 1 && keyIndex !== -1) {
  509. fetched.leaf.k.splice(keyIndex, 1);
  510. removed = fetched.leaf.v[keyIndex][0];
  511. fetched.leaf.v.splice(keyIndex, 1);
  512. this.numKeys--;
  513. } else if (val !== undefined) {
  514. // key contains val, but we have other vals, only remove this val
  515. removed = fetched.leaf.v[keyIndex][valIndex];
  516. fetched.leaf.v[keyIndex].splice(valIndex, 1);
  517. } else {
  518. // key has several vals, we don't remove anything
  519. return false;
  520. }
  521. // we lost one val
  522. this.numVals--;
  523. return { val: removed, leaf: fetched.leaf, path: fetched.path };
  524. }
  525. _doRemove(key: any, val: any): boolean | any {
  526. // get leaf for key, remove key from leaf
  527. const removed = this._removeKey(key, val);
  528. if (typeof removed === 'boolean') {
  529. return false;
  530. }
  531. const leaf = removed.leaf;
  532. const path = removed.path;
  533. // if key in branch.k, replace it with new smallest key in leaf
  534. const parentPath = path.slice(0, path.length - 1);
  535. let parent = this._get(parentPath);
  536. let index = parent.k.indexOf(key);
  537. // if leaf is at least half full, terminate
  538. if (leaf.v.length >= this.minKeys) {
  539. return removed.val;
  540. }
  541. const leafIndex = path[path.length - 1];
  542. // else borrow
  543. // if rightSibling is more than half full, borrow leftmost value
  544. let canBorrowRight = false;
  545. if (leafIndex < parent.v.length - 1) {
  546. const rightSibling = parent.v[leafIndex + 1];
  547. if (rightSibling && rightSibling.k.length > this.minKeys) {
  548. // can borrow from right because it is more than half full
  549. canBorrowRight = true;
  550. const keyToBorrow = rightSibling.k.shift();
  551. const valBorrowed = rightSibling.v.shift();
  552. leaf.k.push(keyToBorrow);
  553. leaf.v.push(valBorrowed);
  554. leaf.n = rightSibling.k[0];
  555. parent.k = parent.v.slice(1).map((o: Object): any => o.k[0]);
  556. parent.v[leafIndex] = leaf;
  557. parent.v[leafIndex + 1] = rightSibling;
  558. }
  559. }
  560. // if leftSibling is more than half full, borrow rightmost value
  561. let canBorrowLeft = false;
  562. if (leafIndex > 0) {
  563. const leftSibling = parent.v[leafIndex - 1];
  564. if (leftSibling && leftSibling.k.length > this.minKeys) {
  565. // can borrow from left because it is more than half full
  566. canBorrowLeft = true;
  567. const keyToBorrow = leftSibling.k.pop();
  568. const valBorrowed = leftSibling.v.pop();
  569. leaf.k.unshift(keyToBorrow);
  570. leaf.v.unshift(valBorrowed);
  571. parent.k = parent.v.slice(1).map((o: Object): any => o.k[0]);
  572. parent.v[leafIndex] = leaf;
  573. parent.v[leafIndex - 1] = leftSibling;
  574. }
  575. }
  576. if (!canBorrowRight && !canBorrowLeft) {
  577. let again = true;
  578. let lastIndex = -1;
  579. while (again) {
  580. parent = this._get(path);
  581. lastIndex = index;
  582. if (path.length) {
  583. index = path.pop();
  584. } else {
  585. index = 0;
  586. again = false;
  587. }
  588. const mergeNeeded = parent.t !== 'leaf' && parent.v[lastIndex].k.length < this.minKeys;
  589. if (mergeNeeded) {
  590. const leftExists = parent.v[lastIndex - 1];
  591. let leftSum = leftExists && parent.v[lastIndex - 1].k.length + parent.v[lastIndex].k.length;
  592. leftSum += parent.v[lastIndex].t === 'leaf' ? 0 : 1;
  593. const roomOnLeft = leftExists && leftSum && leftSum <= this.maxKeys;
  594. const rightExists = parent.v[lastIndex + 1];
  595. let rightSum = rightExists && parent.v[lastIndex + 1].k.length + parent.v[lastIndex].k.length;
  596. rightSum += parent.v[lastIndex].t === 'leaf' ? 0 : 1;
  597. const roomOnRight = rightExists && rightSum && rightSum <= this.maxKeys;
  598. let splitIndex = false;
  599. if ((leftExists && roomOnLeft) || (leftExists && !roomOnRight)) {
  600. if (!roomOnLeft) {
  601. splitIndex = lastIndex - 1;
  602. }
  603. // merging with left, deleting sibling
  604. // node becomes (sibling merged with node)
  605. parent.v[lastIndex] = BPlusTree._mergeLeft(parent.v[lastIndex - 1], parent.v[lastIndex]);
  606. parent.v.splice(lastIndex, 1); // delete now merged sibling
  607. } else if (rightExists) {
  608. if (!roomOnRight) {
  609. splitIndex = lastIndex;
  610. }
  611. // merging with right, deleting sibling
  612. // node becomes (node merged with sibling)
  613. parent.v[lastIndex] = BPlusTree._mergeRight(parent.v[lastIndex + 1], parent.v[lastIndex]);
  614. parent.v.splice(lastIndex + 1, 1); // delete now merged sibling
  615. }
  616. if (splitIndex !== false) {
  617. const branchToSplit = parent.v[splitIndex];
  618. const mid = this.minKeys;
  619. const leftContent = branchToSplit.v.slice(0, mid);
  620. const rightContent = branchToSplit.v.slice(mid);
  621. const childType = parent.t;
  622. const left = { t: childType, k: leftContent.slice(1).map((o: Object): any => o.k[0]), v: leftContent };
  623. const right = { t: childType, k: rightContent.slice(1).map((o: Object): any => o.k[0]), v: rightContent };
  624. parent.v.splice(splitIndex, 1, left, right);
  625. }
  626. }
  627. }
  628. if (this.tree.v.length < 2 && this.tree.t !== 'leaf') {
  629. // underpopulated root
  630. if (this.tree.v[index].v.length > this.maxKeys) {
  631. // need to split
  632. const mid = this.minKeys;
  633. const leftContent = this.tree.v[index].v.slice(0, mid);
  634. const rightContent = this.tree.v[index].v.slice(mid);
  635. const left = { t: 'branch', k: [leftContent[leftContent.length - 1].k[0]], v: leftContent };
  636. const right = { t: 'branch', k: [rightContent[rightContent.length - 1].k[0]], v: rightContent };
  637. this.tree.t = 'branch';
  638. this.tree.n = null;
  639. this.tree.k = [right.v[0].k[0]];
  640. this.tree.v = [left, right];
  641. } else {
  642. // need to hoist
  643. this.tree.t = 'leaf';
  644. this.tree = this.tree.v[index];
  645. const slice = this.tree.v.slice(1);
  646. if (slice.length && slice[0].t) {
  647. this.tree.k = slice.map((o: Object): any => o.k[0]);
  648. }
  649. }
  650. }
  651. this._fixKeys();
  652. }
  653. return removed.val;
  654. }
  655. /**
  656. * Remove value from key key, or remove key and its value if key only has one value
  657. * @param {Key} key
  658. * @param {Value?} value
  659. * @return {Value} The removed value
  660. */
  661. remove(key: any, value: any): any {
  662. const removed = this._doRemove(key, value);
  663. if (this.debug) {
  664. this.check();
  665. }
  666. return removed;
  667. }
  668. static _mergeLeft(dest: Tree, src: Tree): Tree {
  669. const node = dest;
  670. node.k = node.k.concat(src.k);
  671. node.v = node.v.concat(src.v);
  672. node.n = src.n;
  673. return node;
  674. }
  675. static _mergeRight(dest: Tree, _src: Tree): Tree {
  676. const node = dest;
  677. const src = _src;
  678. if (src.t !== 'leaf') {
  679. src.v[src.v.length - 1].n = node.v[0].k[0];
  680. }
  681. node.k = src.k.concat(node.k);
  682. node.v = src.v.concat(node.v);
  683. return node;
  684. }
  685. }
  686. module.exports = BPlusTree;