UNPKG

bgraph

Version:

B-Graph. B-Tree that can perform ranged search.

1,330 lines (1,086 loc) 38.6 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["BGraph"] = factory(); else root["BGraph"] = factory(); })(global, function() { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ 922: /***/ ((module) => { module.exports = class BGraphData { constructor() { this.key; this.ref; } } /***/ }), /***/ 136: /***/ ((module) => { module.exports = class BGraphNode { constructor() { /** * @type {Data[]} */ this.dataList = []; /** * @type {BGraphNode[]} */ this.children = []; /** * @type {boolean} */ this.isLeaf = true; } } /***/ }), /***/ 138: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const BGraphData = __webpack_require__(922); const BGraphNode = __webpack_require__(136); const ListNode = __webpack_require__(972); const Mutex = __webpack_require__(49); module.exports = class BGraph { constructor(order = 5, compareCallback = undefined) { this.root; this.start; this.end; this.order = order; this.size = 0; this.height = 1; this.readingCount = 0; this.lock = false; this.mutex = new Mutex(); this.compareCallback = compareCallback; if (this.compareCallback === undefined) this.compareCallback = this.compareKey; } async search(key) { let tempNode = this.root; if (!key || typeof key !== 'string' || !tempNode) return undefined; await this.waitForUnlock(); this.readingCount = this.readingCount + 1; let height = this.height; let nextNodeIndex = 0; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data = dataList[j]; let dataKey = data.key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult == 0) { this.readingCount = this.readingCount - 1; return data.ref.value; } else if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; } tempNode = tempNode.children[nextNodeIndex]; } this.readingCount = this.readingCount - 1; return undefined; } async searchRange(key, total, position = 0) { let tempNode = this.root; if (!key || typeof key !== 'string' || !tempNode) return undefined; await this.waitForUnlock(); this.readingCount = this.readingCount + 1; let height = this.height; let nextNodeIndex = 0; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data = dataList[j]; let dataKey = data.key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult == 0) { let result = []; let listNode = data.ref; for (let k = 0; k < position; k++) { listNode = listNode.next; if (listNode === undefined) { this.readingCount = this.readingCount - 1; return []; } } for (let k = 0; k < total; k++) { result.push({ key: listNode.key, value: listNode.value }); if (listNode.next === undefined) break; else listNode = listNode.next; } this.readingCount = this.readingCount - 1; return result; } else if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; } tempNode = tempNode.children[nextNodeIndex]; } this.readingCount = this.readingCount - 1; return []; } async searchRangeBackward(key, total, position = 0) { let tempNode = this.root; if (!key || typeof key !== 'string' || !tempNode) return undefined; await this.waitForUnlock(); this.readingCount = this.readingCount + 1; let height = this.height; let nextNodeIndex = 0; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data = dataList[j]; let dataKey = data.key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult == 0) { let result = []; let listNode = data.ref; for (let k = 0; k < position; k++) { listNode = listNode.prev; if (listNode === undefined) { this.readingCount = this.readingCount - 1; return []; } } for (let k = 0; k < total; k++) { result.splice(0, 0, { key: listNode.key, value: listNode.value }); if (listNode.prev === undefined) break; else listNode = listNode.prev; } this.readingCount = this.readingCount - 1; return result; } else if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; } tempNode = tempNode.children[nextNodeIndex]; } this.readingCount = this.readingCount - 1; return []; } async searchKeyContains(substring, total, position = 0, lastKey = "") { if (!substring || typeof substring !== 'string') return undefined; await this.waitForUnlock(); this.readingCount = this.readingCount + 1; let result; if (lastKey === "") { result = await this.searchKeyContainsFromListNode(this.start, substring, total, position); } else { let listNode = await this.searchListNode(lastKey); result = await this.searchKeyContainsFromListNode(listNode, substring, total, position); } this.readingCount = this.readingCount - 1; return result; } async searchKeyContainsFromListNode(listNode, substring, total, position) { let tempNode = listNode; if (!tempNode) return undefined; let result = []; let count = 0; let positionCount = 0; while (tempNode !== undefined && tempNode.key !== undefined) { let key = tempNode.key; if (key.includes(substring)) { if (positionCount >= position) { result.push({ key: key, value: tempNode.value }); count++; } else positionCount++; } if (count >= total) return result; tempNode = tempNode.next; } return result; } async searchValueContains(substring, total, position = 0, lastKey = "") { if (!substring || typeof substring !== 'string') return undefined; await this.waitForUnlock(); this.readingCount = this.readingCount + 1; let result; if (lastKey === "") { result = await this.searchValueContainsFromListNode(this.start, substring, total, position); } else { let listNode = await this.searchListNode(lastKey); result = await this.searchValueContainsFromListNode(listNode, substring, total, position); } this.readingCount = this.readingCount - 1; return result; } async searchValueContainsFromListNode(listNode, substring, total, position) { let tempNode = listNode; if (!tempNode) return undefined; let result = []; let count = 0; let positionCount = 0; while (tempNode !== undefined) { let value = tempNode.value; if (value.includes(substring)) { if (positionCount >= position) { result.push({ key: tempNode.key, value: value }); count++; } else positionCount++; } if (count >= total) return result; tempNode = tempNode.next; } return result; } async searchListNode(key) { let tempNode = this.root; if (!key || typeof key !== 'string' || !tempNode) return undefined; let height = this.height; let nextNodeIndex = 0; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data = dataList[j]; let dataKey = data.key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult == 0) { return data.ref; } else if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; } tempNode = tempNode.children[nextNodeIndex]; } return undefined; } async insert(key, value) { if (!key || typeof key !== 'string') return false; await this.waitForReadingCountToBeZero() this.lock = true; await this.mutex.acquire(); let data = new BGraphData(); data.key = key; if (this.root === undefined || this.root === null) { this.root = new BGraphNode(); } let result = false; let tempNode = this.root; let order = this.order; let height = this.height; let nextNodeIndex = 0; let indexes = [0]; let parents = []; let newListNode = new ListNode(); newListNode.key = key; newListNode.value = value; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; if (tempNode.isLeaf || dataList.length == 0) { let pos = await this.getDataPos(data, dataList); if (this.size == 0) { this.start = newListNode; this.end = newListNode; data.ref = this.start; } else if (pos >= dataList.length) { let prevListNode = dataList[pos - 1].ref; let nextListNode = prevListNode.next; if (prevListNode !== undefined && prevListNode.key !== undefined) { if (prevListNode.key === key) { result = false; break; } } if (nextListNode !== undefined && nextListNode.key !== undefined) { if (nextListNode.key === key) { result = false; break; } } prevListNode.next = newListNode; newListNode.prev = prevListNode; if (nextListNode !== undefined && nextListNode.key !== undefined) { nextListNode.prev = newListNode; newListNode.next = nextListNode; } else this.end = newListNode; data.ref = newListNode; } else { let nextListNode = dataList[pos].ref; let prevListNode = nextListNode.prev; if (prevListNode !== undefined && prevListNode.key !== undefined) { if (prevListNode.key === key) { result = false; break; } } if (nextListNode !== undefined && nextListNode.key !== undefined) { if (nextListNode.key === key) { result = false; break; } } if (prevListNode !== undefined && prevListNode.key !== undefined) { prevListNode.next = newListNode; newListNode.prev = prevListNode; } else this.start = newListNode; nextListNode.prev = newListNode; newListNode.next = nextListNode; data.ref = newListNode; } dataList.splice(pos, 0, data); let numberOfData = dataList.length; if (numberOfData == order) { if (i == 0) { await this.splitRoot(tempNode); tempNode.isLeaf = false; } else await this.split(tempNode, parents, parents.length - 1, indexes); } this.size = this.size + 1; result = true; break; } let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data2 = dataList[j]; let compareResult = await this.compareCallback(data2.key, data.key); if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; else if (compareResult == 0) { await this.mutex.release(); this.lock = false; return false; } } parents.push(tempNode); indexes.push(nextNodeIndex); tempNode = tempNode.children[nextNodeIndex]; } await this.mutex.release(); this.lock = false; return result; } async splitRoot(node) { let order = this.order; let dataList = node.dataList; if (node.dataList.length == order) { let leftNode = new BGraphNode(); let leftNodeDataList = leftNode.dataList; let leftNodeChildren = leftNode.children; let rightNode = new BGraphNode(); let rightNodeDataList = rightNode.dataList; let rightNodeChildren = rightNode.children; let t = Math.ceil(order / 2); for (let i = t; i < order; i++) { await this.addData(await this.removeData(t, dataList), rightNodeDataList); } while (dataList.length > 1) { await this.addData(await this.removeData(0, dataList), leftNodeDataList); } let children = node.children; if (children.length != 0) { for (let i = 0; i < leftNodeDataList.length + 1; i++) { await this.addChild(await this.removeChild(0, children), i, leftNodeChildren); } while (children.length > 0) { await this.addChild(await this.removeChild(0, children), rightNodeChildren.length, rightNodeChildren); } leftNode.isLeaf = false; rightNode.isLeaf = false; } await this.addChild(leftNode, 0, node.children); await this.addChild(rightNode, 1, node.children); this.height = this.height + 1; } } async split(child, parents, parentIndex, indexes) { let order = this.order; if (child.dataList.length == order) { let parent = parents[parentIndex]; let t = Math.ceil(order / 2); let newChild = new BGraphNode(); for (let i = t; i < order; i++) { if (child.dataList.length > t) { await this.addData(await this.removeData(t, child.dataList), newChild.dataList) } else { break; } } if (!child.isLeaf) { let childrenSize = child.children.length; let half = Math.ceil(childrenSize / 2); if (order % 2 == 0) half -= 1; for (let i = half; i < childrenSize; i++) { if (childrenSize > half) { await this.addChild(await this.removeChild(half, child.children), newChild.children.length, newChild.children); } else { break; } } newChild.isLeaf = false; } await this.addData(await this.removeData(t - 1, child.dataList), parent.dataList) let pos = indexes[parentIndex + 1] + 1; await this.addChild(newChild, pos, parent.children); parent.isLeaf = false; if (parentIndex > 0) { await this.split(parent, parents, parentIndex - 1, indexes); } else if (parentIndex == 0) { await this.splitRoot(parents[0]); } } } async update(key, value) { let tempNode = this.root; if (!key || !tempNode) return false; await this.waitForReadingCountToBeZero() this.lock = true; await this.mutex.acquire(); let height = this.height; let nextNodeIndex = 0; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data = dataList[j]; let dataKey = data.key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult == 0) { data.ref.value = value; await this.mutex.release(); this.lock = false; return true; } else if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; } tempNode = tempNode.children[nextNodeIndex]; } await this.mutex.release(); this.lock = false; return false; } async delete(key) { let tempNode = this.root; let height = this.height; let nextNodeIndex = 0; let indexes = [0]; let parents = []; if (!key || typeof key !== 'string' || !tempNode) return false; await this.waitForReadingCountToBeZero() this.lock = true; await this.mutex.acquire(); if (this.size == 1) { if (this.start.key === key) { this.root = undefined; this.start = undefined; this.end = undefined; this.size = 0; this.height = 1; await this.mutex.release(); this.lock = false; return true; } else { await this.mutex.release(); this.lock = false; return false; } } for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let dataKey = dataList[j].key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; else if (compareResult == 0) { await this.deleteFromNode(tempNode, j, parents, indexes); await this.mutex.release(); this.lock = false; return true; } } if (tempNode.isLeaf) { await this.mutex.release(); this.lock = false; return false; } parents.push(tempNode); indexes.push(nextNodeIndex); tempNode = tempNode.children[nextNodeIndex]; } await this.mutex.release(); this.lock = false; return false; } async deleteFromNode(node, dataIndex, parents, indexes) { if (node.isLeaf) { await this.removeListNode(node.dataList[dataIndex].ref); await this.removeData(dataIndex, node.dataList); this.size = this.size - 1; await this.balanceAfterDeletion(node, parents, parents.length - 1, indexes); return true; } else { parents.push(node); indexes.push(dataIndex); let maxNode = node.children[dataIndex]; while (!maxNode.isLeaf) { parents.push(maxNode); indexes.push(maxNode.children.length - 1); maxNode = maxNode.children[maxNode.children.length - 1]; } await this.removeListNode(node.dataList[dataIndex].ref); await this.removeData(dataIndex, node.dataList); this.size = this.size - 1; await this.addData(await this.removeData(maxNode.dataList.length - 1, maxNode.dataList), node.dataList) await this.balanceAfterDeletion(maxNode, parents, parents.length - 1, indexes); return true; } } async balanceAfterDeletion(node, parents, parentIndex, indexes) { let order = this.order; let mk = Math.ceil(order / 2) - 1; let dataList = node.dataList; let childIndex = indexes[parentIndex + 1]; if (dataList.length < mk) { if (parentIndex < 0) { if (dataList.length == 0) { let child = node.children[0]; this.height = this.height - 1; this.root = child; return true; } } else { let parent = parents[parentIndex]; if (childIndex > 0 && parent.children[childIndex - 1].dataList.length > mk) { await this.borrowFromLeft(node, childIndex, parent); } else if (childIndex < parent.dataList.length && parent.children[childIndex + 1].dataList.length > mk) { await this.borrowFromRight(node, childIndex, parent); } else if (childIndex == 0) { await this.mergeRight(node, childIndex, parent); await this.balanceAfterDeletion(parent, parents, parentIndex - 1, indexes); } else { await this.mergeRight(parent.children[childIndex - 1], childIndex - 1, parent); await this.balanceAfterDeletion(parent, parents, parentIndex - 1, indexes); } } } } async borrowFromLeft(child, childIndex, parent) { let leftChild = parent.children[childIndex - 1]; if (!child.isLeaf) { await this.addChild(await this.removeChild(leftChild.dataList.length, leftChild.children), 0, child.children); } await this.addData(await this.removeData(childIndex - 1, parent.dataList), child.dataList); await this.addData(await this.removeData(leftChild.dataList.length - 1, leftChild.dataList), parent.dataList); } async borrowFromRight(child, childIndex, parent) { let rightChild = parent.children[childIndex + 1]; await this.addData(await this.removeData(childIndex, parent.dataList), child.dataList); await this.addData(await this.removeData(0, rightChild.dataList), parent.dataList); if (!child.isLeaf) { await this.addChild(await this.removeChild(0, rightChild.children), child.dataList.length, child.children); } } async mergeRight(child, childIndex, parent) { let rightChild = parent.children[childIndex + 1]; await this.addData(await this.removeData(childIndex, parent.dataList), child.dataList); while (rightChild.dataList.length > 0) { await this.addData(await this.removeData(0, rightChild.dataList), child.dataList); } if (!child.isLeaf) { let childChildren = child.children; let rightChildChildren = rightChild.children; while (rightChildChildren.length > 0) { await this.addChild(await this.removeChild(0, rightChildChildren), childChildren.length, childChildren); } } await this.removeChild(childIndex + 1, parent.children); } async removeListNode(listNode) { let prevListNode = listNode.prev; let nextListNode = listNode.next; if (prevListNode === undefined) { nextListNode.prev = undefined; this.start = nextListNode; } else prevListNode.next = nextListNode; if (nextListNode === undefined) this.end = prevListNode; else nextListNode.prev = prevListNode; } async getDataPos(data, dataList) { let pos = 0; let dataListSize = dataList.length; while (pos < dataListSize && await this.compareCallback(dataList[pos].key, data.key) < 0) { pos++; } return pos; } async addData(data, dataList) { let pos = 0; let dataListSize = dataList.length; while (pos < dataListSize && await this.compareCallback(dataList[pos].key, data.key) < 0) { pos++; } await dataList.splice(pos, 0, data); } async removeData(pos, dataList) { let dataListSize = dataList.length; if (pos >= dataListSize) { return null; } return dataList.splice(pos, 1)[0]; } async addChild(node, pos, children) { children.splice(pos, 0, node); } async removeChild(pos, children) { return children.splice(pos, 1)[0]; } async serialize() { let result = {}; let size = this.size; result.order = this.order; result.size = size; result.height = this.height; if (size == 0) return JSON.stringify(result); let listNode = this.start; let list = {}; let tracker; list.key = listNode.key; list.value = listNode.value; list.next = {}; tracker = list.next; listNode = listNode.next; for (let i = 1; i < size; i++) { if (listNode === undefined) break; tracker.key = listNode.key; tracker.value = listNode.value; if (i < size - 1) tracker.next = {}; tracker = tracker.next; listNode = listNode.next; } result.list = list; result.btree = JSON.parse(JSON.stringify(this.root, ['dataList', 'children', 'isLeaf', 'key'])); return JSON.stringify(result); } async serializeToObj() { let result = {}; let size = this.size; result.order = this.order; result.size = size; result.height = this.height; if (size == 0) return result; let listNode = this.start; let list = {}; let tracker; list.key = listNode.key; list.value = listNode.value; list.next = {}; tracker = list.next; listNode = listNode.next; for (let i = 1; i < size; i++) { if (listNode === undefined) break; tracker.key = listNode.key; tracker.value = listNode.value; if (i < size - 1) tracker.next = {}; tracker = tracker.next; listNode = listNode.next; } result.list = list; result.btree = JSON.parse(JSON.stringify(this.root, ['dataList', 'children', 'isLeaf', 'key'])); return result; } async deserialize(string) { let data = JSON.parse(string); this.order = data.order; this.size = data.size; this.height = data.height; if (data.size == 0) { this.root = undefined; this.start = undefined; this.end = undefined; return; } this.start = data.list; this.root = data.btree; let listNode = this.start; let prevNode = undefined; for (let i = 0; i < this.size; i++) { listNode.prev = prevNode; await this.connectRef(listNode); if (listNode.next === undefined) this.end = listNode; prevNode = listNode; listNode = listNode.next; } } async deserializeFromObj(data) { if (!data || data === null || data === undefined) return false; this.order = data.order; this.size = data.size; this.height = data.height; if (data.size == 0) { this.root = undefined; this.start = undefined; this.end = undefined; return; } this.start = data.list; this.root = data.btree; let listNode = this.start; let prevNode = undefined; for (let i = 0; i < this.size; i++) { listNode.prev = prevNode; await this.connectRef(listNode); if (listNode.next === undefined) this.end = listNode; prevNode = listNode; listNode = listNode.next; } } async connectRef(node) { let tempNode = this.root; if (!node || !tempNode || !node.key) return false; let key = node.key; let height = this.height; let nextNodeIndex = 0; for (let i = 0; i < height; i++) { let dataList = tempNode.dataList; let dataListSize = dataList.length; for (let j = 0; j < dataListSize; j++) { nextNodeIndex = j; let data = dataList[j]; let dataKey = data.key; let compareResult = await this.compareCallback(dataKey, key); if (compareResult == 0) { data.ref = node; return true; } else if (compareResult > 0) break; else if (compareResult < 0) nextNodeIndex = nextNodeIndex + 1; } tempNode = tempNode.children[nextNodeIndex]; } return false; } async getAllKeys() { let tempNode = this.start; if (!tempNode) return []; let result = []; while (tempNode !== undefined) { if (tempNode.key === undefined) break; result.push(tempNode.key); tempNode = tempNode.next; } return result; } async getAllValues() { let tempNode = this.start; if (!tempNode) return []; let result = []; while (tempNode !== undefined) { if (tempNode.value === undefined) break; result.push(tempNode.value); tempNode = tempNode.next; } return result; } async getAll() { let tempNode = this.start; if (!tempNode) return []; let result = []; while (tempNode !== undefined) { if (tempNode.key === undefined || tempNode.value === undefined) break; result.push({ key: tempNode.key, value: tempNode.value }); tempNode = tempNode.next; } return result; } async compareKey(key1, key2) { return key1.localeCompare(key2); } waitForReadingCountToBeZero() { return new Promise(resolve => { const checkCount = () => { if (this.readingCount === 0) { resolve(); } else { setTimeout(checkCount, 10); } }; checkCount(); }); } waitForUnlock() { return new Promise(resolve => { const checkCount = () => { if (this.lock == false) { resolve(); } else { setTimeout(checkCount, 10); } }; checkCount(); }); } } /***/ }), /***/ 972: /***/ ((module) => { module.exports = class ListNode { constructor() { this.next; this.key; this.value; this.prev; } } /***/ }), /***/ 49: /***/ ((module) => { module.exports = class Mutex { constructor() { this.locked = false; this.waitingQueue = []; } async acquire() { return new Promise((resolve) => { if (!this.locked) { this.locked = true; resolve(); } else { this.waitingQueue.push(resolve); } }); } async release() { if (this.waitingQueue.length > 0) { const resolve = this.waitingQueue.shift(); resolve(); } else { this.locked = false; } } } /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module is referenced by other modules so it can't be inlined /******/ var __webpack_exports__ = __webpack_require__(138); /******/ /******/ return __webpack_exports__; /******/ })() ; });