sql-from-mongo
Version:
Simple conversion from MongoDB-like syntax to SQL WHERE clauses
263 lines (258 loc) • 10.3 kB
JavaScript
// Generated by CoffeeScript 1.9.3
(function() {
var sqlFromMongo;
sqlFromMongo = function(mongoQueryObject, collectionName, fields) {
var JOIN_LOOKUP, field, fieldStringParts, fieldsString, key, keys, parseSingleKeyValuePair, parts, prefix, sql, subObject, type, value;
if ((fields != null) && (collectionName == null)) {
throw new Error("Must provide a collectionName if fields is provided.");
}
JOIN_LOOKUP = {
$and: " AND ",
$or: " OR ",
$nor: " OR "
};
type = (function() {
var classToType, i, len, name, ref;
classToType = {};
ref = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
for (i = 0, len = ref.length; i < len; i++) {
name = ref[i];
classToType["[object " + name + "]"] = name.toLowerCase();
}
return function(obj) {
var strType;
strType = Object.prototype.toString.call(obj);
return classToType[strType] || "object";
};
})();
if (type(mongoQueryObject) === 'string' && mongoQueryObject.toUpperCase().indexOf('SELECT') === 0) {
return mongoQueryObject;
}
parseSingleKeyValuePair = function(key, value, collectionName) {
var i, joinOperator, key2, keys, len, maxDistance, minDistance, o, parts, s, value2, valueKey, valueValue;
switch (key) {
case "$not":
s = sqlFromMongo(value, collectionName);
if (s.indexOf("(") === 0) {
return "NOT " + s;
} else {
return "NOT (" + s + ")";
}
break;
case "$and":
case "$or":
case "$nor":
if (type(value) !== "array") {
throw new Error("Use of $and, $or, or $nor operator requires an array as its parameter.");
}
parts = [];
for (i = 0, len = value.length; i < len; i++) {
o = value[i];
parts.push(sqlFromMongo(o, collectionName));
}
joinOperator = JOIN_LOOKUP[key];
s = "(" + parts.join(joinOperator) + ")";
if (key === "$nor") {
return "NOT " + s;
} else {
return s;
}
break;
default:
if (type(value) === "object") {
parts = [];
s = (prefix + key) + " ";
for (valueKey in value) {
valueValue = value[valueKey];
switch (valueKey) {
case "$lt":
parts.push(s + ("< " + (JSON.stringify(valueValue))));
break;
case "$gt":
parts.push(s + ("> " + (JSON.stringify(valueValue))));
break;
case "$lte":
parts.push(s + ("<= " + (JSON.stringify(valueValue))));
break;
case "$gte":
parts.push(s + (">= " + (JSON.stringify(valueValue))));
break;
case "$ne":
parts.push(s + ("<> " + (JSON.stringify(valueValue))));
break;
case "$eq":
parts.push(s + ("= " + (JSON.stringify(valueValue))));
break;
case "$in":
if (type(valueValue) === 'array') {
if (valueValue.length > 100) {
throw new Error("In DocumentDB the maximum number of values per IN expression is 100");
}
s = JSON.stringify(valueValue);
s = s.substr(1, s.length - 2);
return (prefix + key) + " IN (" + s + ")";
} else {
return "ARRAY_CONTAINS(" + (prefix + valueValue) + ", " + key + ")";
}
break;
case "$nin":
if (type(valueValue) === 'array') {
if (valueValue.length > 100) {
throw new Error("In DocumentDB the maximum number of values per IN expression is 100");
}
s = JSON.stringify(valueValue);
s = s.substr(1, s.length - 2);
return "NOT " + (prefix + key) + " IN (" + s + ")";
} else {
return "NOT ARRAY_CONTAINS(" + (prefix + valueValue) + ", " + key + ")";
}
break;
case "$size":
return "ARRAY_LENGTH(" + (prefix + key) + ") = " + valueValue;
case "$exists":
if (valueValue) {
return "IS_DEFINED(" + (prefix + key) + ")";
} else {
return "NOT IS_DEFINED(" + (prefix + key) + ")";
}
break;
case "$isArray":
if (valueValue) {
return "IS_ARRAY(" + (prefix + key) + ")";
} else {
return "NOT IS_ARRAY(" + (prefix + key) + ")";
}
break;
case "$isBool":
if (valueValue) {
return "IS_BOOL(" + (prefix + key) + ")";
} else {
return "NOT IS_BOOL(" + (prefix + key) + ")";
}
break;
case "$isNull":
if (valueValue) {
return "IS_NULL(" + (prefix + key) + ")";
} else {
return "NOT IS_NULL(" + (prefix + key) + ")";
}
break;
case "$isNumber":
if (valueValue) {
return "IS_NUMBER(" + (prefix + key) + ")";
} else {
return "NOT IS_NUMBER(" + (prefix + key) + ")";
}
break;
case "$isObject":
if (valueValue) {
return "IS_OBJECT(" + (prefix + key) + ")";
} else {
return "NOT IS_OBJECT(" + (prefix + key) + ")";
}
break;
case "$isString":
if (valueValue) {
return "IS_STRING(" + (prefix + key) + ")";
} else {
return "NOT IS_STRING(" + (prefix + key) + ")";
}
break;
case "$isPrimitive":
if (valueValue) {
return "IS_PRIMITIVE(" + (prefix + key) + ")";
} else {
return "NOT IS_PRIMITIVE(" + (prefix + key) + ")";
}
break;
case "$startsWith":
return "STARTSWITH(" + (prefix + key) + ", " + (JSON.stringify(valueValue)) + ")";
case "$endsWith":
return "ENDSWITH(" + (prefix + key) + ", " + (JSON.stringify(valueValue)) + ")";
case "$contains":
return "CONTAINS(" + (prefix + key) + ", " + (JSON.stringify(valueValue)) + ")";
case "$geoWithin":
return "ST_WITHIN(" + (prefix + key) + ", " + (JSON.stringify(valueValue)) + ")";
case "$near":
maxDistance = valueValue.$maxDistance;
minDistance = valueValue.$minDistance;
if (maxDistance != null) {
if (minDistance != null) {
return "(ST_DISTANCE(" + (prefix + key) + ", " + (JSON.stringify(valueValue.$geometry)) + ") <= " + maxDistance + " AND ST_DISTANCE(" + (prefix + key) + ", " + (JSON.stringify(valueValue.$geometry)) + ") >= " + minDistance + ")";
} else {
return "ST_DISTANCE(" + (prefix + key) + ", " + (JSON.stringify(valueValue.$geometry)) + ") <= " + maxDistance;
}
}
if (minDistance != null) {
return "ST_DISTANCE(" + (prefix + key) + ", " + (JSON.stringify(valueValue.$geometry)) + ") >= " + minDistance;
} else {
throw new Error("No minDistance nor maxDistance found in {" + (prefix + key) + ": " + (JSON.stringify(value)) + "}");
}
break;
default:
throw new Error("sql-from-mongo does not recognize {" + (prefix + key) + ": " + (JSON.stringify(value)) + "}");
}
}
keys = [];
for (key2 in value) {
value2 = value[key2];
keys.push(key2);
}
if (keys.length === 1) {
return parts[0];
} else {
return "(" + parts.join(" AND ") + ")";
}
} else {
return (prefix + key) + " = " + (JSON.stringify(value));
}
}
};
if ((collectionName != null) && collectionName.length > 0) {
prefix = collectionName + ".";
} else {
prefix = "";
}
keys = [];
for (key in mongoQueryObject) {
value = mongoQueryObject[key];
keys.push(key);
}
if (keys.length === 1) {
parts = [parseSingleKeyValuePair(keys[0], mongoQueryObject[keys[0]], collectionName)];
} else {
parts = [];
for (key in mongoQueryObject) {
value = mongoQueryObject[key];
subObject = {};
subObject[key] = value;
parts.push(sqlFromMongo(subObject, collectionName));
}
}
if (parts.length === 1) {
sql = parts[0];
} else {
sql = "(" + parts.join(" AND ") + ")";
}
if (fields != null) {
if (fields === '*' || (fields[0] === '*') || fields === true) {
fieldsString = '*';
} else {
fieldStringParts = (function() {
var i, len, results;
results = [];
for (i = 0, len = fields.length; i < len; i++) {
field = fields[i];
results.push(prefix + field);
}
return results;
})();
fieldsString = fieldStringParts.join(", ");
}
sql = ("SELECT " + fieldsString + " FROM " + collectionName + " WHERE ") + sql;
}
return sql;
};
exports.sqlFromMongo = sqlFromMongo;
}).call(this);
//# sourceMappingURL=sql-from-mongo.js.map