UNPKG

mongo-portable

Version:

Portable Pure JS MongoDB - Based on Monglodb (https://github.com/euforic/monglodb.git) by Christian Sullivan (http://RogueSynaptics.com)

451 lines 18 kB
"use strict"; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; }; Object.defineProperty(exports, "__esModule", { value: true }); var jsw_logger_1 = require("jsw-logger"); var _ = require("lodash"); var SelectorMatcher_1 = require("./SelectorMatcher"); var index_1 = require("../document/index"); var Selector = /** @class */ (function () { function Selector(selector, type) { if (type === void 0) { type = Selector.MATCH_SELECTOR; } this.logger = jsw_logger_1.JSWLogger.instance; this.selectorCompiled = null; if (type === Selector.MATCH_SELECTOR) { this.selectorCompiled = this.compile(selector); } else if (type === Selector.SORT_SELECTOR) { return this.compileSort(selector); } else if (type === Selector.FIELD_SELECTOR) { return this.compileFields(selector, false); } else if (type === Selector.AGG_FIELD_SELECTOR) { return this.compileFields(selector, true); } else { this.logger.throw("You need to specify the selector type"); } } Selector.prototype.test = function (doc) { return this.selectorCompiled.test(doc); }; Selector.prototype.compile = function (selector) { if (_.isNil(selector)) { this.logger.debug("selector -> null"); selector = {}; } else { this.logger.debug("selector -> not null"); if (!selector || (_.hasIn(selector, "_id") && !selector._id)) { this.logger.debug("selector -> false value || { _id: false value }"); selector = { _id: false }; } } if (_.isFunction(selector)) { this.logger.debug("selector -> function(doc) { ... }"); // _initFunction.call(matcher, selector); this.clauses = [{ kind: "function", value: selector }]; this.logger.debug("clauses created: " + JSON.stringify(this.clauses)); } else if (_.isString(selector) || _.isNumber(selector)) { this.logger.debug("selector -> \"123456789\" || 123456798"); selector = { _id: selector }; // _initObject.call(matcher, selector); this.clauses = this.__buildSelector(selector); this.logger.debug("clauses created: " + JSON.stringify(this.clauses)); } else { this.logger.debug("selector -> { field: value }"); // _initObject.call(matcher, selector); this.clauses = this.__buildSelector(selector); this.logger.debug("clauses created: " + JSON.stringify(this.clauses)); } var matcher = new SelectorMatcher_1.SelectorMatcher(this); return matcher; }; Selector.prototype.compileSort = function (spec) { if (_.isNil(spec)) { return function () { return 0; }; } var keys = []; var asc = []; if (_.isString(spec)) { spec = spec.replace(/( )+/ig, " ").trim(); if (spec.indexOf(",") !== -1) { // Replace commas by spaces, and treat it as a spaced-separated string return this.compileSort(spec.replace(/,/ig, " ")); } else if (spec.indexOf(" ") !== -1) { var fields = spec.split(" "); for (var i = 0; i < fields.length; i++) { var field = fields[i].trim(); if ((field === "desc" || field === "asc") || (field === "-1" || field === "1") || (field === "false" || field === "true")) { this.logger.throw("Bad sort specification: %s", JSON.stringify(spec)); } else { var next = _.toString(fields[i + 1]); if (next === "desc" || next === "asc") { keys.push(field); asc.push((next === "asc") ? true : false); i++; } else if (next === "-1" || next === "1") { keys.push(field); asc.push((next === "1") ? true : false); i++; } else if (next === "false" || next === "true") { keys.push(field); asc.push((next === "true") ? true : false); i++; } else { keys.push(field); asc.push(true); // Default sort } } } } else { // .sort("field1") keys.push(spec); asc.push(true); } } else if (_.isArray(spec)) { // Join the array with spaces, and treat it as a spaced-separated string return this.compileSort(spec.join(" ")); // for (var i = 0; i < spec.length; i++) { // if (_.isString(spec[i])) { // keys.push(spec[i]); // asc.push(true); // } else { // keys.push(spec[i][0]); // asc.push(spec[i][1] !== "desc"); // } // } } else if (_.isPlainObject(spec)) { // TODO Nested path -> .sort({ "field1.field12": "asc" }) var _spec = []; for (var key in spec) { if (_.hasIn(spec, key)) { _spec.push(key); _spec.push(spec[key]); } } return this.compileSort(_spec); } else { this.logger.throw("Bad sort specification: %s", JSON.stringify(spec)); } // return {keys: keys, asc: asc}; return function (a, b) { var x = 0; for (var i = 0; i < keys.length; i++) { if (i !== 0 && x !== 0) { return x; } // Non reachable? // x = Selector._f._cmp(a[JSON.stringify(keys[i])], b[JSON.stringify(keys[i])]); x = SelectorMatcher_1.SelectorMatcher.cmp(a[keys[i]], b[keys[i]]); if (!asc[i]) { x *= -1; } } return x; }; // eval() does not return a value in IE8, nor does the spec say it // should. Assign to a local to get the value, instead. // var _func; // var code = "_func = (function(c){return function(a,b){var x;"; // for (var i = 0; i < keys.length; i++) { // if (i !== 0) { // code += "if(x!==0)return x;"; // } // code += "x=" + (asc[i] ? "" : "-") + "c(a[" + JSON.stringify(keys[i]) + "],b[" + JSON.stringify(keys[i]) + "]);"; // } // code += "return x;};})"; // eval(code); // return _func(Selector._f._cmp); }; Selector.prototype.compileFields = function (spec, aggregation) { var projection = {}; if (_.isNil(spec)) { return projection; } if (_.isString(spec)) { // trim surrounding and inner spaces spec = spec.replace(/( )+/ig, " ").trim(); // Replace the commas by spaces if (spec.indexOf(",") !== -1) { // Replace commas by spaces, and treat it as a spaced-separated string return this.compileFields(spec.replace(/,/ig, " "), aggregation); } else if (spec.indexOf(" ") !== -1) { var fields = spec.split(" "); for (var i = 0; i < fields.length; i++) { // Get the field from the spec (we will be working with pairs) var field = fields[i].trim(); // If the first is not a field, throw error if ((field === "-1" || field === "1") || (field === "false" || field === "true")) { this.logger.throw("Bad fields specification: %s", JSON.stringify(spec)); } else { // Get the next item of the pair var next = _.toString(fields[i + 1]); if (next === "-1" || next === "1") { if (next === "-1") { for (var _key in projection) { if (field !== "_id" && projection[_key] === 1) { this.logger.throw("A projection cannot contain both include and exclude specifications"); } } projection[field] = -1; } else { projection[field] = 1; } i++; } else if (next === "false" || next === "true") { if (next === "false") { if (field === "_id") { projection[field] = -1; } else { this.logger.throw("A projection cannot contain both include and exclude specifications"); } } else { projection[field] = 1; } i++; } else if (aggregation && next.indexOf("$") === 0) { projection[field] = next.replace("$", ""); i++; } else { projection[field] = 1; } } } } else if (spec.length > 0) { // .find({}, "field1") projection[spec] = 1; } } else if (_.isArray(spec)) { // Join the array with spaces, and treat it as a spaced-separated string return this.compileFields(spec.join(" "), aggregation); } else if (_.isPlainObject(spec)) { // TODO Nested path -> .find({}, { "field1.field12": "asc" }) var _spec = []; for (var key in spec) { if (_.hasIn(spec, key)) { _spec.push(key); _spec.push(spec[key]); } } return this.compileFields(_spec, aggregation); } else { this.logger.throw("Bad fields specification: %s", JSON.stringify(spec)); } return projection; }; Selector.prototype.createClause = function () { return { key: null, kind: null, type: null, value: null }; }; Selector.prototype.__buildSelector = function (selector) { var e_1, _a; this.logger.debug("Called: __buildSelector"); var clauses = []; try { for (var _b = __values(Object.keys(selector)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; var value = selector[key]; if (key.charAt(0) === "$") { this.logger.debug("selector -> operator => { $and: [{...}, {...}] }"); clauses.push(this.buildDocumentSelector(key, value)); } else { this.logger.debug("selector -> plain => { field1: <value> }"); clauses.push(this.buildKeypathSelector(key, value)); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return clauses; }; Selector.prototype.buildDocumentSelector = function (key, value) { var e_2, _a; var clause = this.createClause(); switch (key) { case "$or": // falls through case "$and": // falls through case "$nor": clause.key = key.replace(/\$/, ""); // falls through // The rest will be handled by "_operator_" case "_operator_": // Generic handler for operators ($or, $and, $nor) clause.kind = "operator"; clause.type = "array"; clause.value = []; try { for (var value_1 = __values(value), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) { var item = value_1_1.value; clause.value = _.union(clause.value, this.__buildSelector(item)); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (value_1_1 && !value_1_1.done && (_a = value_1.return)) _a.call(value_1); } finally { if (e_2) throw e_2.error; } } break; default: this.logger.throw("Unrecogized key in selector: %s", key); } // TODO cases: $where, $elemMatch this.logger.debug("clause created: " + JSON.stringify(clause)); return clause; }; Selector.prototype.buildKeypathSelector = function (keypath, value) { this.logger.debug("Called: buildKeypathSelector"); var clause = this.createClause(); clause.value = value; if (_.isNil(value)) { this.logger.debug("clause of type null"); clause.type = "null"; } else if (_.isRegExp(value)) { this.logger.debug("clause of type RegExp"); clause.type = "regexp"; var source = value.toString().split("/"); clause.value = { $regex: source[1] // The first item splitted is an empty string }; if (source[2] !== "") { clause.value.$options = source[2]; } } else if (_.isArray(value)) { this.logger.debug("clause of type Array"); clause.type = "array"; } else if (_.isString(value)) { this.logger.debug("clause of type String"); clause.type = "string"; } else if (_.isNumber(value)) { this.logger.debug("clause of type Number"); clause.type = "number"; } else if (_.isBoolean(value)) { this.logger.debug("clause of type Boolean"); clause.type = "boolean"; } else if (_.isFunction(value)) { this.logger.debug("clause of type Function"); clause.type = "function"; } else if (_.isPlainObject(value)) { var literalObject = true; for (var key in value) { if (key.charAt(0) === "$") { literalObject = false; break; } } if (literalObject) { this.logger.debug("clause of type Object => { field: { field_1: <value>, field_2: <value> } }"); clause.type = "literal_object"; } else { this.logger.debug("clause of type Operator => { field: { $gt: 2, $lt 5 } }"); clause.type = "operator_object"; } } else if (value instanceof index_1.ObjectId) { this.logger.debug("clause of type ObjectId -> String"); clause.type = "string"; clause.value = value.toString(); } else { clause.type = "__invalid__"; } var parts = keypath.split("."); if (parts.length > 1) { this.logger.debug("clause over Object field => { \"field1.field1_2\": <value> }"); clause.kind = "object"; clause.key = parts; } else { this.logger.debug("clause over Plain field => { \"field\": <value> }"); clause.kind = "plain"; clause.key = parts[0]; } this.logger.debug("clause created: " + JSON.stringify(clause)); return clause; }; /* STATIC METHODS */ Selector.isSelectorCompiled = function (selector) { if (!_.isNil(selector) && (selector instanceof SelectorMatcher_1.SelectorMatcher || (selector instanceof Selector && selector.selectorCompiled instanceof SelectorMatcher_1.SelectorMatcher))) { return true; } else { return false; } }; Selector.matches = function (selector, doc) { return (new Selector(selector)).test(doc); }; Selector.MATCH_SELECTOR = "match"; Selector.SORT_SELECTOR = "sort"; Selector.FIELD_SELECTOR = "field"; Selector.AGG_FIELD_SELECTOR = "project"; return Selector; }()); exports.Selector = Selector; //# sourceMappingURL=Selector.js.map