spahql
Version:
A query language and data model for deep Javascript object structures.
116 lines (100 loc) • 4.14 kB
JavaScript
/**
* class SpahQL.Token.SelectionQuery < SpahQL.Token.Base
*
* A token describing any selection query, comprised of an optional root flag followed by one or more
* path components.
**/
SpahQL_classExtend("SpahQL.Token.SelectionQuery", SpahQL.Token.Base, {
// Singleton
// --------------------------
// Atom configuration: paths
ATOM_PATH_ROOT: "$",
/**
* SpahQL.Token.SelectionQuery.parseAt(index, queryString) -> Array result or null
*
* Reads the given queryString starting at the given index and attempts to identify and parse
* a selection query token. If found, the token will be returned in a tuple \[resumeIndex, foundToken\].
* Returns null if nothing is found.
**/
"parseAt": function(i, query) {
var ch = query.charAt(i);
var firstComponent = SpahQL.Token.PathComponent.parseAt(i, query);
if(ch == this.ATOM_PATH_ROOT || firstComponent) {
// Query as a whole may have: root flag, set of path fragments
var pq = new this();
var j = i;
if(ch == this.ATOM_PATH_ROOT) {
firstComponent = SpahQL.Token.PathComponent.parseAt(j+1, query);
if(!firstComponent) {
this.throwParseErrorAt(j+1, query, "Found unexpected character '"+query.charAt(j+1)+"', expected TOKEN_PATH_COMPONENT");
}
// Valid root path, wind ahead and start chunking queries
pq.useRoot = true;
}
j = firstComponent[0];
pq.pathComponents.push(firstComponent[1]);
// Start chunking into path segments
var pathComponent;
while(pathComponent = SpahQL.Token.PathComponent.parseAt(j, query)) {
pq.pathComponents.push(pathComponent[1]);
j = pathComponent[0];
}
return [j, pq];
}
return null;
}
}, {
// Instance
// --------------------
/**
* SpahQL.Token.SelectionQuery#pathComponents -> Array Token.PathComponent
*
* Contains all path components that comprise this selection query, in the order in which they
* were encountered during parsing.
**/
/**
* SpahQL.Token.SelectionQuery#useRoot -> Boolean
*
* A flag indicating whether or not this query is locked to the root data context.
**/
/**
* new SpahQL.Token.SelectionQuery(pathComponents, useRoot)
*
* Instantiate a new selection query token with blank-slate values.
**/
"init": function(pathComponents, useRoot) {
this.pathComponents = pathComponents || [];
this.useRoot = useRoot || false;
},
/**
* SpahQL.Token.SelectionQuery#evaluate(rootData, scopeData, scopePath) -> Array
* - rootData (Object): The entire root-level data structure being queried
* - scopeData (Object): The data for the scope at which this query is being executed.
* - scopePath (String): The string path for the root of the scopeData argument.
*
* Evaluates all the path components in the given query in turn.
**/
"evaluate": function(rootData, scopeData, scopePath) {
// Start off with a simulated result using the data required by the query
var results = [
SpahQL.result(
((this.useRoot)? null : scopePath) || "/",
((this.useRoot)? rootData : scopeData)
)
];
// Loop path components and pass reduced data
for(var i=0; i< this.pathComponents.length; i++) {
var pc = this.pathComponents[i];
var pcResults = []; // The results, flattened, for this path component
for(var j=0; j < results.length; j++) {
// Run each result from the previous iteration through the path component evaluator.
// Resultset for initial run is defined at top of this method.
pcResults = pcResults.concat(pc.evaluate(rootData, results[j].value, results[j].path));
}
results = pcResults;
// only continue if there are results to work with
if(results.length == 0) break;
}
return results;
}
});