UNPKG

forerunnerdb

Version:

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

676 lines (580 loc) 20.1 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: Document.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: Document.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; // TODO: Remove the _update* methods because we are already mixing them // TODO: in now via Mixin.Updating and update autobind to extend the _update* // TODO: methods like we already do with collection var Shared, Collection, Overload, Db, Path; Shared = require('./Shared'); /** * Creates a new Document instance. Documents allow you to create individual * objects that can have standard ForerunnerDB CRUD operations run against * them, as well as data-binding if the AutoBind module is included in your * project. * @name Document * @class Document * @constructor */ var FdbDocument = function () { this.init.apply(this, arguments); }; FdbDocument.prototype.init = function (name) { this._name = name; this._data = {}; }; Shared.addModule('Document', FdbDocument); Shared.mixin(FdbDocument.prototype, 'Mixin.Common'); Shared.mixin(FdbDocument.prototype, 'Mixin.Events'); Shared.mixin(FdbDocument.prototype, 'Mixin.ChainReactor'); Shared.mixin(FdbDocument.prototype, 'Mixin.Constants'); Shared.mixin(FdbDocument.prototype, 'Mixin.Triggers'); Shared.mixin(FdbDocument.prototype, 'Mixin.Matching'); Shared.mixin(FdbDocument.prototype, 'Mixin.Updating'); Shared.mixin(FdbDocument.prototype, 'Mixin.Tags'); Overload = require('./Overload'); Collection = require('./Collection'); Db = Shared.modules.Db; Path = require('./Path'); /** * Gets / sets the current state. * @func state * @memberof Document * @param {String=} val The name of the state to set. * @returns {*} */ Shared.synthesize(FdbDocument.prototype, 'state'); /** * Gets / sets the db instance this class instance belongs to. * @func db * @memberof Document * @param {Db=} db The db instance. * @returns {*} */ Shared.synthesize(FdbDocument.prototype, 'db'); /** * Gets / sets the document name. * @func name * @memberof Document * @param {String=} val The name to assign * @returns {*} */ Shared.synthesize(FdbDocument.prototype, 'name'); /** * Sets the data for the document. * @func setData * @memberof Document * @param data * @param options * @returns {Document} */ FdbDocument.prototype.setData = function (data, options) { var i, eventData, $unset; if (data) { options = options || { $decouple: true }; if (options &amp;&amp; options.$decouple === true) { data = this.decouple(data); } if (this._linked) { $unset = {}; // Remove keys that don't exist in the new data from the current object for (i in this._data) { if (i.substr(0, 6) !== 'jQuery' &amp;&amp; this._data.hasOwnProperty(i)) { // Check if existing data has key if (data[i] === undefined) { // Add property name to those to unset $unset[i] = 1; } } } data.$unset = $unset; // Now update the object with new data this.updateObject(this._data, data, {}); } else { // Straight data assignment this._data = data; } eventData = {type: 'setData', data: this.decouple(this._data)}; this.emit('immediateChange', eventData); this.deferEmit('change', eventData); } return this; }; /** * Gets the document's data returned as a single object. * @func find * @memberof Document * @param {Object} query The query object - currently unused, just * provide a blank object e.g. {} * @param {Object=} options An options object. * @returns {Object} The document's data object. */ FdbDocument.prototype.find = function (query, options) { var result; if (options &amp;&amp; options.$decouple === false) { result = this._data; } else { result = this.decouple(this._data); } return result; }; /** * Finds sub-documents in this document. * @param {Object} match A query to check if this document should * be queried. If this document data doesn't match the query then * no results are returned. * @param {String} path The path string used to identify the * key in which sub-documents are stored in the parent document. * @param {Object=} subDocQuery The query to use when matching * which sub-documents to return. * @param {Object=} subDocOptions The options object to use * when querying for sub-documents. * @returns {*} */ FdbDocument.prototype.findSub = function (match, path, subDocQuery, subDocOptions) { return this._findSub([this.find(match)], path, subDocQuery, subDocOptions); }; FdbDocument.prototype._findSub = function (docArr, path, subDocQuery, subDocOptions) { var pathHandler = new Path(path), docCount = docArr.length, docIndex, subDocArr, subDocCollection = new Collection('__FDB_temp_' + this.objectId()).db(this._db), subDocResults, resultObj = { parents: docCount, subDocTotal: 0, subDocs: [], pathFound: false, err: '' }; subDocOptions = subDocOptions || {}; for (docIndex = 0; docIndex &lt; docCount; docIndex++) { subDocArr = pathHandler.value(docArr[docIndex])[0]; if (subDocArr) { subDocCollection.setData(subDocArr); subDocResults = subDocCollection.find(subDocQuery, subDocOptions); if (subDocOptions.$returnFirst &amp;&amp; subDocResults.length) { return subDocResults[0]; } if (subDocOptions.$split) { resultObj.subDocs.push(subDocResults); } else { resultObj.subDocs = resultObj.subDocs.concat(subDocResults); } resultObj.subDocTotal += subDocResults.length; resultObj.pathFound = true; } } // Drop the sub-document collection subDocCollection.drop(); if (!resultObj.pathFound) { resultObj.err = 'No objects found in the parent documents with a matching path of: ' + path; } // Check if the call should not return stats, if so return only subDocs array if (subDocOptions.$stats) { return resultObj; } else { return resultObj.subDocs[0]; } }; /** * Modifies the document. This will update the document with the data held in 'update'. * @func update * @memberof Document * @param {Object} query The query that must be matched for a document to be * operated on. * @param {Object} update The object containing updated key/values. Any keys that * match keys on the existing document will be overwritten with this data. Any * keys that do not currently exist on the document will be added to the document. * @param {Object=} options An options object. * @returns {Array} The items that were updated. */ FdbDocument.prototype.update = function (query, update, options) { var result = this.updateObject(this._data, update, query, options), eventData; if (result) { eventData = {type: 'update', data: this.decouple(this._data)}; this.emit('immediateChange', eventData); this.deferEmit('change', eventData); } }; /** * Internal method for document updating. * @func updateObject * @memberof Document * @param {Object} doc The document to update. * @param {Object} update The object with key/value pairs to update the document with. * @param {Object} query The query object that we need to match to perform an update. * @param {Object} options An options object. * @param {String=} path The current recursive path. * @param {String=} opType The type of update operation to perform, if none is specified * default is to set new data against matching fields. * @returns {Boolean} True if the document was updated with new / changed data or * false if it was not updated because the data was the same. * @private */ FdbDocument.prototype.updateObject = Collection.prototype.updateObject; /** * Determines if the passed key has an array positional mark (a dollar at the end * of its name). * @func _isPositionalKey * @memberof Document * @param {String} key The key to check. * @returns {Boolean} True if it is a positional or false if not. * @private */ FdbDocument.prototype._isPositionalKey = function (key) { return key.substr(key.length - 2, 2) === '.$'; }; /** * Updates a property on an object depending on if the collection is * currently running data-binding or not. * @func _updateProperty * @memberof Document * @param {Object} doc The object whose property is to be updated. * @param {String} prop The property to update. * @param {*} val The new value of the property. * @private */ FdbDocument.prototype._updateProperty = function (doc, prop, val) { if (this._linked) { window.jQuery.observable(doc).setProperty(prop, val); if (this.debug()) { console.log(this.logIdentifier() + ' Setting data-bound document property "' + prop + '"'); } } else { doc[prop] = val; if (this.debug()) { console.log(this.logIdentifier() + ' Setting non-data-bound document property "' + prop + '" to val "' + val + '"'); } } }; /** * Increments a value for a property on a document by the passed number. * @func _updateIncrement * @memberof Document * @param {Object} doc The document to modify. * @param {String} prop The property to modify. * @param {Number} val The amount to increment by. * @private */ FdbDocument.prototype._updateIncrement = function (doc, prop, val) { if (this._linked) { window.jQuery.observable(doc).setProperty(prop, doc[prop] + val); } else { doc[prop] += val; } }; /** * Changes the index of an item in the passed array. * @func _updateSpliceMove * @memberof Document * @param {Array} arr The array to modify. * @param {Number} indexFrom The index to move the item from. * @param {Number} indexTo The index to move the item to. * @private */ FdbDocument.prototype._updateSpliceMove = function (arr, indexFrom, indexTo) { if (this._linked) { window.jQuery.observable(arr).move(indexFrom, indexTo); if (this.debug()) { console.log(this.logIdentifier() + ' Moving data-bound document array index from "' + indexFrom + '" to "' + indexTo + '"'); } } else { arr.splice(indexTo, 0, arr.splice(indexFrom, 1)[0]); if (this.debug()) { console.log(this.logIdentifier() + ' Moving non-data-bound document array index from "' + indexFrom + '" to "' + indexTo + '"'); } } }; /** * Inserts an item into the passed array at the specified index. * @func _updateSplicePush * @memberof Document * @param {Array} arr The array to insert into. * @param {Number} index The index to insert at. * @param {Object} doc The document to insert. * @private */ FdbDocument.prototype._updateSplicePush = function (arr, index, doc) { if (arr.length > index) { if (this._linked) { window.jQuery.observable(arr).insert(index, doc); } else { arr.splice(index, 0, doc); } } else { if (this._linked) { window.jQuery.observable(arr).insert(doc); } else { arr.push(doc); } } }; /** * Inserts an item at the end of an array. * @func _updatePush * @memberof Document * @param {Array} arr The array to insert the item into. * @param {Object} doc The document to insert. * @private */ FdbDocument.prototype._updatePush = function (arr, doc) { if (this._linked) { window.jQuery.observable(arr).insert(doc); } else { arr.push(doc); } }; /** * Removes an item from the passed array. * @func _updatePull * @memberof Document * @param {Array} arr The array to modify. * @param {Number} index The index of the item in the array to remove. * @private */ FdbDocument.prototype._updatePull = function (arr, index) { if (this._linked) { window.jQuery.observable(arr).remove(index); } else { arr.splice(index, 1); } }; /** * Multiplies a value for a property on a document by the passed number. * @func _updateMultiply * @memberof Document * @param {Object} doc The document to modify. * @param {String} prop The property to modify. * @param {Number} val The amount to multiply by. * @private */ FdbDocument.prototype._updateMultiply = function (doc, prop, val) { if (this._linked) { window.jQuery.observable(doc).setProperty(prop, doc[prop] * val); } else { doc[prop] *= val; } }; /** * Renames a property on a document to the passed property. * @func _updateRename * @memberof Document * @param {Object} doc The document to modify. * @param {String} prop The property to rename. * @param {Number} val The new property name. * @private */ FdbDocument.prototype._updateRename = function (doc, prop, val) { var existingVal = doc[prop]; if (this._linked) { window.jQuery.observable(doc).setProperty(val, existingVal); window.jQuery.observable(doc).removeProperty(prop); } else { doc[val] = existingVal; delete doc[prop]; } }; /** * Deletes a property on a document. * @func _updateUnset * @memberof Document * @param {Object} doc The document to modify. * @param {String} prop The property to delete. * @private */ FdbDocument.prototype._updateUnset = function (doc, prop) { if (this._linked) { window.jQuery.observable(doc).removeProperty(prop); } else { delete doc[prop]; } }; /** * Drops the document. * @func drop * @memberof Document * @returns {boolean} True if successful, false if not. */ FdbDocument.prototype.drop = function (callback) { if (!this.isDropped()) { if (this._db &amp;&amp; this._name) { if (this._db &amp;&amp; this._db._document &amp;&amp; this._db._document[this._name]) { this._state = 'dropped'; delete this._db._document[this._name]; delete this._data; this.emit('drop', this); if (callback) { callback(false, true); } delete this._listeners; return true; } } } else { return true; } return false; }; Db.prototype.document = new Overload('Db.prototype.document', { /** * Get a document with no name (generates a random name). If the * document does not already exist then one is created for that * name automatically. * @name document * @method Db.document * @func document * @memberof Db * @returns {Document} */ '': function () { return this.$main.call(this, { name: this.objectId() }); }, /** * Get a document by name. If the document does not already exist * then one is created for that name automatically. * @name document * @method Db.document * @func document * @memberof Db * @param {Object} data An options object or a document instance. * @returns {Document} */ 'object': function (data) { // Handle being passed an instance if (data instanceof FdbDocument) { if (data.state() !== 'droppped') { return data; } else { return this.$main.call(this, { name: data.name() }); } } return this.$main.call(this, data); }, /** * Get a document by name. If the document does not already exist * then one is created for that name automatically. * @name document * @method Db.document * @func document * @memberof Db * @param {String} documentName The name of the document. * @returns {Document} */ 'string': function (documentName) { return this.$main.call(this, { name: documentName }); }, /** * Get a document by name. If the document does not already exist * then one is created for that name automatically. * @name document * @method Db.document * @func document * @memberof Db * @param {String} documentName The name of the document. * @param {Object} options An options object. * @returns {Document} */ 'string, object': function (documentName, options) { options.name = documentName; return this.$main.call(this, options); }, '$main': function (options) { var self = this, name = options.name; if (!name) { if (!options || (options &amp;&amp; options.throwError !== false)) { throw(this.logIdentifier() + ' Cannot get document with undefined name!'); } return; } if (this._document &amp;&amp; this._document[name]) { return this._document[name]; } if (options &amp;&amp; options.autoCreate === false) { if (options &amp;&amp; options.throwError !== false) { throw(this.logIdentifier() + ' Cannot get document ' + name + ' because it does not exist and auto-create has been disabled!'); } return undefined; } if (this.debug()) { console.log(this.logIdentifier() + ' Creating document ' + name); } this._document = this._document || {}; this._document[name] = this._document[name] || new FdbDocument(name, options).db(this); // Listen for events on this document so we can fire global events // on the database in response to it self._document[name].on('change', function () { self.emit('change', self._document[name], 'document', name); }); self.deferEmit('create', self._document[name], 'document', name); return this._document[name]; } }); /** * Returns an array of documents the DB currently has. * @func documents * @memberof Db * @returns {Array} An array of objects containing details of each document * the database is currently managing. */ Db.prototype.documents = function () { var arr = [], item, i; for (i in this._document) { if (this._document.hasOwnProperty(i)) { item = this._document[i]; arr.push({ name: i, linked: item.isLinked !== undefined ? item.isLinked() : false }); } } return arr; }; Shared.finishModule('Document'); module.exports = FdbDocument;</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>