i2bplustree
Version:
A package to implement the Improved Interval B+ tree, in TypeScript
214 lines (213 loc) • 7.53 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const internal_1 = require("./internal");
/**
* Class implementation of a generic Interval B+-Tree node
*/
class IBplusNode {
/**
* @param order Node's order. Must be the same in all tree nodes.
* @param parent This node's parent node. If null, node is root
* @param keys Keys are the lower bounds of this tree subtree
* @param maximums Maximum end point of the intervals indexed by its subtree
*/
constructor(order, parent, keys, maximums) {
this.order = order;
this.parent = parent;
this.keys = keys;
this.maximums = maximums;
this.rightSibling = null;
this.leftSibling = null;
}
/**
* Get this node's parent node
*/
getParent() {
return this.parent;
}
/**
* Set this node parent node
*
* @param newParent new parent node
*/
setParent(newParent) {
this.parent = newParent;
}
/**
* Get the minimum key out of this node keys
*/
getMinKey() {
return this.keys[0];
}
/**
* Get the maximum number out of this node maximums
*/
getMax() {
return Math.max(...this.maximums);
}
/**
* Gets this node right sibling.
*/
getRightSibling() {
return this.rightSibling;
}
/**
* Sets this node right sibling to the given value.
*
* @param sibling new right sibling
*/
setRightSibling(sibling) {
this.rightSibling = sibling;
}
/**
* Gets this node left sibling.
*/
getLeftSibling() {
return this.leftSibling;
}
/**
* Sets this node left sibling to the given value.
*
* @param sibling new left sibling
*/
setLeftSibling(sibling) {
this.leftSibling = sibling;
}
/**
* Function to be called on node deletion.
* Sets this node siblings' siblings to one each other, making this node no one's sibling
*/
concatSiblings() {
if (this.rightSibling !== null)
this.rightSibling.setLeftSibling(this.leftSibling);
if (this.leftSibling !== null)
this.leftSibling.setRightSibling(this.rightSibling);
}
/**
* Checks if this node is the root.
*
* @returns true if this node is Root, false otherwise.
*/
isRoot() {
return this.parent == null;
}
/**
* Update the key and the maximum representing a node in its parent
*/
updateParentValues() {
this.parent.updateMin(this);
this.parent.updateMax(this);
}
/**
* Insert the given Interval in the tree.
* If alpha parameter is > 0, TimeSplit is applied to a leaf node N after a
* new interval is inserted into N and the end point of the new interval is
* greater than the maximum end point among the intervals that were already in N.
*
* @param int the interval to be inserted
* @param alpha the value of alpha used in the PickSplitPoint algorithm. If alpha <= 0, time splits aren't used.
*/
insert(int, alpha) {
// Insertions must always start in the root -> Valid since this function is not recursive
if (!this.isRoot())
return this.parent.insert(int, alpha);
// Perform a search to determine what leaf the new record should go into.
let insertionNode = this.findInsertNode(int);
// No leafs in the tree
if (insertionNode === null && this instanceof internal_1.IBplusInternalNode) {
insertionNode = new internal_1.IBplusLeafNode(this.order, this);
this.setChildren([insertionNode]);
}
// If we are adding and the keys's size is already equal to order, split before insertion
if (insertionNode.keys.length >= this.order)
// Split also inserts
insertionNode = insertionNode.split(int);
else
insertionNode.addInterval(int);
// Comparison viable since maximums and minimums were not yet updated
if (alpha > 0 && int.getUpperBound() > this.getMax())
insertionNode.timeSplit(this, alpha);
insertionNode.updateParentValues();
}
/**
* Remove an entry from the sibling node and add it to this node
*
* @param sibling The sibling this node is going to borrow from
* @param insertId The index where the element will be inserted
* @param removeId The index of the element going to be removed from the sibling
*/
borrow(sibling, insertId, removeId) {
this.keys.splice(insertId, 0, sibling.keys.splice(removeId, 1)[0]);
this.maximums.splice(insertId, 0, sibling.maximums.splice(removeId, 1)[0]);
this.setChildParentOnBorrow(sibling.getChildren().splice(removeId, 1)[0], insertId);
this.updateParentValues();
sibling.updateParentValues();
}
/**
* Merge this node into the given node
*
* @param sibling The sibling to merge with
* @param id The position where this node elements will be inserted
*/
merge(sibling, id) {
sibling.keys.splice(id, 0, ...this.keys);
sibling.maximums.splice(id, 0, ...this.maximums);
sibling.getChildren().splice(id, 0, ...this.getChildren());
this.setSubstitutionNode(sibling);
this.setChildrenParentOnMerge(sibling);
this.concatSiblings();
this.parent.removeChild(this.parent.getChildren().indexOf(this));
if (!sibling.isRoot())
sibling.updateParentValues();
}
/**
* Handle underflow by borrowing form siblings or merging with them
*/
handleUnderflow() {
// Minimum number of entries a node must have
let minEntries = Math.floor(this.order / 2);
// Borrow from left sibling
if (this.leftSibling != null && this.leftSibling.keys.length > minEntries)
this.borrow(this.leftSibling, 0, this.leftSibling.keys.length - 1);
//Borrow from right sibling
else if (this.rightSibling != null && this.rightSibling.keys.length > minEntries)
this.borrow(this.rightSibling, this.keys.length, 0);
// Merge left sibling
else if (this.leftSibling != null)
this.merge(this.leftSibling, this.leftSibling.keys.length);
// Merge right sibling
else if (this.rightSibling != null)
this.merge(this.rightSibling, 0);
}
/**
* Find out if this Node is in an underflow situation.
* Invariant Underflow: Every node but the root must always have at least floor(order/ 2) keys.
*
* @returns true if it is, false otherwise
*/
isUnderflow() {
return this.keys.length < Math.floor(this.order / 2);
}
/**
* Remove the node's child in the given idx
* http://sites.fas.harvard.edu/~cs165/papers/comer.pdf
* http://www.cburch.com/cs/340/reading/btree/index.html#s3
*
* @param idx idx of the interval
*/
removeChild(idx) {
// Removing the interval
this.keys.splice(idx, 1);
this.maximums.splice(idx, 1);
this.getChildren().splice(idx, 1);
if (this.isChildNewRoot()) {
this.getChildren()[0].setParent(null); //new Root
return;
}
if (this.isUnderflow())
this.handleUnderflow();
else if (!this.isRoot())
this.updateParentValues(); // Update parent keys
}
}
exports.IBplusNode = IBplusNode;