UNPKG

mongoose-diff-tracking

Version:
347 lines (289 loc) 11.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); exports.filter = filter; exports.checkCondition = checkCondition; var _fastDeepEqual = require('fast-deep-equal'); var _fastDeepEqual2 = _interopRequireDefault(_fastDeepEqual); var _oadUtils = require('oad-utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * */ var PowerFilter = function () { function PowerFilter() { _classCallCheck(this, PowerFilter); } _createClass(PowerFilter, null, [{ key: 'filter', value: function filter(values, where) { return this.classify(values, where).ok; } /** * @private * Classify values into match or unmatch groups */ }, { key: 'classify', value: function classify(values, where) { var _this = this; if (where.$and != null) { // TODO: confirm spec when $and is [] return where.$and.reduce(function (vals, conds) { var _classify = _this.classify(vals.ok, conds), ok = _classify.ok, ng = _classify.ng; return { ok: ok, ng: vals.ng.concat(ng) }; }, { ok: values, ng: [] }); } if (where.$nor != null) { // TODO: confirm spec when $nor is [] return where.$nor.reduce(function (vals, conds) { var _classify2 = _this.classify(vals.ok, conds), ok = _classify2.ok, ng = _classify2.ng; return { ok: ng, ng: vals.ng.concat(ok) }; }, { ok: values, ng: [] }); } if (where.$or != null) { // TODO: confirm spec when $or is [] return where.$or.reduce(function (vals, conds) { var _classify3 = _this.classify(vals.ng, conds), ok = _classify3.ok, ng = _classify3.ng; return { ok: vals.ok.concat(ok), ng: ng }; }, { ok: [], ng: values }); } // SimpleFindOperation var documentPaths = Object.keys(where); return values.reduce(function (classified, value) { var isOk = documentPaths.every(function (documentPath) { // $FlowIssue(queryCondition-is-QueryCondition-or-EqCondition) var queryCondition = where[documentPath]; var nestedValue = (0, _oadUtils.getNestedValue)(value, documentPath); return _this.checkCondition(nestedValue, (0, _oadUtils.normalizeQueryCondition)(queryCondition)); }); classified[isOk ? 'ok' : 'ng'].push(value); return classified; }, { ok: [], ng: [] }); } /** * Check if leftOperand matches the condition */ }, { key: 'checkCondition', value: function checkCondition(leftOperand, condition) { var _this2 = this; var operators = Object.keys(condition); return operators.every(function (operator) { switch (operator) { case '$eq': case '$gt': case '$gte': case '$lt': case '$lte': case '$ne': // $FlowIssue(condition-has-operator-key) return _this2.compare(operator, leftOperand, condition[operator]); case '$in': case '$nin': // $FlowIssue(condition-has-operator-key) return _this2.compareIn(operator, leftOperand, condition[operator]); case '$not': if (condition.$not == null) throw new Error('$not is not found'); // for flow return _this2.checkCondition(leftOperand, condition.$not) === false; case '$exists': return leftOperand != null === condition.$exists; case '$type': return typeof condition.$type === 'number' ? getBSONTypeNumber(leftOperand) === condition.$type : getBSONTypeString(leftOperand) === condition.$type; case '$mod': if (condition.$mod == null) throw new Error('$mod is not found'); // for flow var _condition$$mod = _slicedToArray(condition.$mod, 2), divisor = _condition$$mod[0], remainder = _condition$$mod[1]; return leftOperand % divisor === remainder; case '$regex': if (condition.$regex == null) throw new Error('$regex is not found'); // for flow var regex = typeof condition.$regex === 'string' ? new RegExp(condition.$regex, condition.$options || undefined) // "null" is not allowed but undefined. : condition.$regex; return regex.test(leftOperand); case '$text': case '$where': case '$geoIntersects': case '$geoWithin': case '$near': case '$nearSphere': throw new Error('Operator "' + operator + '" is currently unimplemented in power-filter.'); case '$all': if (condition.$all == null) throw new Error('$all is not found'); // for flow if (!Array.isArray(leftOperand)) return false; return condition.$all.every(function (val) { return leftOperand.some(function (elem) { return (0, _fastDeepEqual2.default)(elem, val); }); }); case '$elemMatch': if (condition.$elemMatch == null) throw new Error('$elemMatch is not found'); // for flow if (!Array.isArray(leftOperand)) return false; return leftOperand.every(function (elem) { return ( // $FlowIssue(condition-$elemMatch-is-not-null) _this2.checkCondition(elem, condition.$elemMatch) ); }); case '$size': if (!Array.isArray(leftOperand)) return false; return leftOperand.length === condition.$size; case '$bitsAllClear': case '$bitsAllSet': case '$bitsAnyClear': case '$bitsAnySet': throw new Error('Operator "' + operator + '" is currently unimplemented in power-filter.'); // co-operator of $regex case '$options': return true; default: throw new Error('Unknown operator: "' + operator + '".'); } }); } /** * Compare values * when to query an array for an element * return true if the array field contains at least one matched element * @see https://docs.mongodb.com/manual/tutorial/query-arrays/#query-an-array-for-an-element */ }, { key: 'compareIn', value: function compareIn(operator, target, condValues) { var _this3 = this; if (!Array.isArray(condValues)) throw new Error(operator + ' needs an array'); if (!Array.isArray(target)) { return COMPARE_FUNC[operator](target, condValues); } var isIn = condValues.some(function (condValue) { return _this3.compare('$eq', target, condValue); }); return operator === '$in' ? isIn : !isIn; } }, { key: 'compare', value: function compare(operator, target, condValue) { var compareFunc = COMPARE_FUNC[operator]; var isQueryArrayForAnElement = Array.isArray(target) && !Array.isArray(condValue); if (isQueryArrayForAnElement) { if (operator === '$ne') { compareFunc = COMPARE_FUNC['$eq']; return !target.some(function (val) { return compareFunc(val, condValue); }); } return target.some(function (val) { return compareFunc(val, condValue); }); } return compareFunc(target, condValue); } }]); return PowerFilter; }(); exports.default = PowerFilter; var COMPARE_FUNC = { $eq: _fastDeepEqual2.default, $gt: function $gt(t, c) { return t > c; }, $gte: function $gte(t, c) { return t >= c; }, $in: function $in(t, c) { return c.some(function (v) { return (0, _fastDeepEqual2.default)(t, v); }); }, $lt: function $lt(t, c) { return t < c; }, $lte: function $lte(t, c) { return t <= c; }, $ne: function $ne(t, c) { return !(0, _fastDeepEqual2.default)(t, c); }, $nin: function $nin(t, c) { return !c.some(function (v) { return (0, _fastDeepEqual2.default)(t, v); }); } /** * Filter the given values */ };function filter(values, where) { return PowerFilter.filter(values, where); } /** * Check if the givne value matches the condition */ function checkCondition(value, condition) { return PowerFilter.checkCondition(value, condition); } /** * get BSON Type of the given value as a string * @see https://docs.mongodb.com/manual/reference/operator/query/type/ */ function getBSONTypeString(val) { switch (typeof val === 'undefined' ? 'undefined' : _typeof(val)) { case 'number': return parseInt(val, 10) === val ? 'int' : 'double'; case 'string': return 'string'; case 'boolean': return 'bool'; case 'undefined': return 'undefined'; case 'function': return 'javascript'; case 'object': default: if (val instanceof Date) return 'date'; if (val instanceof RegExp) return 'regex'; if (Array.isArray(val)) return 'array'; return val ? 'object' : 'null'; // TODO: confirm spec } } /** * get BSON Type of the given value as a number * @see https://docs.mongodb.com/manual/reference/operator/query/type/ */ function getBSONTypeNumber(val) { var map = { double: 1, string: 2, object: 3, array: 4, binData: 5, undefined: 6, objectId: 7, bool: 8, date: 9, null: 10, regex: 11, dbPointer: 12, javascript: 13, symbol: 14, javascriptWithScope: 15, int: 16, timestamp: 17, long: 18, decimal: 19, minKey: -1, maxKey: 127 }; return map[getBSONTypeString(val)]; }