UNPKG

iridium

Version:

A custom lightweight ORM for MongoDB designed for power-users

187 lines (185 loc) 7.87 kB
var _ = require('lodash'); var MongoDB = require('mongodb'); var Omnom = (function () { function Omnom(options) { if (options === void 0) { options = {}; } this.options = options; this._changes = {}; } Object.defineProperty(Omnom.prototype, "changes", { get: function () { return this._changes; }, enumerable: true, configurable: true }); Omnom.prototype.diff = function (original, modified) { this.onObject(original, modified); return this; }; Omnom.diff = function (original, modified, options) { return new Omnom(options).diff(original, modified).changes; }; Omnom.prototype.onObject = function (original, modified, changePath) { if (original === undefined || original === null) return (original !== modified) && this.set(changePath, modified); if (typeof original == 'number' && typeof modified == 'number' && original !== modified) { if (this.options.atomicNumbers) return this.inc(changePath, modified - original); return this.set(changePath, modified); } if (Array.isArray(original) && Array.isArray(modified)) return this.onArray(original, modified, changePath); if (original instanceof MongoDB.ObjectID && modified instanceof MongoDB.ObjectID) return !original.equals(modified) && this.set(changePath, modified); if (!_.isPlainObject(original) || !_.isPlainObject(modified)) return !_.isEqual(original, modified) && this.set(changePath, modified); _.each(modified, function (value, key) { // Handle array diffs in their own special way if (Array.isArray(value) && Array.isArray(original[key])) this.onArray(original[key], value, this.resolve(changePath, key)); else this.onObject(original[key], value, this.resolve(changePath, key)); }, this); // Unset removed properties _.each(original, function (value, key) { if (modified[key] === undefined) return this.unset(this.resolve(changePath, key)); }, this); }; Omnom.prototype.onArray = function (original, modified, changePath) { var _this = this; var i, j; // Check if we can get from original => modified using just pulls if (original.length > modified.length) { var pulls = []; for (i = 0, j = 0; i < original.length && j < modified.length; i++) { if (this.almostEqual(original[i], modified[j])) j++; else pulls.push(original[i]); } for (; i < original.length; i++) pulls.push(original[i]); if (j === modified.length) { if (pulls.length === 1) return this.pull(changePath, pulls[0]); // We can complete using just pulls return pulls.forEach(function (pull) { return _this.pull(changePath, pull); }); } else return this.set(changePath, modified); } // Check if we can get from original => modified using just pushes if (original.length < modified.length) { var canPush = true; for (i = 0; i < original.length; i++) if (this.almostEqual(original[i], modified[i]) < 1) { canPush = false; break; } if (canPush) { for (i = original.length; i < modified.length; i++) this.push(changePath, modified[i]); return; } } // Otherwise, we need to use $set to generate the new array // Check how many manipulations would need to be performed, if it's more than half the array size // then rather re-create the array var sets = []; var partials = []; for (i = 0; i < modified.length; i++) { var equality = this.almostEqual(original[i], modified[i]); if (equality === 0) sets.push(i); else if (equality < 1) partials.push(i); } if (sets.length > modified.length / 2) return this.set(changePath, modified); for (i = 0; i < sets.length; i++) this.set(this.resolve(changePath, sets[i].toString()), modified[sets[i]]); for (i = 0; i < partials.length; i++) this.onObject(original[partials[i]], modified[partials[i]], this.resolve(changePath, partials[i].toString())); }; Omnom.prototype.set = function (path, value) { if (!this.changes.$set) this.changes.$set = {}; this.changes.$set[path] = value; }; Omnom.prototype.unset = function (path) { if (!this.changes.$unset) this.changes.$unset = {}; this.changes.$unset[path] = 1; }; Omnom.prototype.inc = function (path, value) { if (!this.changes.$inc) this.changes.$inc = {}; this.changes.$inc[path] = value; }; Omnom.prototype.push = function (path, value) { if (!this.changes.$push) this.changes.$push = {}; if (this.changes.$push[path]) { if (this.changes.$push[path].$each) this.changes.$push[path].$each.push(value); else this.changes.$push[path] = { $each: [this.changes.$push[path], value] }; } else this.changes.$push[path] = value; }; Omnom.prototype.pull = function (path, value) { if (!this.changes.$pull) this.changes.$pull = {}; if (this.changes.$pullAll && this.changes.$pullAll[path]) { return this.changes.$pullAll[path].push(value); } if (this.changes.$pull[path]) { this.pullAll(path, [this.changes.$pull[path], value]); delete this.changes.$pull[path]; if (_.keys(this.changes.$pull).length === 0) delete this.changes.$pull; return; } this.changes.$pull[path] = value; }; Omnom.prototype.pullAll = function (path, values) { if (!this.changes.$pullAll) this.changes.$pullAll = {}; this.changes.$pullAll[path] = values; }; Omnom.prototype.resolve = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i - 0] = arguments[_i]; } var validArguments = []; args.forEach(function (arg) { if (arg) validArguments.push(arg); }); return validArguments.join('.'); }; Omnom.prototype.almostEqual = function (o1, o2) { if (!_.isPlainObject(o1) || !_.isPlainObject(o2)) return o1 == o2 ? 1 : 0; var o1i, o1k = Object.keys(o1); var o2k = Object.keys(o2); var commonKeys = []; for (o1i = 0; o1i < o1k.length; o1i++) if (~o2k.indexOf(o1k[o1i])) commonKeys.push(o1k[o1i]); var totalKeys = o1k.length + o2k.length - commonKeys.length; var keysDifference = totalKeys - commonKeys.length; var requiredChanges = 0; for (var i = 0; i < commonKeys.length; i++) if (this.almostEqual(o1[commonKeys[i]], o2[commonKeys[i]]) < 1) requiredChanges++; return 1 - (keysDifference / totalKeys) - (requiredChanges / commonKeys.length); }; return Omnom; })(); exports.Omnom = Omnom; //# sourceMappingURL=Omnom.js.map