| /** |
| * Asterix SDK - Beta Version |
| * @author Eugenia Gabrielov <genia.likes.science@gmail.com> |
| * |
| * This is a Javascript helper file for generating AQL queries for AsterixDB (https://code.google.com/p/asterixdb/) |
| */ |
| |
| /** |
| * AsterixDBConnection |
| * |
| * This is a handler for connections to a local AsterixDB REST API Endpoint. |
| * This initialization takes as input a configuraiton object, and initializes |
| * same basic functionality. |
| */ |
| function AsterixDBConnection(configuration) { |
| // Initialize AsterixDBConnection properties |
| this._properties = {}; |
| |
| // Set dataverse as null for now, this needs to be set by the user. |
| this._properties["dataverse"] = ""; |
| |
| // By default, we will wait for calls to the REST API to complete. The query method |
| // sends a different setting when executed asynchronously. Calls that do not specify a mode |
| // will be executed synchronously. |
| this._properties["mode"] = "synchronous"; |
| |
| // These are the default error behaviors for Asterix and ajax errors, respectively. |
| // They can be overridden by calling initializing your AsterixDBConnection like so: |
| // adb = new AsterixDBConnection({ |
| // "error" : function(data) { |
| // // override here... |
| // }); |
| // and similarly for ajax_error, just pass in the configuration as a json object. |
| this._properties["error"] = function(data) { |
| alert("Asterix REST API Error:\n" + data["error-code"][0] + "\n" + data["error-code"][1]); |
| }; |
| |
| this._properties["ajax_error"] = function(message) { |
| alert("[Ajax Error]\n" + message); |
| }; |
| |
| // This is the default path to the local Asterix REST API. Can be overwritten for remote configurations |
| // or for demo setup purposes (such as with a proxy handler with Python or PHP. |
| this._properties["endpoint_root"] = "http://localhost:19002/"; |
| |
| // If we have passed in a configuration, we will update the internal properties |
| // using that configuration. You can do things such as include a new endpoint_root, |
| // a new error function, a new dataverse, etc. You can even store extra info. |
| // |
| // NOTE Long-term, this should have more strict limits. |
| var configuration = configuration || {}; |
| |
| for (var key in configuration) { |
| this._properties[key] = configuration[key]; |
| } |
| |
| return this; |
| } |
| |
| |
| /** |
| * dataverse |
| * |
| * Sets dataverse for execution for the AsterixDBConnection. |
| */ |
| AsterixDBConnection.prototype.dataverse = function(dataverseName) { |
| this._properties["dataverse"] = dataverseName; |
| |
| return this; |
| }; |
| |
| |
| /** |
| * query (http://asterix.ics.uci.edu/documentation/api.html#QueryApi) |
| * |
| * @param statements, statements of an AQL query |
| * @param successFn, a function to execute if this query is run successfully |
| * @param mode, a string either "synchronous" or "asynchronous", depending on preferred |
| * execution mode. |
| */ |
| AsterixDBConnection.prototype.query = function(statements, successFn, mode) { |
| |
| if ( typeof statements === 'string') { |
| statements = [ statements ]; |
| } |
| |
| var m = typeof mode ? mode : "synchronous"; |
| |
| // DEBUG |
| //alert(statements.join("\n")); |
| |
| var query = "use dataverse " + this._properties["dataverse"] + ";\n" + statements.join("\n"); |
| |
| this._api( |
| { |
| "query" : query, |
| "mode" : m |
| }, |
| successFn, |
| "query" |
| ); |
| |
| return this; |
| }; |
| |
| /** |
| * query_status (http://asterix.ics.uci.edu/documentation/api.html#QueryStatusApi) |
| * |
| * @param handle, a json object of the form {"handle" : handleObject}, where |
| * the handle object is an opaque handle previously returned |
| * from an asynchronous call. |
| * @param successFn, a function to call on successful execution of this API call. |
| */ |
| AsterixDBConnection.prototype.query_status = function(handle, successFn) { |
| this._api( |
| handle, |
| successFn, |
| "query/status" |
| ); |
| |
| return this; |
| }; |
| |
| |
| /** |
| * query_result (http://asterix.ics.uci.edu/documentation/api.html#AsynchronousResultApi) |
| * |
| * handle, a json object of the form {"handle" : handleObject}, where |
| * the handle object is an opaque handle previously returned |
| * from an asynchronous call. |
| * successFn, a function to call on successful execution of this API call. |
| */ |
| AsterixDBConnection.prototype.query_result = function(handle, successFn) { |
| this._api( |
| handle, |
| successFn, |
| "query/result" |
| ); |
| |
| return this; |
| }; |
| |
| |
| /** |
| * ddl (http://asterix.ics.uci.edu/documentation/api.html#DdlApi) |
| * |
| * @param statements, statements to run through ddl api |
| * @param successFn, a function to execute if they are successful |
| */ |
| AsterixDBConnection.prototype.ddl = function(statements, successFn) { |
| if ( typeof statements === 'string') { |
| statements = [ statements ]; |
| } |
| |
| this._api( |
| { |
| "ddl" : "use dataverse " + this._properties["dataverse"] + ";\n" + statements.join("\n") |
| }, |
| successFn, |
| "ddl" |
| ); |
| } |
| |
| |
| /** |
| * update (http://asterix.ics.uci.edu/documentation/api.html#UpdateApi) |
| * |
| * @param statements, statement(s) for an update API call |
| * @param successFn, a function to run if this is executed successfully. |
| * |
| * This is an AsterixDBConnection handler for the update API. It passes statements provided |
| * to the internal API endpoint handler. |
| */ |
| AsterixDBConnection.prototype.update = function(statements, successFn) { |
| if ( typeof statements === 'string') { |
| statements = [ statements ]; |
| } |
| |
| // DEBUG |
| // alert(statements.join("\n")); |
| |
| this._api( |
| { |
| "statements" : "use dataverse " + this._properties["dataverse"] + ";\n" + statements.join("\n") |
| }, |
| successFn, |
| "update" |
| ); |
| } |
| |
| |
| /** |
| * meta |
| * @param statements, a string or a list of strings representing an Asterix query object |
| * @param successFn, a function to execute if call succeeds |
| * |
| * Queries without a dataverse. This is a work-around for an Asterix REST API behavior |
| * that sometiems throws an error. This is handy for Asterix Metadata queries. |
| */ |
| AsterixDBConnection.prototype.meta = function(statements, successFn) { |
| |
| if ( typeof statements === 'string') { |
| statements = [ statements ]; |
| } |
| |
| var query = statements.join("\n"); |
| |
| this._api( |
| { |
| "query" : query, |
| "mode" : "synchronous" |
| }, |
| successFn, |
| "query" |
| ); |
| |
| return this; |
| } |
| |
| |
| /** |
| * _api |
| * |
| * @param json, the data to be passed with the request |
| * @param onSuccess, the success function to be run if this succeeds |
| * @param endpoint, a string representing one of the Asterix API endpoints |
| * |
| * Documentation of endpoints is here: |
| * http://asterix.ics.uci.edu/documentation/api.html |
| * |
| * This is treated as an internal method for making the actual call to the API. |
| */ |
| AsterixDBConnection.prototype._api = function(json, onSuccess, endpoint) { |
| |
| // The success function is called if the response is successful and returns data, |
| // or is just OK. |
| var success_fn = onSuccess; |
| |
| // This is the error function. Called if something breaks either on the Asterix side |
| // or in the Ajax call. |
| var error_fn = this._properties["error"]; |
| var ajax_error_fn = this._properties["ajax_error"]; |
| |
| // This is the target endpoint from the REST api, called as a string. |
| var endpoint_url = this._properties["endpoint_root"] + endpoint; |
| |
| // This SDK does not rely on jQuery, but utilizes its Ajax capabilities when present. |
| if (window.jQuery) { |
| $.ajax({ |
| |
| // The Asterix API does not accept post requests. |
| type : 'GET', |
| |
| // This is the endpoint url provided by combining the default |
| // or reconfigured endpoint root along with the appropriate api endpoint |
| // such as "query" or "update". |
| url : endpoint_url, |
| |
| // This is the data in the format specified on the API documentation. |
| data : json, |
| |
| // We send out the json datatype to make sure our data is parsed correctly. |
| dataType : "json", |
| |
| // The success option calls a function on success, which in this case means |
| // something was returned from the API. However, this does not mean the call succeeded |
| // on the REST API side, it just means we got something back. This also contains the |
| // error return codes, which need to be handled before we call th success function. |
| success : function(data) { |
| |
| // Check Asterix Response for errors |
| // See http://asterix.ics.uci.edu/documentation/api.html#ErrorCodes |
| if (data["error-code"]) { |
| error_fn(data); |
| |
| // Otherwise, run our provided success function |
| } else { |
| success_fn(data); |
| } |
| }, |
| |
| // This is the function that gets called if there is an ajax-related (non-Asterix) |
| // error. Network errors, empty response bodies, syntax errors, and a number of others |
| // can pop up. |
| error : function(data) { |
| |
| // Some of the Asterix API endpoints return empty responses on success. |
| // However, the ajax function treats these as errors while reporting a |
| // 200 OK code with no payload. So we will check for that, otherwise |
| // alert of an error. An example response is as follows: |
| // {"readyState":4,"responseText":"","status":200,"statusText":"OK"} |
| if (data["status"] == 200 && data["responseText"] == "") { |
| success_fn(data); |
| } else { |
| alert("[Ajax Error]\n" + JSON.stringify(data)); |
| } |
| } |
| }); |
| |
| } else { |
| |
| // NOTE: This section is in progress; currently API requires jQuery. |
| |
| // First, we encode the parameters of the query to create a new url. |
| api_endpoint = endpoint_url + "?" + Object.keys(json).map(function(k) { |
| return encodeURIComponent(k) + '=' + encodeURIComponent(json[k]) |
| }).join('&'); |
| |
| // Now, create an XMLHttp object to carry our request. We will call the |
| // UI callback function on ready. |
| var xmlhttp; |
| xmlhttp = new XMLHttpRequest(); |
| xmlhttp.open("GET", endpoint_url, true); |
| xmlhttp.send(null); |
| |
| xmlhttp.onreadystatechange = function(){ |
| if (xmlhttp.readyState == 4) { |
| if (xmlhttp.status === 200) { |
| alert(xmlhttp.responseText); |
| //success.call(null, xmlHttp.responseText); |
| } else { |
| //error.call(null, xmlHttp.responseText); |
| } |
| } else { |
| // Still processing |
| } |
| }; |
| } |
| return this; |
| }; |
| |
| // Asterix Expressions - Base |
| function AExpression () { |
| |
| this._properties = {}; |
| this._success = function() {}; |
| |
| if (arguments.length == 1) { |
| this._properties["value"] = arguments[0]; |
| } |
| |
| return this; |
| } |
| |
| |
| AExpression.prototype.bind = function(options) { |
| var options = options || {}; |
| |
| if (options.hasOwnProperty("success")) { |
| this._success = options["success"]; |
| } |
| |
| if (options.hasOwnProperty("return")) { |
| this._properties["return"] = " return " + options["return"].val(); |
| } |
| }; |
| |
| |
| AExpression.prototype.run = function(successFn) { |
| return this; |
| }; |
| |
| |
| AExpression.prototype.val = function() { |
| |
| var value = ""; |
| |
| // If there is a dataverse defined, provide it. |
| if (this._properties.hasOwnProperty("dataverse")) { |
| value += "use dataverse " + this._properties["dataverse"] + ";\n"; |
| }; |
| |
| if (this._properties.hasOwnProperty("value")) { |
| value += this._properties["value"].toString(); |
| } |
| |
| return value; |
| }; |
| |
| |
| // @param expressionValue [String] |
| AExpression.prototype.set = function(expressionValue) { |
| this._properties["value"] = expressionValue; |
| return this; |
| }; |
| |
| |
| // AQL Statements |
| // SingleStatement ::= DataverseDeclaration |
| // | FunctionDeclaration |
| // | CreateStatement |
| // | DropStatement |
| // | LoadStatement |
| // | SetStatement |
| // | InsertStatement |
| // | DeleteStatement |
| // | Query |
| function InsertStatement(quantifiedName, query) { |
| AExpression.call(this); |
| |
| var innerQuery = ""; |
| if (query instanceof AExpression) { |
| innerQuery = query.val(); |
| } else if (typeof query == "object" && Object.getPrototypeOf( query ) === Object.prototype ) { |
| |
| var insertStatements = []; |
| for (querykey in query) { |
| if (query[querykey] instanceof AExpression) { |
| insertStatements.push('"' + querykey + '" : ' + query[querykey].val()); |
| } else if (typeof query[querykey] == "string") { |
| insertStatements.push('"' + querykey + '" : ' + query[querykey]); |
| } else { |
| insertStatements.push('"' + querykey + '" : ' + query[querykey].toString()); |
| } |
| } |
| |
| innerQuery = "{" + insertStatements.join(', ') + "}"; |
| } |
| |
| var statement = "insert into dataset " + quantifiedName + "(" + innerQuery + ");"; |
| |
| AExpression.prototype.set.call(this, statement); |
| |
| return this; |
| } |
| |
| InsertStatement.prototype = Object.create(AExpression.prototype); |
| InsertStatement.prototype.constructor = InsertStatement; |
| |
| |
| // Delete Statement |
| // DeleteStatement ::= "delete" Variable "from" "dataset" QualifiedName ( "where" Expression )? |
| function DeleteStatement (variable, quantifiedName, whereExpression) { |
| AExpression.call(this); |
| |
| var statement = "delete " + variable + " from dataset " + quantifiedName; |
| |
| if (whereExpression instanceof AExpression) { |
| statement += " where " + whereExpression.val(); |
| } |
| |
| AExpression.prototype.set.call(this, statement); |
| |
| return this; |
| } |
| |
| DeleteStatement.prototype = Object.create(AExpression.prototype); |
| DeleteStatement.prototype.constructor = DeleteStatement; |
| |
| // SetStatement |
| // |
| // Grammar |
| // "set" Identifier StringLiteral |
| function SetStatement (identifier, stringLiteral) { |
| AExpression.call(this); |
| |
| var statement = "set " + identifier + ' "' + stringLiteral + '";'; |
| |
| AExpression.prototype.set.call(this, statement); |
| |
| return this; |
| } |
| |
| SetStatement.prototype = Object.create(AExpression.prototype); |
| SetStatement.prototype.constructor = SetStatement; |
| |
| |
| // Other Expressions |
| |
| // FunctionExpression |
| // Parent: AsterixExpression |
| // |
| // @param options [Various], |
| // @key function [String], a function to be applid to the expression |
| // @key expression [AsterixExpression or AQLClause] an AsterixExpression/Clause to which the fn will be applied |
| function FunctionExpression() { |
| |
| // Initialize superclass |
| AExpression.call(this); |
| |
| this._properties["function"] = ""; |
| this._properties["expressions"] = []; |
| |
| // Check for fn/expression input |
| if (arguments.length >= 2 && typeof arguments[0] == "string") { |
| |
| this._properties["function"] = arguments[0]; |
| |
| for (i = 1; i < arguments.length; i++) { |
| if (arguments[i] instanceof AExpression || arguments[i] instanceof AQLClause) { |
| this._properties["expressions"].push(arguments[i]); |
| } else { |
| this._properties["expressions"].push(new AExpression(arguments[i])); |
| } |
| } |
| } |
| |
| // Return FunctionCallExpression object |
| return this; |
| } |
| |
| |
| FunctionExpression.prototype = Object.create(AExpression.prototype); |
| FunctionExpression.prototype.constructor = FunctionExpression; |
| |
| |
| FunctionExpression.prototype.val = function () { |
| var fn_args = []; |
| for (var i = 0; i < this._properties["expressions"].length; i++) { |
| fn_args.push(this._properties["expressions"][i].val()); |
| } |
| |
| return this._properties["function"] + "(" + fn_args.join(", ") + ")"; |
| }; |
| |
| |
| // FLWOGRExpression |
| // |
| // FLWOGRExpression ::= ( ForClause | LetClause ) ( Clause )* "return" Expression |
| function FLWOGRExpression (options) { |
| // Initialize superclass |
| AExpression.call(this); |
| |
| this._properties["clauses"] = []; |
| this._properties["minSize"] = 0; |
| |
| // Bind options and return |
| this.bind(options); |
| return this; |
| } |
| |
| |
| FLWOGRExpression.prototype = Object.create(AExpression.prototype); |
| FLWOGRExpression.prototype.constructor = FLWOGRExpression; |
| |
| |
| FLWOGRExpression.prototype.bind = function(options) { |
| AExpression.prototype.bind.call(this, options); |
| |
| var options = options || {}; |
| |
| if (options instanceof SetStatement) { |
| this._properties["clauses"].push(options); |
| this._properties["minSize"] += 1; |
| } |
| |
| if (this._properties["clauses"].length <= this._properties["minSize"]) { |
| // Needs to start with for or let clause |
| if (options instanceof ForClause || options instanceof LetClause) { |
| this._properties["clauses"].push(options); |
| } |
| } else { |
| if (options instanceof AQLClause) { |
| this._properties["clauses"].push(options); |
| } |
| } |
| |
| return this; |
| }; |
| |
| |
| FLWOGRExpression.prototype.val = function() { |
| var value = AExpression.prototype.val.call(this); |
| |
| var clauseValues = []; |
| for (var c in this._properties["clauses"]) { |
| clauseValues.push(this._properties["clauses"][c].val()); |
| } |
| |
| return value + clauseValues.join("\n");// + ";"; |
| }; |
| |
| // Pretty Expression Shorthand |
| |
| FLWOGRExpression.prototype.ReturnClause = function(expression) { |
| return this.bind(new ReturnClause(expression)); |
| }; |
| |
| FLWOGRExpression.prototype.ForClause = function() { |
| return this.bind(new ForClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| FLWOGRExpression.prototype.LetClause = function() { |
| return this.bind(new LetClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| FLWOGRExpression.prototype.WhereClause = function() { |
| return this.bind(new WhereClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| FLWOGRExpression.prototype.and = function() { |
| var args = Array.prototype.slice.call(arguments); |
| args.push(true); |
| return this.bind(new WhereClause().and(args)); |
| }; |
| |
| FLWOGRExpression.prototype.or = function() { |
| var args = Array.prototype.slice.call(arguments); |
| args.push(true); |
| return this.bind(new WhereClause().or(args)); |
| }; |
| |
| FLWOGRExpression.prototype.OrderbyClause = function() { |
| return this.bind(new OrderbyClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| |
| FLWOGRExpression.prototype.GroupClause = function() { |
| return this.bind(new GroupClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| FLWOGRExpression.prototype.LimitClause = function() { |
| return this.bind(new LimitClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| FLWOGRExpression.prototype.DistinctClause = function() { |
| return this.bind(new DistinctClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| FLWOGRExpression.prototype.AQLClause = function() { |
| return this.bind(new AQLClause(Array.prototype.slice.call(arguments))); |
| }; |
| |
| |
| // AQLClause |
| // |
| // Base Clause ::= ForClause | LetClause | WhereClause | OrderbyClause | GroupClause | LimitClause | DistinctClause |
| function AQLClause() { |
| this._properties = {}; |
| this._properties["clause"] = ""; |
| this._properties["stack"] = []; |
| if (typeof arguments[0] == 'string') { |
| this._properties["clause"] = arguments[0]; |
| } |
| return this; |
| } |
| |
| AQLClause.prototype.val = function() { |
| var value = this._properties["clause"]; |
| |
| return value; |
| }; |
| |
| AQLClause.prototype.bind = function(options) { |
| |
| if (options instanceof AQLClause) { |
| this._properties["clause"] += " " + options.val(); |
| } |
| |
| return this; |
| }; |
| |
| AQLClause.prototype.set = function(value) { |
| this._properties["clause"] = value; |
| return this; |
| }; |
| |
| |
| // ForClause |
| // |
| // Grammar: |
| // "for" Variable ( "at" Variable )? "in" ( Expression ) |
| // |
| // @param for_variable [String], REQUIRED, first variable in clause |
| // @param at_variable [String], NOT REQUIRED, first variable in clause |
| // @param expression [AsterixExpression], REQUIRED, expression to evaluate |
| function ForClause(for_variable, at_variable, expression) { |
| AQLClause.call(this); |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| this._properties["clause"] = "for " + parameters[0]; |
| |
| if (parameters.length == 3) { |
| this._properties["clause"] += " at " + parameters[1]; |
| this._properties["clause"] += " in " + parameters[2].val(); |
| } else if (parameters.length == 2) { |
| this._properties["clause"] += " in " + parameters[1].val(); |
| } |
| |
| return this; |
| } |
| |
| ForClause.prototype = Object.create(AQLClause.prototype); |
| ForClause.prototype.constructor = ForClause; |
| |
| |
| // LetClause |
| // |
| // Grammar: |
| // LetClause ::= "let" Variable ":=" Expression |
| // |
| // @param let_variable [String] |
| // @param expression [AExpression] |
| function LetClause(let_variable, expression) { |
| AQLClause.call(this); |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| this._properties["clause"] = "let " + parameters[0] + " := "; |
| this._properties["clause"] += parameters[1].val(); |
| |
| return this; |
| } |
| |
| LetClause.prototype = Object.create(AQLClause.prototype); |
| LetClause.prototype.constructor = LetClause; |
| |
| |
| // ReturnClause |
| // |
| // Grammar: |
| // return [AQLExpression] |
| function ReturnClause(expression) { |
| AQLClause.call(this); |
| |
| this._properties["clause"] = "return "; |
| |
| if (expression instanceof AExpression || expression instanceof AQLClause) { |
| this._properties["clause"] += expression.val(); |
| |
| } else if ( typeof expression == "object" && Object.getPrototypeOf( expression ) === Object.prototype ) { |
| |
| this._properties["clause"] += "\n{\n"; |
| var returnStatements = []; |
| for (returnValue in expression) { |
| |
| if (expression[returnValue] instanceof AExpression) { |
| returnStatements.push('"' + returnValue + '" ' + " : " + expression[returnValue].val()); |
| } else if (typeof expression[returnValue] == "string") { |
| returnStatements.push('"' + returnValue + '" ' + " : " + expression[returnValue]); |
| } |
| } |
| this._properties["clause"] += returnStatements.join(",\n"); |
| this._properties["clause"] += "\n}"; |
| |
| } else { |
| this._properties["clause"] += new AQLClause().set(expression).val(); |
| } |
| |
| return this; |
| } |
| |
| |
| ReturnClause.prototype = Object.create(AQLClause.prototype); |
| ReturnClause.prototype.constructor = ReturnClause; |
| |
| |
| // WhereClause |
| // |
| // Grammar: |
| // ::= "where" Expression |
| // |
| // @param expression [BooleanExpression], pushes this expression onto the stack |
| function WhereClause(expression) { |
| AQLClause.call(this); |
| |
| this._properties["stack"] = []; |
| |
| if (expression instanceof Array) { |
| this.bind(expression[0]); |
| } else { |
| this.bind(expression); |
| } |
| |
| return this; |
| } |
| |
| |
| WhereClause.prototype = Object.create(AQLClause.prototype); |
| WhereClause.prototype.constructor = WhereClause; |
| |
| |
| WhereClause.prototype.bind = function(expression) { |
| if (expression instanceof AExpression) { |
| this._properties["stack"].push(expression); |
| } |
| return this; |
| }; |
| |
| |
| WhereClause.prototype.val = function() { |
| var value = ""; |
| |
| if (this._properties["stack"].length == 0) { |
| return value; |
| } |
| |
| var count = this._properties["stack"].length - 1; |
| while (count >= 0) { |
| value += this._properties["stack"][count].val() + " "; |
| count -= 1; |
| } |
| |
| return "where " + value; |
| }; |
| |
| |
| WhereClause.prototype.and = function() { |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| var andClauses = []; |
| for (var expression in parameters) { |
| |
| if (parameters[expression] instanceof AExpression) { |
| andClauses.push(parameters[expression].val()); |
| } |
| } |
| |
| if (andClauses.length > 0) { |
| this._properties["stack"].push(new AExpression().set(andClauses.join(" and "))); |
| } |
| |
| return this; |
| }; |
| |
| |
| WhereClause.prototype.or = function() { |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| var orClauses = []; |
| for (var expression in parameters) { |
| |
| if (parameters[expression] instanceof AExpression) { |
| orClauses.push(parameters[expression].val()); |
| } |
| } |
| |
| if (andClauses.length > 0) { |
| this._properties["stack"].push(new AExpression().set(orClauses.join(" and "))); |
| } |
| |
| return this; |
| }; |
| |
| // LimitClause |
| // Grammar: |
| // LimitClause ::= "limit" Expression ( "offset" Expression )? |
| // |
| // @param limitExpression [REQUIRED, AQLExpression] |
| // @param offsetExpression [OPTIONAL, AQLExpression] |
| function LimitClause(limitExpression, offsetExpression) { |
| |
| AQLClause.call(this); |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| // limitExpression required |
| this._properties["clause"] = "limit " + parameters[0].val(); |
| |
| // Optional: Offset |
| if (parameters.length == 2) { |
| this._properties["clause"] += " offset " + parameters[1].val(); |
| } |
| |
| return this; |
| } |
| |
| LimitClause.prototype = Object.create(AQLClause.prototype); |
| LimitClause.prototype.constructor = LimitClause; |
| |
| |
| // OrderbyClause |
| // |
| // Grammar: |
| // OrderbyClause ::= "order" "by" Expression ( ( "asc" ) | ( "desc" ) )? ( "," Expression ( ( "asc" ) | ( "desc" ) )? )* |
| // |
| // @params AQLExpressions and asc/desc strings, in any quantity. At least one required. |
| function OrderbyClause() { |
| |
| AQLClause.call(this); |
| |
| // At least one argument expression is required, and first should be expression |
| if (arguments.length == 0) { |
| this._properties["clause"] = null; |
| return this; |
| } |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| var expc = 0; |
| var expressions = []; |
| |
| while (expc < parameters.length) { |
| |
| var expression = ""; |
| |
| if (parameters[expc] instanceof AExpression) { |
| expression += parameters[expc].val(); |
| } |
| |
| var next = expc + 1; |
| if (next < parameters.length && (parameters[next] == "asc" || parameters[next] == "desc")) { |
| expc++; |
| expression += " " + parameters[expc]; |
| } |
| |
| expressions.push(expression); |
| |
| expc++; |
| } |
| |
| this._properties["clause"] = "order by " + expressions.join(", "); |
| return this; |
| } |
| |
| OrderbyClause.prototype = Object.create(AQLClause.prototype); |
| OrderbyClause.prototype.constructor = OrderbyClause; |
| |
| |
| // GroupClause |
| // |
| // Grammar: |
| // GroupClause ::= "group" "by" ( Variable ":=" )? Expression ( "," ( Variable ":=" )? Expression )* ( "decor" Variable ":=" Expression ( "," "decor" Variable ":=" Expression )* )? "with" VariableRef ( "," VariableRef )* |
| function GroupClause() { |
| AQLClause.call(this); |
| |
| if (arguments.length == 0) { |
| this._properties["clause"] = null; |
| return this; |
| } |
| |
| var parameters = []; |
| if (arguments[0] instanceof Array) { |
| parameters = arguments[0]; |
| } else { |
| parameters = arguments; |
| } |
| |
| var expc = 0; |
| var expressions = []; |
| var variableRefs = []; |
| var isDecor = false; |
| |
| while (expc < parameters.length) { |
| |
| if (parameters[expc] instanceof AExpression) { |
| |
| isDecor = false; |
| expressions.push(parameters[expc].val()); |
| |
| } else if (typeof parameters[expc] == "string") { |
| |
| // Special keywords, decor & with |
| if (parameters[expc] == "decor") { |
| isDecor = true; |
| } else if (parameters[expc] == "with") { |
| isDecor = false; |
| expc++; |
| while (expc < parameters.length) { |
| variableRefs.push(parameters[expc]); |
| expc++; |
| } |
| |
| // Variables and variable refs |
| } else { |
| |
| var nextc = expc + 1; |
| var expression = ""; |
| |
| if (isDecor) { |
| expression += "decor "; |
| isDecor = false; |
| } |
| |
| expression += parameters[expc] + " := " + parameters[nextc].val(); |
| expressions.push(expression); |
| expc++; |
| } |
| } |
| |
| expc++; |
| } |
| |
| this._properties["clause"] = "group by " + expressions.join(", ") + " with " + variableRefs.join(", "); |
| return this; |
| } |
| |
| GroupClause.prototype = Object.create(AQLClause.prototype); |
| GroupClause.prototype.constructor = GroupClause; |
| |
| |
| // Quantified Expression |
| // |
| // Grammar |
| // QuantifiedExpression ::= ( ( "some" ) | ( "every" ) ) Variable "in" Expression ( "," Variable "in" Expression )* "satisfies" Expression |
| // |
| // @param String some/every |
| // @param [AExpression] |
| // @param [Aexpression] satisfiesExpression |
| function QuantifiedExpression (keyword, expressions, satisfiesExpression) { |
| AExpression.call(this); |
| |
| var expression = keyword + " "; |
| var varsInExpressions = []; |
| |
| for (var varInExpression in expressions) { |
| varsInExpressions.push(varInExpression + " in " + expressions[varInExpression].val()); |
| } |
| expression += varsInExpressions.join(", ") + " satisfies " + satisfiesExpression.val(); |
| |
| AExpression.prototype.set.call(this, expression); |
| |
| return this; |
| } |
| |
| QuantifiedExpression.prototype = Object.create(AExpression.prototype); |
| QuantifiedExpression.prototype.constructor = QuantifiedExpression; |
| |
| QuantifiedExpression.prototype.val = function() { |
| var value = AExpression.prototype.val.call(this); |
| return "(" + value + ")"; |
| }; |