Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Query.normalize() -- a helper to disambiguate and post-parse query

  • Loading branch information...
commit 1a1b5d9defbcc7538f5170fbcd19a52735b4c009 1 parent 9fd7aa4
@dvv dvv authored
Showing with 147 additions and 52 deletions.
  1. +22 −0 lib/parser.js
  2. +125 −52 lib/query.js
View
22 lib/parser.js
@@ -153,6 +153,28 @@ exports.parseGently = function(){
return terms;
}
+/*
+ * reduces query to simple SQL-ish one
+ *
+ * that is only cond1(args1)&...condN(argsN)&sort(...)&limit(...)&select(...)[&values()] are supported and no hierarchy is supported
+ */
+exports.parseSimple = function(){
+ var terms;
+ try {
+ // vanilla parse
+ terms = parse.apply(this, arguments);
+ // delete any doubled lastSeens
+
+ terms = {
+ sele: 1
+ };
+ } catch(err) {
+ terms = new exports.Query();
+ terms.error = err.message;
+ }
+ return terms;
+}
+
exports.commonOperatorMap = {
"and" : "&",
"or" : "|",
View
177 lib/query.js
@@ -3,10 +3,10 @@
* var Query = require("./query").Query;
* query = Query();
* query.executor = function(query){
- * require("./js-array").query(query, params, data); // if we want to operate on an array
+ * require("./js-array").query(query, params, data); // if we want to operate on an array
* };
* query.eq("a", 3).le("b", 4).forEach(function(object){
- * // for each object that matches the query
+ * // for each object that matches the query
* });
*/
({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory(exports, require("./parser"));}}).
@@ -44,62 +44,62 @@ Query.prototype.toString = function(){
};
function queryToString(part) {
- if (part instanceof Array) {
- return '('+part.map(function(arg) {
- return queryToString(arg);
- }).join(",")+')';
- }
- if (part && part.name && part.args) {
- return [
- part.name,
- "(",
- part.args.map(function(arg) {
- return queryToString(arg);
- }).join(","),
- ")"
- ].join("");
- }
- return exports.encodeValue(part);
+ if (part instanceof Array) {
+ return '('+part.map(function(arg) {
+ return queryToString(arg);
+ }).join(",")+')';
+ }
+ if (part && part.name && part.args) {
+ return [
+ part.name,
+ "(",
+ part.args.map(function(arg) {
+ return queryToString(arg);
+ }).join(","),
+ ")"
+ ].join("");
+ }
+ return exports.encodeValue(part);
};
function encodeString(s) {
- if (typeof s === "string") {
- s = encodeURIComponent(s);
- if (s.match(/[\(\)]/)) {
- s = s.replace("(","%28").replace(")","%29");
- };
- }
- return s;
+ if (typeof s === "string") {
+ s = encodeURIComponent(s);
+ if (s.match(/[\(\)]/)) {
+ s = s.replace("(","%28").replace(")","%29");
+ };
+ }
+ return s;
}
exports.encodeValue = function(val) {
- var encoded;
- if (val === null) val = 'null';
- if (val !== parser.converters["default"]('' + (
- val.toISOString && val.toISOString() || val.toString()
- ))) {
- var type = typeof val;
- if(val instanceof RegExp){
- // TODO: control whether to we want simpler glob() style
- val = val.toString();
- var i = val.lastIndexOf('/');
- type = val.substring(i).indexOf('i') >= 0 ? "re" : "RE";
- val = encodeString(val.substring(1, i));
- encoded = true;
- }
- if(type === "object"){
- type = "epoch";
- val = val.getTime();
- encoded = true;
- }
- if(type === "string") {
- val = encodeString(val);
- encoded = true;
- }
- val = [type, val].join(":");
- }
- if (!encoded && typeof val === "string") val = encodeString(val);
- return val;
+ var encoded;
+ if (val === null) val = 'null';
+ if (val !== parser.converters["default"]('' + (
+ val.toISOString && val.toISOString() || val.toString()
+ ))) {
+ var type = typeof val;
+ if(val instanceof RegExp){
+ // TODO: control whether to we want simpler glob() style
+ val = val.toString();
+ var i = val.lastIndexOf('/');
+ type = val.substring(i).indexOf('i') >= 0 ? "re" : "RE";
+ val = encodeString(val.substring(1, i));
+ encoded = true;
+ }
+ if(type === "object"){
+ type = "epoch";
+ val = val.getTime();
+ encoded = true;
+ }
+ if(type === "string") {
+ val = encodeString(val);
+ encoded = true;
+ }
+ val = [type, val].join(":");
+ }
+ if (!encoded && typeof val === "string") val = encodeString(val);
+ return val;
};
exports.updateQueryMethods = function(){
@@ -136,5 +136,78 @@ exports.updateQueryMethods = function(){
exports.updateQueryMethods();
+/* recursively iterate over query terms calling 'fn' for each term */
+Query.prototype.walk = function(fn, options){
+ options = options || {};
+ function walk(name, terms){
+ (terms || []).forEach(function(term, i, arr) {
+ var args, func, key, x;
+ term != null ? term : term = {};
+ func = term.name;
+ args = term.args;
+ if (!func || !args) {
+ return;
+ }
+ if (args[0] instanceof Query) {
+ walk.call(this, func, args);
+ } else {
+ fn.call(this, func, args);
+ }
+ });
+ }
+ walk.call(this, this.name, this.args);
+};
+
+/* disambiguate query */
+Query.prototype.normalize = function(options){
+ options = options || {};
+ options.primaryKey = options.primaryKey || 'id';
+ var result = {
+ original: this,
+ sort: [],
+ limit: [Infinity, 0, Infinity],
+ skip: 0,
+ limit: Infinity,
+ select: [],
+ values: false
+ // TODO: cache conditions
+ // TODO: flag for non-void conditions
+ };
+ function normal(func, args){
+ if (func === 'sort' || func === 'select') {
+ result[func] = args;
+ result[func+'1'] = result[func].map(function(x){
+ var o = {};
+ var a = /([-+]*)(.+)/.exec(x);
+ o[a[2]] = a[1].charAt(0) === '-' ? -1 : 1;
+ return o;
+ });
+ result[func+'2'] = {};
+ result[func].forEach(function(x){
+ var a = /([-+]*)(.+)/.exec(x);
+ result[func+'2'][a[2]] = a[1].charAt(0) === '-' ? -1 : 1;
+ });
+ } else if (func === 'limit') {
+ var limit = args;
+ result.skip = +limit[1] || 0;
+ limit = +limit[0] || 0;
+ if (options.hardLimit && limit > options.hardLimit)
+ limit = options.hardLimit;
+ result.limit = limit;
+ result.needCount = true;
+ } else if (func === 'values') {
+ result.values = true;
+ } else if (func === 'eq') {
+ // cache primary key equality -- useful to distinguish between .get(id) and .query(query)
+ var t = typeof args[1];
+ if (args[0] === options.primaryKey && ['string','number'].indexOf(t) >= 0) {
+ result.id = String(args[1]);
+ }
+ }
+ }
+ this.walk(normal);
+ return result;
+};
+
return exports;
});
Please sign in to comment.
Something went wrong with that request. Please try again.