UNPKG

obop

Version:

MongoDB-style object operators makes array manipulation easy: where/order/update/view

1,156 lines (1,018 loc) 29.5 kB
function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } /*! dollar_where.js */ var dollar_where; var hasRequiredDollar_where; function requireDollar_where () { if (hasRequiredDollar_where) return dollar_where; hasRequiredDollar_where = 1; function $where() { if (!(this instanceof $where)) return new $where(); } dollar_where = $where; $where.prototype = { // Matches values that are greater than the value specified in the query. $gt: function(val) { return function(test) { return (test > val); }; }, // Matches values that are equal to or greater than the value specified in the query. $gte: function(val) { return function(test) { return (test >= val); }; }, // Matches any of the values that exist in an array specified in the query. $in: function(arr) { if (!(arr instanceof Array)) { return new Error("invalid query"); // 12580 } var len = arr.length; return function(test) { for (var i = 0; i < len; i++) { /* jshint eqeqeq:false */ if (test == arr[i]) return true; } return false; }; }, // Matches vales that are less than the value specified in the query. $lt: function(val) { return function(test) { return (test < val); }; }, // Matches values that are less than or equal to the value specified in the query. $lte: function(val) { return function(test) { return (test <= val); }; }, // Matches all values that are not equal to the value specified in the query. $ne: function(val) { return function(test) { /* jshint eqeqeq:false */ return (test != val); }; }, // Matches values that do not exist in an array specified to the query. $nin: function(arr) { var len = arr.length; return function(test) { for (var i = 0; i < len; i++) { /* jshint eqeqeq:false */ if (test == arr[i]) return false; } return true; }; }, // Joins query clauses with a logical OR returns all documents that match the conditions of either clause. $or: function(arr, obop) { if (!arr) return; if (!(arr instanceof Array) || arr.length === 0) { return new Error("$or requires nonempty array"); // 13262 } var out = []; arr.forEach(function(val) { var where = obop.where(val); out.push(where); }); return join_or(out); }, // Joins query clauses with a logical AND returns all documents that match the conditions of both clauses. $and: function(arr, obop) { if (!arr) return; if (!(arr instanceof Array) || arr.length === 0) { return new Error("$and expression must be a nonempty array"); // 14816 } var out = []; arr.forEach(function(val) { var where = obop.where(val); out.push(where); }); return join_and(out); }, // Inverts the effect of a query expression and returns documents that do not match the query expression. $not: function(val, obop) { var where = obop.where(val); return function(test) { return !where(test); }; }, // Matches documents that have the specified field. $exists: function(val) { return function(test) { var exists = ('undefined' !== typeof test); return (exists === val); }; }, // Selects documents if the array field is a specified size. $size: function(val) { return function(test) { var len; var exists = ('undefined' !== typeof test); if (!exists) { // len = 0; return false; } else if (!(test instanceof Array)) { // len = 1; return false; // follow MongoDB behavior } else { len = test.length; } return len === val; }; } }; function check_err(array) { var len = array.length; for (var i = 0; i < len; i++) { var f = array[i]; if (f instanceof Error) return f; } } function join_and(array) { var len = array.length; return check_err(array) || function(item) { for (var i = 0; i < len; i++) { var f = array[i]; if (!f(item)) return false; } return true; }; } function join_or(array) { var len = array.length; return check_err(array) || function(item) { for (var i = 0; i < len; i++) { var f = array[i]; if (f(item)) return true; } return false; }; } return dollar_where; } /*! dollar_update.js */ var dollar_update; var hasRequiredDollar_update; function requireDollar_update () { if (hasRequiredDollar_update) return dollar_update; hasRequiredDollar_update = 1; function $update() { if (!(this instanceof $update)) return new $update(); } dollar_update = $update; $update.prototype = { // Sets the value of a field in an existing document. $set: function(hash) { return function(item) { if ('object' !== typeof item) return item; for (var key in hash) { item[key] = hash[key]; } return item; }; }, // Removes the specified field from an existing document. $unset: function(hash) { return function(item) { if ('object' !== typeof item) return item; for (var key in hash) { delete item[key]; } return item; }; }, // Renames a field. $rename: function(hash) { return function(item) { if ('object' !== typeof item) return item; for (var key in hash) { var val = item[key]; if ('undefined' === typeof val) continue; delete item[key]; var newkey = hash[key]; item[newkey] = val; } return item; }; }, // Adds an item to an array. $push: function(hash) { return function(item) { if ('object' !== typeof item) return item; for (var key in hash) { var val = hash[key]; var old = item[key]; if (old instanceof Array) ; else if ('undefined' === typeof old) { item[key] = []; } else { // MongoDB: Cannot apply $push/$pushAll modifier to non-array // continue; item[key] = [old]; // upgrade to array } item[key].push(val); } return item; }; }, // Removes items from an array that match a query statement. $pull: function(hash) { return function(item) { if ('object' !== typeof item) return item; for (var key in hash) { /* jshint eqeqeq:false */ var val = hash[key]; var old = item[key]; if (old instanceof Array) { var tmp = []; var len = old.length; for (var i = 0; i < len; i++) { var test = old[i]; if (val != test) tmp.push(test); } item[key] = tmp; } else if (val == old) { item[key] = []; // empty array } } return item; }; }, // Increments the value of the field by the specified amount. $inc: function(hash) { return function(item) { if ('object' !== typeof item) return item; for (var key in hash) { var val = hash[key]; var old = item[key]; val = parseFloat(val) || 0; old = parseFloat(old) || 0; item[key] = old + val; } return item; }; } }; return dollar_update; } var where = {}; /*! where.js */ var hasRequiredWhere; function requireWhere () { if (hasRequiredWhere) return where; hasRequiredWhere = 1; /** * This search items from an array, or generates a conditional function compiled from where-clause operators object. * Since version 0.0.4, dot notation (e.g. foo.bar.baz) is supported to handle child nodes. * * @method obop.prototype.where * @param {Array} [array] - source array to search * @param {Object|Function} param - query selector or function * @see http://docs.mongodb.org/manual/reference/operator/#query-params * @returns {Array} Array of matched items (if source array given) * @returns {Function} Conditional function compiled for Array.prototype.filter() method (if source array not given) * @returns {Error} Error instance (if source array not given but invalid query selector given) * * @example * var list = [ * { name: "apple", price: 50 }, * { name: "orange", price: 10 }, * { name: "pineapple", price: 70 }, * { name: "grape", price: 30 } * ]; * * // search without obop * var out1 = list.filter(function(item) { * return item.name == "orange"; * }); * console.log(out1); * * // search with obop * var where = { name: "orange" }; * var out2 = obop.where(list, where); * console.log(out2); * * // compile a search function * var where = { name: "orange" }; * var filter = obop.where(where); * if (filter instanceof Error) throw filter; * var out3 = list.filter(filter); * console.log(out3); */ where.where = function(array, param) { var func; var len = arguments.length; if (len === 1) { func = where$1.call(this, array); if (func instanceof Error) throw func; return func; } else if (len === 2) { if (array instanceof Array) { func = where$1.call(this, param); if (func instanceof Error) throw func; return array.filter(func); } else { throw new Error('Invalid argument type: ' + array); } } else { throw new Error('Invalid arguments length: ' + len); } }; function where$1(param) { var obop = this; var _nop = null; // no operation // function type if ('function' === typeof param) { return param; } // default parameters param = param || {}; // other types than object if ('object' !== typeof param) { return new Error('Invalid where operator type: ' + param); } var array = []; var equal = {}; var hasdot = {}; var children = {}; // group queries for (var key1 in param) { if (/(^|\.)__proto__(\.|$)/.test(key1)) { return new Error('Invalid target: ' + key1); } var val1 = param[key1]; var pos1 = key1.indexOf('.'); var gen1 = obop.$where[key1]; if (gen1) { // {"$gt": 1234} var f = gen1(val1, obop, key1); if (f) array.push(f); } else if (pos1 > -1) { // {"foo.bar": 1234} var pre1 = key1.substr(0, pos1); var post1 = key1.substr(pos1 + 1); hasdot[pre1] = hasdot[pre1] || {}; hasdot[pre1][post1] = val1; } else if (val1 instanceof Array) { // {"foo": []} return new Error('Unknown where operator: ' + key1); } else if ('object' === typeof val1) { // {"foo": {}} children[key1] = val1; } else { // {"foo": 1234} equal[key1] = val1; } } var sckeys = Object.keys(equal); if (sckeys.length === 1) { // one equal operator (faster) var key = sckeys[0]; var val = param[key]; var single_eq = function(item) { /* jshint eqeqeq:false */ return ('object' === typeof item) && (item[key] == val); }; array.push(single_eq); } else if (sckeys.length > 1) { // more equal operators var multiple_eq = function(item) { if ('object' !== typeof item) return false; for (var key in equal) { /* jshint eqeqeq:false */ if (item[key] != equal[key]) return false; } return true; }; array.push(multiple_eq); } var err; add_children(hasdot); add_children(children); if (err) return new Error(err); // one query type used if (array.length < 2) { return array.shift() || _nop; } // more query types used return join_and(array); function add_children(hash) { Object.keys(hash).forEach(function(key) { if (err) return; var param = hash[key]; var where = obop.where(param); if (!where) return; if (where instanceof Error) { err = where; return; } var f = function(item) { return where(item ? item[key] : void 0); }; if (f) array.push(f); }); } } function check_err(array) { var len = array.length; for (var i = 0; i < len; i++) { var f = array[i]; if (f instanceof Error) return f; } } function join_and(array) { var len = array.length; return check_err(array) || function(item) { for (var i = 0; i < len; i++) { var f = array[i]; if (!f(item)) return false; } return true; }; } return where; } var view = {}; /*! view.js */ var hasRequiredView; function requireView () { if (hasRequiredView) return view; hasRequiredView = 1; /** * This filters fields of items, or generates a projection function compiled from view mapping parameters object. * Since version 0.0.6, dot notation (e.g. foo.bar.baz) is supported to handle child nodes. * * @method obop.prototype.view * @param {Array} [array] - source array to map * @param {Object|Function} param - output fields or function * @returns {Array} Array of filtered items (if source array given) * @returns {Function} Projection function compiled for Array.prototype.map() method (if source array not given) * @returns {Error} Error instance (if source array not given but invalid output fields specified) * * @example * var list = [ * { name: "apple", price: 50 }, * { name: "orange", price: 10 }, * { name: "pineapple", price: 70 }, * { name: "grape", price: 30 } * ]; * * // map without obop * var out1 = list.map(function(item) { * return { name: item.name }; * }); * console.log(out1); * * // map with obop * var view = { name: 1 }; * var out2 = obop.view(list, view); * console.log(out2); * * // compile a map function * var view = { name: 1 }; * var filter = obop.view(view); * if (filter instanceof Error) throw filter; * var out3 = list.map(filter); * console.log(out3); */ view.view = function(array, param) { var func; var len = arguments.length; if (len === 1) { func = view$1.call(this, array); if (func instanceof Error) throw func; return func; } else if (len === 2) { if (array instanceof Array) { func = view$1.call(this, param); if (func instanceof Error) throw func; return array.map(func); } else { throw new Error('Invalid argument type: ' + array); } } else { throw new Error('Invalid arguments length: ' + len); } }; function view$1(param) { var obop = this; var _nop = null; // no operation // function type if ('function' === typeof param) { return param; } // default parameters param = param || {}; // other types than object if ('object' !== typeof param) { return new Error('Invalid view parameters type: ' + param); } var queries = Object.keys(param); // no param: all properties if (!queries.length) { return _nop; } var key0 = queries[0]; var truthy = {}; var falsy = {}; var dottruthy = {}; var dotfalsy = {}; var hasdot; // group parameters for (var key in param) { if (/(^|\.)__proto__(\.|$)/.test(key)) { return new Error('Invalid target: ' + key); } var val = param[key]; var pos1 = key.indexOf('.'); if (pos1 > -1) { var pre = key.substr(0, pos1); var post = key.substr(pos1 + 1); hasdot = true; if (val) { // {"foo.bar": 1} dottruthy[pre] = dottruthy[pre] || {}; dottruthy[pre][post] = val; } else { // {"foo.bar": 0} dotfalsy[pre] = dotfalsy[pre] || {}; dotfalsy[pre][post] = val; } } else if (val) { // {"foo": 1} truthy[key] = true; } else { // {"bar": 0} falsy[key] = true; } } if (hasdot) { Object.keys(dottruthy).forEach(function(key) { var param = dottruthy[key]; truthy[key] = obop.view(param); }); Object.keys(dotfalsy).forEach(function(key) { var param = dotfalsy[key]; falsy[key] = obop.view(param); }); } var _truthy = Object.keys(truthy).length; var _falsy = Object.keys(falsy).length; // single truthy field: faster if (_truthy === 1 && !_falsy && !hasdot) { return single_view; } if (_truthy && !_falsy) { return hasdot ? truthy_has_dot : truthy_view; } if (!_truthy && _falsy) { return hasdot ? falsy_has_dot : falsy_view; } return truthy_and_falsy; function single_view(item) { var out = {}; if (item.hasOwnProperty(key0)) { out[key0] = item[key0]; } return out; } // negative filter function falsy_view(item) { var out = {}; Object.keys(item).forEach(function(key) { if (!falsy[key]) { out[key] = item[key]; } }); return out; } // positive filter function truthy_view(item) { var out = {}; Object.keys(item).forEach(function(key) { if (truthy[key]) { out[key] = item[key]; } }); return out; } // negative filter (with dot) function falsy_has_dot(item) { var out = {}; Object.keys(item).forEach(function(key) { var view = falsy[key]; var val = item[key]; if ('function' === typeof view) { if ('object' === typeof val) { out[key] = view(val); } else { out[key] = val; } } else if (!view) { out[key] = val; } }); return out; } // positive filter (with dot) function truthy_has_dot(item) { var out = {}; Object.keys(item).forEach(function(key) { var view = truthy[key]; var val = item[key]; if ('function' === typeof view) { if ('object' === typeof val) { out[key] = view(val); } } else if (view) { out[key] = val; } }); return out; } // both positive and negative filters function truthy_and_falsy(item) { if (_truthy) item = truthy_has_dot(item); if (_falsy) item = falsy_has_dot(item); return item; } } return view; } var order = {}; /*! order.js */ var hasRequiredOrder; function requireOrder () { if (hasRequiredOrder) return order; hasRequiredOrder = 1; /** * This sorts an array of items, or generates a sort function compiled from order-by-clause sort parameters object. * Since version 0.0.7, dot notation (e.g. foo.bar.baz) is supported to handle child nodes. * * @method obop.prototype.order * @param {Array} [array] - source array to sort * @param {Object|Array|Function} sort - sort parameters or function * @returns {Array} Sorted array of items (if source array given) * @returns {Function} Sort function compiled for Array.prototype.sort() method (if source array not given) * @returns {Error} Error instance (if source array not given but invalid sort parameters given) * * @example * var list = [ * { name: "apple", price: 50 }, * { name: "orange", price: 10 }, * { name: "pineapple", price: 70 }, * { name: "grape", price: 30 } * ]; * * // without obop * var out1 = list.sort(function(a, b) { * return a.price - b.price; * }); * console.log(out1); * * // with obop * var order = { price: 1 }; * var out2 = obop.order(list, order); * console.log(out2); * * // compile a sort function * var order = { price: 1 }; * var sorter = obop.order(order); * if (sorter instanceof Error) throw sorter; * var out3 = list.sort(sorter); * console.log(out3); */ order.order = function(array, param) { var func; var len = arguments.length; if (len === 1) { func = order$1.call(this, array); if (func instanceof Error) throw func; return func; } else if (len === 2) { if (array instanceof Array) { func = order$1.call(this, param); if (func instanceof Error) throw func; return array.sort(func); } else { throw new Error('Invalid argument type: ' + array); } } else { throw new Error('Invalid arguments length: ' + len); } }; function order$1(param) { var obop = this; var _nop = null; // no operation // function type if ('function' === typeof param) { return param; } // default parameters param = param || {}; // other types than object if ('object' !== typeof param) { return new Error('Invalid order operator type: ' + param); } if (param instanceof Array) { // Array format: [ ["foo", 1], ["bar", -1] ] var err; param.forEach(function(pair) { if (err) return; // each pair must have a couple of elements if ((pair instanceof Array) && (pair.length === 2) && ('undefined' !== typeof pair[0]) && (pair[1] - pair[1] === 0)) ; else { err = new Error('Invalid order pair: ' + pair); } }); if (err) return err; } else { // Object format: { "foo": 1, "bar": -1 } var array = []; for (var key in param) { if (/(^|\.)__proto__(\.|$)/.test(key)) { return new Error('Invalid target: ' + key); } var pair = [key, param[key]]; array.push(pair); } param = array; } var len = param.length; // no parameters if (len === 0) { return _nop; } // one or more parameters var func; for (var i = len - 1; i >= 0; i--) { func = gen(param[i][0], param[i][1], func); } return func; function gen(key, ret, next) { var pos = key.indexOf('.'); var pre, post, pair, test; if (pos > -1) { pre = key.substr(0, pos); post = key.substr(pos + 1); pair = [post, ret]; test = obop.order([pair]); return sort_dot; } else { return sort; } var undef; // function sort(a, b) { // return (a[key] < b[key]) ? -ret : (a[key] > b[key]) ? ret : next ? next(a, b) : 0; // } function sort(a, b) { var aa = a[key]; var bb = b[key]; if (aa > bb) return ret; if (aa < bb) return -ret; if (aa !== undef && bb === undef) return ret; // aa > bb == undef if (aa === undef && bb !== undef) return -ret; // bb > aa == undef if (next) return next(a, b); return 0; } function sort_dot(a, b) { var aa = a[pre]; var bb = b[pre]; var ao = (aa && 'object' === typeof aa); var bo = (bb && 'object' === typeof bb); if (ao || bo) { if (!ao) aa = {}; if (!bo) bb = {}; var re = test(aa, bb); if (re) return re; } if (next) return next(a, b); return 0; } } } return order; } var update = {}; /*! update.js */ var hasRequiredUpdate; function requireUpdate () { if (hasRequiredUpdate) return update; hasRequiredUpdate = 1; /** * This updates an array of items, or generates an update compiled function from update-clause operators object. * Since version 0.0.4, dot notation (e.g. foo.bar.baz) is supported to handle child nodes. * * @method obop.prototype.update * @param {Array} [array] - source array to update * @param {Object|Function} update - update operators or function * @see http://docs.mongodb.org/manual/reference/operator/#update-operators * @returns {Array} Array of updated items (if source array given) * @returns {Function} Update function compiled for Array.prototype.map() method (if source array not given) * @returns {Error} Error instance (if source array not given but invalid update operaters given) * * @example * var list = [ * { name: "apple", price: 50 }, * { name: "orange", price: 10 }, * { name: "pineapple", price: 70 }, * { name: "grape", price: 30 } * ]; * * // update without obop * var out1 = list.filter(function(item) { * item.sale = true; * item.price -= 5; * return item; * }); * console.log(out1); * * // update with obop * var update = { $set: { sale: true }, $inc: { price: -5 } }; * var out2 = obop.update(list, update); * console.log(out2); * * // compile a update function * var update = { $set: { sale: true }, $inc: { price: -5 } }; * var updater = obop.update(update); * if (updater instanceof Error) throw updater; * var out3 = list.map(updater); * console.log(out3); */ update.update = function(array, param) { var func; var len = arguments.length; if (len === 1) { func = update$1.call(this, array); if (func instanceof Error) throw func; return func; } else if (len === 2) { if (array instanceof Array) { func = update$1.call(this, param); if (func instanceof Error) throw func; return array.map(func); } else { throw new Error('Invalid argument type: ' + array); } } else { throw new Error('Invalid arguments length: ' + len); } }; function update$1(param) { var obop = this; var _nop = null; // no operation var err; // function type if ('function' === typeof param) { return param; } // default parameters param = param || {}; // no operators: through if (!Object.keys(param)) { return _nop; } // other types than object if ('object' !== typeof param) { return new Error('Invalid update operator type: ' + update$1); } // parse operators var array = []; Object.keys(param).forEach(function(op) { if (err) return; var gen = obop.$update[op]; if (!gen) { err = new Error('Unknown update operator: ' + op); return; } var hash = param[op]; var root = {}; var hasdot = {}; for (var key in hash) { if (/(^|\.)__proto__(\.|$)/.test(key)) { err = new Error('Invalid target: ' + key); return; } var val = hash[key]; var pos = key.indexOf('.'); if (pos > -1) { // $op: { "pre.post": "val" } var pre = key.substr(0, pos); var post = key.substr(pos + 1); hasdot[pre] = hasdot[pre] || {}; hasdot[pre][post] = val; } else { // $op: { "key": "val" } root[key] = val; } } // $op: { "key": "val" } if (Object.keys(root).length) { var f = gen(root); array.push(f); } // $op: { "pre.post": "val" } if (Object.keys(hasdot).length) { Object.keys(hasdot).forEach(function(key) { var tmp = {}; tmp[op] = hasdot[key]; var update = obop.update(tmp); var f = function(item) { var hash = item[key]; if ('object' !== typeof hash) { hash = item[key] = {}; } item[key] = update(hash); return item; }; array.push(f); }); } }); if (err) return err; // one operator type used if (array.length < 2) { return array.shift() || _nop; } // more operator types used return join(array); } function join(array) { var len = array.length; return function(item) { for (var i = 0; i < len; i++) { var update = array[i]; item = update(item); } return item; }; } return update; } var name = "obop"; var version = "1.0.0"; var require$$6 = { name: name, version: version }; /*! index.js */ var obop_1; var hasRequiredObop; function requireObop () { if (hasRequiredObop) return obop_1; hasRequiredObop = 1; /** * This class provides a set of methods * [where()]{@linkcode obop#where} * / * [order()]{@linkcode obop#order} * / * [update()]{@linkcode obop#update} * / * [view()]{@linkcode obop#view} * which manipulate array such like MongoDB-style object operators do. * * @class obop * @property {String} system.name - Read only. System name: "obop" * @property {String} system.version - Read only. System version: e.g. "0.0.1" * @see https://github.com/kawanet/obop * @see http://kawanet.github.io/obop/docs/obop.html * @see https://raw.github.com/kawanet/obop/master/build/obop.min.js * * @example * // node.js * var obop = require('obop')(); * var src = [ { a: 1 }, { a: 2 }, { a: 3 } ]; * var out = obop.where( src, { a: 2 } ); // => [ { a: 2 } ] * * @example * &lt;script src="obop.min.js"&gt;&lt;/script&gt; * &lt;script&gt; * var src = [ { a: 1 }, { a: 2 }, { a: 3 } ]; * var out = obop.where( src, { a: 2 } ); // => [ { a: 2 } ] * &lt;/script&gt; */ var $where = requireDollar_where(); var $update = requireDollar_update(); function obop() { if (!(this instanceof obop)) return new obop(); } obop.where = obop.prototype.where = requireWhere().where; obop.view = obop.prototype.view = requireView().view; obop.order = obop.prototype.order = requireOrder().order; obop.update = obop.prototype.update = requireUpdate().update; obop.system = obop.prototype.system = require$$6; obop.$where = obop.prototype.$where = new $where(); obop.$update = obop.prototype.$update = new $update(); obop_1 = obop; return obop_1; } var obopExports = requireObop(); var index = /*@__PURE__*/getDefaultExportFromCjs(obopExports); export { index as default };