query.js
Version:
query.js is an abstract query language which can be mapped to different query language implementation.
299 lines (234 loc) • 8.21 kB
JavaScript
/*!
* query.js
*
* Copyright 2012 Tony Findeisen, Marcus Krejpowicz
* Licensed under the MIT license.
*
* Date: Sat Jan 05 2013 19:00:34 GMT+0100 (CET)
*/
(function (exports) {
"use strict";
var query = function () {
return new Query();
};
var Query = function () {
this.query = {};
};
var sortParser = /^([+-])?(.+)$/;
Query.prototype = {
limit: function (limit) {
this.query.limit = limit;
return this;
},
sort: function (fields) {
fields = Array.prototype.slice.call(arguments);
var sort = [];
function parseFields(fields) {
for (var i = 0; i < fields.length; i++) {
parseAndAddField(fields[i]);
}
}
function parseAndAddField(field) {
if (field instanceof Array) {
parseFields(field);
} else if (field instanceof Object) {
if (!field.hasOwnProperty("field")) {
throw new Error("Field in sort direction missing");
}
field.direction = field.direction || 1;
sort.push(field);
} else {
// string
var match = sortParser.exec(field);
if (!match) {
throw new Error("Field cannot be parsed");
}
sort.push({
direction: match[1] === "-" ? -1 : 1,
field: match[2]
});
}
}
parseFields(fields);
this.query.sort = sort;
return this;
},
offset: function (offset) {
this.query.offset = offset;
return this;
},
toObject: function () {
var ret = {};
if(this.query.offset){
ret.offset = this.query.offset;
}
if(this.query.where){
ret.where = this.query.where.toObject();
}
if(this.query.limit){
ret.limit = this.query.limit;
}
if(this.query.sort){
ret.sort = clone(this.query.sort);
}
return ret;
},
where: function (type) {
this.query.where = this.query.where || new Where(type);
return this.query.where;
},
sortCacheId: function () {
var ret = [];
if (this.sort) {
for (var i = 0; i < this.query.sort.length; i++) {
var sortItem = this.query.sort[i];
ret.push((sortItem.direction === -1 ? "-" : "+") + sortItem.field);
}
}
return ret.join(";");
},
whereCacheId: function () {
if(this.query.where){
return this.query.where.operator + ":" + generateExpressionsCache(this.query.where.expressions);
} else {
return "";
}
},
cacheId: function () {
return this.sortCacheId() + ":" + this.whereCacheId();
}
};
function generateExpressionsCache(expressions) {
expressions = expressions || [];
var expression,
cacheId = "",
value;
for (var i = 0; i < expressions.length; i++) {
expression = expressions[i];
if (expression instanceof Where) {
cacheId += expression.operator + ":" + generateExpressionsCache(expressions);
} else if (expression instanceof Comparator) {
value = expression.value;
if (!(value instanceof Array)) {
value = [value];
}
var val = "";
for (var k = 0; k < value.length; k++) {
val += value[k] + ":" + typeof(value[k]);
}
value = val;
cacheId += expression.operator + ":" + expression.field + ":" + value + ":";
}
}
return cacheId;
}
function clone(obj) {
if (obj instanceof Array) {
return obj.slice();
}
var ret = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var value = obj[key];
ret[key] = value instanceof Object ? clone(value) : value;
}
}
return ret;
}
var comparatorMethods = ["eql", "like", "lt", "lte", "gt", "gte", "in", "ne"],
nestedWhereMethods = ["not", "and", "or"];
var Where = function (type) {
this.operator = type || "and";
this.expressions = [];
};
var Comparator = function (operator, field, value, type) {
this.operator = operator;
this.field = field;
this.value = value;
if (type) {
this.type = type;
}
};
Where.prototype.push = function (expression) {
this.expressions.push(expression);
};
Where.prototype.toObject = function(){
var expressions = [], expression;
for(var i =0; i < this.expressions.length; i++){
expression = this.expressions[i];
if(expression instanceof Where){
expressions.push(expression.toObject());
} else {
expressions.push(clone(expression));
}
}
return {
operator: this.operator,
expressions: expressions
};
};
Comparator.prototype = {
getValue: function () {
return this.value;
}
};
for (var i = 0; i < comparatorMethods.length; i++) {
(function (operator) {
Where.prototype[operator] = function (field, value, type) {
if (field instanceof Object) {
// do it for all key values pairs
for (var key in field) {
if (field.hasOwnProperty(key)) {
this[operator](key, field[key]);
}
}
} else {
this.expressions.push(new Comparator(operator, field, value, type));
}
return this;
};
Query.prototype[operator] = function () {
var args = Array.prototype.slice.call(arguments);
var where = this.where();
where[operator].apply(where, args);
return this;
}
})(comparatorMethods[i]);
}
for (var j = 0; j < nestedWhereMethods.length; j++) {
(function (type) {
Where.prototype[type] = function () {
var args = Array.prototype.slice.call(arguments);
var operationWhere = new Where(type);
for (var i = 0; i < args.length; i++) {
var nestedFunction = args[i];
var where;
if (nestedFunction instanceof Where) {
where = nestedFunction;
} else {
where = new Where();
nestedFunction.call(where, where);
}
operationWhere.expressions.push(where);
}
this.expressions.push(operationWhere);
};
Query.prototype[type] = function () {
var args = Array.prototype.slice.call(arguments);
var where = this.where();
where[type].apply(where, args);
return this;
}
})(nestedWhereMethods[j]);
}
// global on the server, window in the browser
var previous_query = exports.query;
query.noConflict = function () {
exports.query = previous_query;
return query;
};
Query.Where = Where;
Query.Comparator = Comparator;
exports.query = query;
exports.Query = Query;
}(typeof(exports) === "undefined" ? this : exports));