jsondb.js

/**
 * Creates or opens a JSON-DB database at the given root directory.
 * 
 * @constructor
 * @param {String} rootPath Root of the JsonDB file hierarchy.  The path must be a path within CompleteFTP's
 * virtual file-system (e.g. /databases).
 * 
 * @classdesc
 * Provides access to JSON-DB databases.  A JSON-DB database is a file-based object database where
 * objects are stored in JSON files.  JSON files may be arranged in an arbitrary directory hierarchy
 * under a single 'root' directory.  Though this class allows files to be written using the 
 * {@link JsonDB#write write}
 * method, any other means of writing files from within CompleteFTP or externally may also be used.
 * Objects in a JSON-DB database are accessed via an 'object-path'.  
 * 
 * The object-path in JSON-DB is analogous to a primary key in relational databases in the sense that
 * it uniquely defines a single entity.  It's more powerful than a primary key though since it 
 * facilitates hierarchical structuring of data like a traditional file-system.  An object path consists of a
 * file-path (with slash as a delimiter) and an optional object-path (with dot as a delimiter).  
 * For example, if a JSON file called 'b.json' is placed in
 * a directory 'a' under the root directory, and if that file defines an object with a property, 'c',
 * then that property may be read using the object-path, 'a/b.c'.  Notice that `/` separates the folder and
 * the file, and `.` separates the file and the property.
 * 
 * An existing JSON object may be updated using the {@link JsonDB#update update} method.  Individual objects may be read 
 * from a JSON-DB via the {@link JsonDB#read read} method.  More generic querying is supported via the {@link JsonDB#query query} method.
 * 
 * Since JSON-DB uses a file-system for storage, non-JSON files may be stored in the database 
 * alongside JSON objects.  The {@link JsonDB#writeText writeText} and {@link JsonDB#writeBase64 writeBase64} allows such files to be written 
 * using the same file-paths as JSON objects.
 * 
 * References between objects in a JSON-DB database are done via string properties with the suffix,
 * '_ref'.  The value is the object-path of the referenced object.  The {@link JsonDB#read read} method returns these
 * properties as strings.  The {@link JsonDB#dereference dereference} method may be used to resolve reference properties
 * to the objects that they reference.  The deferenced properties will have the name of the reference
 * property with the '_ref' suffix removed. 
 */
