UNPKG

forerunnerdb

Version:

A NoSQL document store database for browsers and Node.js.

744 lines (605 loc) 20 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: BinaryTree.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: BinaryTree.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; var Shared = require('./Shared'), Path = require('./Path'), sharedPathSolver = new Path(); var BinaryTree = function (data, compareFunc, hashFunc) { this.init.apply(this, arguments); }; BinaryTree.prototype.init = function (data, index, primaryKey, compareFunc, hashFunc) { this._store = []; this._keys = []; if (primaryKey !== undefined) { this.primaryKey(primaryKey); } if (index !== undefined) { this.index(index); } if (compareFunc !== undefined) { this.compareFunc(compareFunc); } if (hashFunc !== undefined) { this.hashFunc(hashFunc); } if (data !== undefined) { this.data(data); } }; Shared.addModule('BinaryTree', BinaryTree); Shared.mixin(BinaryTree.prototype, 'Mixin.ChainReactor'); Shared.mixin(BinaryTree.prototype, 'Mixin.Sorting'); Shared.mixin(BinaryTree.prototype, 'Mixin.Common'); Shared.synthesize(BinaryTree.prototype, 'compareFunc'); Shared.synthesize(BinaryTree.prototype, 'hashFunc'); Shared.synthesize(BinaryTree.prototype, 'indexDir'); Shared.synthesize(BinaryTree.prototype, 'primaryKey'); Shared.synthesize(BinaryTree.prototype, 'keys'); Shared.synthesize(BinaryTree.prototype, 'index', function (index) { if (index !== undefined) { if (this.debug()) { console.log('Setting index', index, sharedPathSolver.parse(index, true)); } // Convert the index object to an array of key val objects this.keys(sharedPathSolver.parse(index, true)); } return this.$super.call(this, index); }); /** * Remove all data from the binary tree. */ BinaryTree.prototype.clear = function () { delete this._data; delete this._left; delete this._right; this._store = []; }; /** * Sets this node's data object. All further inserted documents that * match this node's key and value will be pushed via the push() * method into the this._store array. When deciding if a new data * should be created left, right or middle (pushed) of this node the * new data is checked against the data set via this method. * @param val * @returns {*} */ BinaryTree.prototype.data = function (val) { if (val !== undefined) { this._data = val; if (this._hashFunc) { this._hash = this._hashFunc(val); } return this; } return this._data; }; /** * Pushes an item to the binary tree node's store array. * @param {*} val The item to add to the store. * @returns {*} */ BinaryTree.prototype.push = function (val) { if (val !== undefined) { this._store.push(val); return this; } return false; }; /** * Pulls an item from the binary tree node's store array. * @param {*} val The item to remove from the store. * @returns {*} */ BinaryTree.prototype.pull = function (val) { if (val !== undefined) { var index = this._store.indexOf(val); if (index > -1) { this._store.splice(index, 1); return this; } } return false; }; /** * Default compare method. Can be overridden. * @param a * @param b * @returns {Number} * @private */ BinaryTree.prototype._compareFunc = function (a, b) { // Loop the index array var i, indexData, result = 0; for (i = 0; i &lt; this._keys.length; i++) { indexData = this._keys[i]; if (indexData.value === 1) { result = this.sortAscIgnoreUndefined(sharedPathSolver.get(a, indexData.path), sharedPathSolver.get(b, indexData.path)); } else if (indexData.value === -1) { result = this.sortDescIgnoreUndefined(sharedPathSolver.get(a, indexData.path), sharedPathSolver.get(b, indexData.path)); } if (this.debug()) { console.log('Compared %s with %s order %d in path %s and result was %d', sharedPathSolver.get(a, indexData.path), sharedPathSolver.get(b, indexData.path), indexData.value, indexData.path, result); } if (result !== 0) { if (this.debug()) { console.log('Retuning result %d', result); } return result; } } if (this.debug()) { console.log('Retuning result %d', result); } return result; }; /** * Default hash function. Can be overridden. * @param obj * @private */ BinaryTree.prototype._hashFunc = function (obj) { /*var i, indexData, hash = ''; for (i = 0; i &lt; this._keys.length; i++) { indexData = this._keys[i]; if (hash) { hash += '_'; } hash += obj[indexData.path]; } return hash;*/ return obj[this._keys[0].path]; }; /** * Removes (deletes reference to) either left or right child if the passed * node matches one of them. * @param {BinaryTree} node The node to remove. */ BinaryTree.prototype.removeChildNode = function (node) { if (this._left === node) { // Remove left delete this._left; } else if (this._right === node) { // Remove right delete this._right; } }; /** * Returns the branch this node matches (left or right). * @param node * @returns {String} */ BinaryTree.prototype.nodeBranch = function (node) { if (this._left === node) { return 'left'; } else if (this._right === node) { return 'right'; } }; /** * Inserts a document into the binary tree. * @param data * @returns {*} */ BinaryTree.prototype.insert = function (data) { var result, inserted, failed, i; if (data instanceof Array) { // Insert array of data inserted = []; failed = []; for (i = 0; i &lt; data.length; i++) { if (this.insert(data[i])) { inserted.push(data[i]); } else { failed.push(data[i]); } } return { inserted: inserted, failed: failed }; } if (this.debug()) { console.log('Inserting', data); } if (!this._data) { if (this.debug()) { console.log('Node has no data, setting data', data); } // Insert into this node (overwrite) as there is no data this.data(data); //this.push(data); return true; } result = this._compareFunc(this._data, data); if (result === 0) { if (this.debug()) { console.log('Data is equal (currrent, new)', this._data, data); } //this.push(data); // Less than this node if (this._left) { // Propagate down the left branch this._left.insert(data); } else { // Assign to left branch this._left = new BinaryTree(data, this._index, this._binaryTree, this._compareFunc, this._hashFunc); this._left._parent = this; } return true; } if (result === -1) { if (this.debug()) { console.log('Data is greater (currrent, new)', this._data, data); } // Greater than this node if (this._right) { // Propagate down the right branch this._right.insert(data); } else { // Assign to right branch this._right = new BinaryTree(data, this._index, this._binaryTree, this._compareFunc, this._hashFunc); this._right._parent = this; } return true; } if (result === 1) { if (this.debug()) { console.log('Data is less (currrent, new)', this._data, data); } // Less than this node if (this._left) { // Propagate down the left branch this._left.insert(data); } else { // Assign to left branch this._left = new BinaryTree(data, this._index, this._binaryTree, this._compareFunc, this._hashFunc); this._left._parent = this; } return true; } return false; }; BinaryTree.prototype.remove = function (data) { var pk = this.primaryKey(), result, removed, i; if (data instanceof Array) { // Insert array of data removed = []; for (i = 0; i &lt; data.length; i++) { if (this.remove(data[i])) { removed.push(data[i]); } } return removed; } if (this.debug()) { console.log('Removing', data); } if (this._data[pk] === data[pk]) { // Remove this node return this._remove(this); } // Compare the data to work out which branch to send the remove command down result = this._compareFunc(this._data, data); if (result === -1 &amp;&amp; this._right) { return this._right.remove(data); } if (result === 1 &amp;&amp; this._left) { return this._left.remove(data); } return false; }; BinaryTree.prototype._remove = function (node) { var leftNode, rightNode; if (this._left) { // Backup branch data leftNode = this._left; rightNode = this._right; // Copy data from left node this._left = leftNode._left; this._right = leftNode._right; this._data = leftNode._data; this._store = leftNode._store; if (rightNode) { // Attach the rightNode data to the right-most node // of the leftNode leftNode.rightMost()._right = rightNode; } } else if (this._right) { // Backup branch data rightNode = this._right; // Copy data from right node this._left = rightNode._left; this._right = rightNode._right; this._data = rightNode._data; this._store = rightNode._store; } else { this.clear(); } return true; }; BinaryTree.prototype.leftMost = function () { if (!this._left) { return this; } else { return this._left.leftMost(); } }; BinaryTree.prototype.rightMost = function () { if (!this._right) { return this; } else { return this._right.rightMost(); } }; /** * Searches the binary tree for all matching documents based on the data * passed (query). * @param {Object} data The data / document to use for lookups. * @param {Object} options An options object. * @param {Operation} op An optional operation instance. Pass undefined * if not being used. * @param {Array=} resultArr The results passed between recursive calls. * Do not pass anything into this argument when calling externally. * @returns {*|Array} */ BinaryTree.prototype.lookup = function (data, options, op, resultArr) { var result = this._compareFunc(this._data, data); resultArr = resultArr || []; if (result === 0) { if (this._left) { this._left.lookup(data, options, op, resultArr); } resultArr.push(this._data); if (this._right) { this._right.lookup(data, options, op, resultArr); } } if (result === -1) { if (this._right) { this._right.lookup(data, options, op, resultArr); } } if (result === 1) { if (this._left) { this._left.lookup(data, options, op, resultArr); } } return resultArr; }; /** * Returns the entire binary tree ordered. * @param {String} type * @param resultArr * @returns {*|Array} */ BinaryTree.prototype.inOrder = function (type, resultArr) { resultArr = resultArr || []; if (this._left) { this._left.inOrder(type, resultArr); } switch (type) { case 'hash': resultArr.push(this._hash); break; case 'data': resultArr.push(this._data); break; default: resultArr.push({ key: this._data, arr: this._store }); break; } if (this._right) { this._right.inOrder(type, resultArr); } return resultArr; }; /** * Searches the binary tree for all matching documents based on the regular * expression passed. * @param path * @param val * @param regex * @param {Array=} resultArr The results passed between recursive calls. * Do not pass anything into this argument when calling externally. * @returns {*|Array} */ BinaryTree.prototype.startsWith = function (path, val, regex, resultArr) { var reTest, thisDataPathVal = sharedPathSolver.get(this._data, path), thisDataPathValSubStr = thisDataPathVal.substr(0, val.length), result; //regex = regex || new RegExp('^' + val); resultArr = resultArr || []; if (resultArr._visitedCount === undefined) { resultArr._visitedCount = 0; } resultArr._visitedCount++; resultArr._visitedNodes = resultArr._visitedNodes || []; resultArr._visitedNodes.push(thisDataPathVal); result = this.sortAscIgnoreUndefined(thisDataPathValSubStr, val); reTest = thisDataPathValSubStr === val; if (result === 0) { if (this._left) { this._left.startsWith(path, val, regex, resultArr); } if (reTest) { resultArr.push(this._data); } if (this._right) { this._right.startsWith(path, val, regex, resultArr); } } if (result === -1) { if (reTest) { resultArr.push(this._data); } if (this._right) { this._right.startsWith(path, val, regex, resultArr); } } if (result === 1) { if (this._left) { this._left.startsWith(path, val, regex, resultArr); } if (reTest) { resultArr.push(this._data); } } return resultArr; }; /*BinaryTree.prototype.find = function (type, search, resultArr) { resultArr = resultArr || []; if (this._left) { this._left.find(type, search, resultArr); } // Check if this node's data is greater or less than the from value var fromResult = this.sortAsc(this._data[key], from), toResult = this.sortAsc(this._data[key], to); if ((fromResult === 0 || fromResult === 1) &amp;&amp; (toResult === 0 || toResult === -1)) { // This data node is greater than or equal to the from value, // and less than or equal to the to value so include it switch (type) { case 'hash': resultArr.push(this._hash); break; case 'data': resultArr.push(this._data); break; default: resultArr.push({ key: this._data, arr: this._store }); break; } } if (this._right) { this._right.find(type, search, resultArr); } return resultArr; };*/ /** * * @param {String} type * @param {String} key The data key / path to range search against. * @param {Number} from Range search from this value (inclusive) * @param {Number} to Range search to this value (inclusive) * @param {Array=} resultArr Leave undefined when calling (internal use), * passes the result array between recursive calls to be returned when * the recursion chain completes. * @param {Path=} pathResolver Leave undefined when calling (internal use), * caches the path resolver instance for performance. * @returns {Array} Array of matching document objects */ BinaryTree.prototype.findRange = function (type, key, from, to, resultArr, pathResolver) { resultArr = resultArr || []; pathResolver = pathResolver || new Path(key); if (this._left) { this._left.findRange(type, key, from, to, resultArr, pathResolver); } // Check if this node's data is greater or less than the from value var pathVal = pathResolver.value(this._data), fromResult = this.sortAscIgnoreUndefined(pathVal, from), toResult = this.sortAscIgnoreUndefined(pathVal, to); if ((fromResult === 0 || fromResult === 1) &amp;&amp; (toResult === 0 || toResult === -1)) { // This data node is greater than or equal to the from value, // and less than or equal to the to value so include it switch (type) { case 'hash': resultArr.push(this._hash); break; case 'data': resultArr.push(this._data); break; default: resultArr.push({ key: this._data, arr: this._store }); break; } } if (this._right) { this._right.findRange(type, key, from, to, resultArr, pathResolver); } return resultArr; }; /*BinaryTree.prototype.findRegExp = function (type, key, pattern, resultArr) { resultArr = resultArr || []; if (this._left) { this._left.findRegExp(type, key, pattern, resultArr); } // Check if this node's data is greater or less than the from value var fromResult = this.sortAsc(this._data[key], from), toResult = this.sortAsc(this._data[key], to); if ((fromResult === 0 || fromResult === 1) &amp;&amp; (toResult === 0 || toResult === -1)) { // This data node is greater than or equal to the from value, // and less than or equal to the to value so include it switch (type) { case 'hash': resultArr.push(this._hash); break; case 'data': resultArr.push(this._data); break; default: resultArr.push({ key: this._data, arr: this._store }); break; } } if (this._right) { this._right.findRegExp(type, key, pattern, resultArr); } return resultArr; };*/ /** * Determines if the passed query and options object will be served * by this index successfully or not and gives a score so that the * DB search system can determine how useful this index is in comparison * to other indexes on the same collection. * @param query * @param queryOptions * @param matchOptions * @returns {{matchedKeys: Array, totalKeyCount: Number, score: number}} */ BinaryTree.prototype.match = function (query, queryOptions, matchOptions) { // Check if the passed query has data in the keys our index // operates on and if so, is the query sort matching our order var indexKeyArr, queryArr, matchedKeys = [], matchedKeyCount = 0, i; indexKeyArr = sharedPathSolver.parseArr(this._index, { verbose: true }); queryArr = sharedPathSolver.parseArr(query, matchOptions &amp;&amp; matchOptions.pathOptions ? matchOptions.pathOptions : { ignore:/\$/, verbose: true }); // Loop the query array and check the order of keys against the // index key array to see if this index can be used for (i = 0; i &lt; indexKeyArr.length; i++) { if (queryArr[i] === indexKeyArr[i]) { matchedKeyCount++; matchedKeys.push(queryArr[i]); } } return { matchedKeys: matchedKeys, totalKeyCount: queryArr.length, score: matchedKeyCount }; //return sharedPathSolver.countObjectPaths(this._keys, query); }; Shared.finishModule('BinaryTree'); module.exports = BinaryTree;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ActiveBucket.html">ActiveBucket</a></li><li><a href="Angular.html">Angular</a></li><li><a href="AutoBind.html">AutoBind</a></li><li><a href="Collection.html">Collection</a></li><li><a href="CollectionGroup.html">CollectionGroup</a></li><li><a href="Condition.html">Condition</a></li><li><a href="Core.html">Core</a></li><li><a href="Db.html">Db</a></li><li><a href="Document.html">Document</a></li><li><a href="Grid.html">Grid</a></li><li><a href="Highchart.html">Highchart</a></li><li><a href="Index2d.html">Index2d</a></li><li><a href="IndexBinaryTree.html">IndexBinaryTree</a></li><li><a href="IndexHashMap.html">IndexHashMap</a></li><li><a href="Infinilist.html">Infinilist</a></li><li><a href="KeyValueStore.html">KeyValueStore</a></li><li><a href="Metrics.html">Metrics</a></li><li><a href="MyModule.html">MyModule</a></li><li><a href="NodeApiClient.html">NodeApiClient</a></li><li><a href="NodeApiServer.html">NodeApiServer</a></li><li><a href="NodeRAS.html">NodeRAS</a></li><li><a href="Odm.html">Odm</a></li><li><a href="OldView.html">OldView</a></li><li><a href="Operation.html">Operation</a></li><li><a href="Overload.html">Overload</a></li><li><a href="Overview.html">Overview</a></li><li><a href="Overview_init.html">init</a></li><li><a href="Path.html">Path</a></li><li><a href="Persist.html">Persist</a></li><li><a href="Procedure.html">Procedure</a></li><li><a href="ReactorIO.html">ReactorIO</a></li><li><a href="Section.html">Section</a></li><li><a href="Serialiser.html">Serialiser</a></li><li><a href="Shared.overload.html">overload</a></li><li><a href="View.html">View</a></li></ul><h3>Mixins</h3><ul><li><a href="ChainReactor.html">ChainReactor</a></li><li><a href="Common.html">Common</a></li><li><a href="Constants.html">Constants</a></li><li><a href="Events.html">Events</a></li><li><a href="Matching.html">Matching</a></li><li><a href="Shared.html">Shared</a></li><li><a href="Sorting.html">Sorting</a></li><li><a href="Tags.html">Tags</a></li><li><a href="Triggers.html">Triggers</a></li><li><a href="Updating.html">Updating</a></li></ul><h3><a href="global.html">Global</a></h3> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Thu Mar 01 2018 11:34:22 GMT+0000 (GMT) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>