mongoose-diff-tracking
Version:
Manage Mongo Collection diff History and versions
347 lines (289 loc) • 11.4 kB
JavaScript
;
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)];
}