var JsonDB = function (rootPath) {

	/**
	 * Reads the object at the given path from the database.  The path may specify a directory of JSON 
	 * files, a specific JSON file, or an property within the object defined in a JSON file.  For 
	 * example, the path 'a/b.c' will reference the property called 'c' within the file with the path 
	 * 'a/b.json'.
	 * 
	 * @method
	 * @param {String} objectPath Path of the object.
	 * @param {Boolean} [fullResult] Should a full result object be returned, or just the object that was read?
	 *                             Defaults to false.
	 * @return {Object} The object returned depends on the 'fullResult' argument.  If fullresult is false then 
	 *                  the object at the path will be returned or an exception will be thrown if no object is 
	 *                  found.  If fullResult is true then an object with the following properties will be returned: 
	 *                  'target' the object at the path or null if not found; 'containers' an array of objects above 
	 *                  the target object in the object-path; 'path' the object path; 'error' the error that occurred 
	 *                  (if any).
	 */
	this.read = function (objectPath, fullResult) { };

	/**
	 * Returns an array of references to all JSON files matching the given file-path pattern.  This method only
	 * lists files, so the pattern must not reference fields inside objects.  Wildcards (i.e. * or ?) may be placed
	 * in any element of the file-path.
	 * 
	 * @method
	 * @param {String} pattern File-path pattern optionally containing wildcards.
	 * @param {Boolean} [includeFolders] Folders are included in the list if `true` otherwise folders are excluded (the default).
	 * @return {String[]} Array containing all matches.
	 */
	this.list = function (pattern, includeFolders) { };

	/**
	 * A JSON-DB may be queried using an object-path containing wild-cards and a comparison.  For example,
	 * a JSON-DB has a directory called `products` that contains a set of product objects stored in JSON files 
	 * whose names are the product IDs, and those objects have a property called `colour`.  To query for a certain
	 * colour, the pattern would be `products/*.colour`.  If we want to find all the green objects then
	 * we'd call the query method as follows: `myDb.query('products/*.colour', '=', 'green', 'string')`.
	 * 
	 * The result may be obtained via the callback or the return value.
	 * 
	 * @method
	 * @param {String} pattern Wildcard object-path.
	 * @param {String} comparer Comparison operator to use: `=`, `>`, `<`, `>=`, `<=`, `<>`, `like` or `between`.
	 * @param {Object} value Value to compare with.  Normally this is a single value, but if the operator is 
	 * `between` then `value` should be an array containing two values: `[minimum, maximum]`
	 * @param {String} type Type being queried for: `number`, `string` or `date`.
	 * @param {Function} [evaluator] Evaluation function.  If this is defined then the value returned from 
	 * the function is matched.  The referenced object is passed as an argument to the evaluator function.
	 * The JSS API is not available and cannot be used inside the evaluator function.
	 * E.g. `function(obj) { return obj.height * obj.width; }`
	 * @param {Function} [callback] Callback function to call for each match found.
	 * @return {String[]} Array containing all matching object references.
	 */
	this.query = function (pattern, comparer, value, type, evaluator, callback) { };

	/**
	 * Removes the object at the given path.
	 * 
	 * @method
	 * @param {String} objectPath Path of the object.
	 */
	this.remove = function (objectPath) { };

	/**
	 * Writes the object to a JSON file at the given file-path.  
	 * 
	 * @method
	 * @param {String} filePath Path of the file to be written.  Note that this path must only specify
	 *                          the file and not any object within a file.
	 * @param {Object} object Object to be written.
	 */
	this.write = function (filePath, object) { };

	/**
	 * Updates an object that already exists in the database using a callback method.  The callback
	 * function takes a single argument, which is the object that was read from the database.  This
	 * object may be modified within the callback.  The file containing the object will be written
	 * back to the database after the callback function returns.
	 * 
	 * @method
	 * @param {String} objectPath Path of object to be updated.  This may specify a JSON file 
	 *                            or an object within a file.
	 * @param {Function} callback Callback function that is called for the object.
	 */
	this.update = function () { };

	/**
	 * Resolves reference properties to the objects that they reference.  In this method, the JsonDB
	 * engine will go through all properties of the given object looking for references, i.e.
	 * string properties whose names end with `_ref`.  For each one that it finds it will read the
	 * object with that reference from the database and add a property to the given object whose
	 * name is the same as the reference property, but with the `_ref` suffix removed.  The original
	 * object is not modified.
	 *
	 * For example, if a database contains one object at `/companies/MyCompany` as follows:
	 * ```
	 * {
	 *     "name": "MyCompany",
	 *     "number": 123456
	 * }
	 * ```
	 * and another as `/people/JohnSmith` as follows:
	 * ```
	 * {
	 *     "firstName": "John",
	 *     "lastName": "Smith",
	 *     "company_ref": "/companies/MyCompany"
	 * }
	 * ```
	 * then if `dereference` is called on `/people/JohnSmith` then it will return:
	 * ```
	 * {
	 *     "firstName": "John",
	 *     "lastName": "Smith",
	 *     "company": {
	 *         "name": "MyCompany",
	 *         "number": 123456
	 *     }
	 * }
	 * ```
	 * 
	 * @method
	 * @param {Object} object Object to be dereferenced.  This object is not modified.
	 * @return {Object} Object with all reference properties dereferenced.
	 */
	this.dereference = function (object) { };

	/**
	 * Writes a text file at the given location (relative to the JSON-DB root).
	 * 
	 * @method
	 * @param {String} filePath Path of the file to write the text to.
	 * @param {String} text Text to write. 
	 */
	this.writeText = function (filePath, text) { };

	/**
	 * Append text to a file at the given location (relative to the JSON-DB root).
	 * 
	 * @method
	 * @param {String} filePath Path of the file to append the text to.
	 * @param {String} text Text to append. 
	 */
	this.appendText = function (filePath, text) { };

	/**
	 * Reads the text file at the given location and returns the content as a string.
	 * 
	 * @method
	 * @param {String} filePath Path of the file to read.
	 * @return {String} Content of file
	 */
	this.readText = function (filePath) { };

	/**
	 * Writes a binary file at the given location (relative to the JSON-DB root).
	 * 
	 * @method
	 * @param {String} filePath Path of the file to write the binary data to.
	 * @param {String} base64 Base-64 encoded binary data to write. 
	 */
	this.writeBase64 = function (filePath, base64) { };

	/**
	 * Reads the binary file at the given location and returns the content as a base-64 encoded string.
	 * 
	 * @method
	 * @param {String} filePath Path of the file to read.
	 * @return {String} Content of file as base-64
	 */
	this.readBase64 = function (filePath) { };
}