UNPKG

mongoose-plugin-autoinc

Version:

This plugin allows you to auto-increment any field on any mongoose schema that you wish (forked mongoose-auto-increment in 2018).

326 lines (270 loc) 10.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; let createCounterIfNotExist = (() => { var _ref = _asyncToGenerator(function* (IC, settings, doc) { const groupingField = doc.get(settings.groupingField) || ''; let existedCounter = yield IC.findOne({ model: settings.model, field: settings.field, groupingField }).exec(); try { if (!existedCounter) { // Check old record format without `groupingField`, // convert old record to the new format existedCounter = yield IC.findOne({ model: settings.model, field: settings.field, groupingField: { $exists: false } }); if (existedCounter) { existedCounter.groupingField = ''; yield existedCounter.save(); } } if (!existedCounter) { // If no counter exists then create one and save it. existedCounter = new IC({ model: settings.model, field: settings.field, groupingField, count: settings.startAt - settings.incrementBy }); yield existedCounter.save(); } } catch (e) { if (isMongoDuplicateError(e)) { return; } throw e; // other unhandled errors } }); return function createCounterIfNotExist(_x, _x2, _x3) { return _ref.apply(this, arguments); }; })(); let preSave = (() => { var _ref2 = _asyncToGenerator(function* (IC, settings, doc, next, attempts = 0) { try { // it is a first run if (!attempts) { yield createCounterIfNotExist(IC, settings, doc); } if (typeof doc.get(settings.field) === 'number') { // check that a number has already been provided, and update the counter // to that number if it is greater than the current count yield IC.findOneAndUpdate({ model: settings.model, field: settings.field, groupingField: doc.get(settings.groupingField) || '', count: { $lt: doc.get(settings.field) } }, { count: doc.get(settings.field) }).exec(); } else { // Find the counter collection entry for this model and field and update it. // Should be done via atomic mongodb operation, cause parallel processes may change value const updatedCounter = yield IC.findOneAndUpdate({ model: settings.model, field: settings.field, groupingField: doc.get(settings.groupingField) || '' }, { // Increment the count by `incrementBy`. $inc: { count: settings.incrementBy } }, { // new:true specifies that the callback should get the counter AFTER it is updated (incremented). new: true }).exec(); if (!updatedCounter) { throw new Error(`MongooseAutoInc cannot update counter for ${settings.model}`); } let count = updatedCounter.count; // if an output filter was provided, apply it. if (typeof settings.outputFilter === 'function') { count = settings.outputFilter(count); } // If there are no errors then go ahead and set the document's field to the current count. doc.set(settings.field, count); // $FlowFixMe doc.__maiRanOnce = true; // eslint-disable-line } next(); } catch (err) { if (isMongoDuplicateError(err) && attempts * 1 < 10) { setTimeout(function () { return preSave(IC, settings, doc, next, attempts + 1); }, 5); } else { next(err); } } }); return function preSave(_x4, _x5, _x6, _x7) { return _ref2.apply(this, arguments); }; })(); // Declare a function to get the next counter for the model/schema. let nextCount = (() => { var _ref3 = _asyncToGenerator(function* (IC, settings, groupingFieldValue) { const counter = yield IC.findOne({ model: settings.model, field: settings.field, groupingField: groupingFieldValue || '' }).exec(); return !counter ? settings.startAt : counter.count + settings.incrementBy; }); return function nextCount(_x8, _x9, _x10) { return _ref3.apply(this, arguments); }; })(); // Declare a function to reset counter at the start value - increment value. let resetCount = (() => { var _ref4 = _asyncToGenerator(function* (IC, settings, groupingFieldValue) { yield IC.findOneAndUpdate({ model: settings.model, field: settings.field, groupingField: groupingFieldValue || '' }, { count: settings.startAt - settings.incrementBy }, { new: true // new: true specifies that the callback should get the updated counter. }).exec(); return settings.startAt; }); return function resetCount(_x11, _x12, _x13) { return _ref4.apply(this, arguments); }; })(); // The function to use when invoking the plugin on a custom schema. exports.initialize = initialize; exports.autoIncrement = autoIncrement; exports.plugin = plugin; var _mongoose = require('mongoose'); var _mongoose2 = _interopRequireDefault(_mongoose); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } const counterSchema = new _mongoose2.default.Schema({ model: { type: String, required: true }, field: { type: String, required: true }, groupingField: { type: String, default: '' }, count: { type: Number, default: 0 } }); counterSchema.index({ field: 1, groupingField: 1, model: 1 }, { unique: true }); function initialize() { console.log(`MongooseAutoIncrement.initialize() method is deprecated. ` + `Just remove this method, it not required anymore.`); } function isMongoDuplicateError(e) { return e.code * 1 === 11000; } // Initialize plugin by creating counter collection in database. function initIdentityCounter(connection) { let identityCounter; try { identityCounter = connection.model('IdentityCounter'); } catch (ex) { if (ex.name === 'MissingSchemaError') { // Create model using new schema. identityCounter = connection.model('IdentityCounter', counterSchema); } else { throw ex; } } return identityCounter; } function autoIncrement(schema, options) { const compoundIndex = {}; let _IC_; function getIC(connection) { if (!_IC_) { _IC_ = initIdentityCounter(connection); } return _IC_; } // Default settings and plugin scope variables. let settings = { migrate: false, model: undefined, field: '_id', groupingField: '', startAt: 0, incrementBy: 1, unique: true, outputFilter: undefined }; switch (typeof options) { // If string, the user chose to pass in just the model name. case 'string': settings.model = options; break; // If object, the user passed in a hash of options. case 'object': settings = _extends({}, settings, options); break; default: } if (typeof settings.model !== 'string') { throw new Error('model must be set'); } if (settings.field === '_id' && settings.groupingField.length) { throw new Error('Cannot use a grouping field with _id, choose a different field name.'); } if (!schema.path(settings.field) || settings.field === '_id') { schema.path(settings.field, Number); } if (settings.groupingField.length) { // If a groupingField is specified, create a compound unique index. compoundIndex[settings.field] = 1; compoundIndex[settings.groupingField] = 1; schema.index(compoundIndex, { unique: settings.unique }); } else if (settings.field !== '_id') { // Otherwise, add the unique index directly to the custom field. schema.path(settings.field).index({ unique: settings.unique }); } // Add nextCount as both a method on documents and a static on the schema for convenience. schema.method('nextCount', function (groupingFieldValue) { const doc = this; const IC = getIC(doc.collection.conn); return nextCount(IC, settings, groupingFieldValue); }); // $FlowFixMe schema.static('nextCount', function (groupingFieldValue) { const model = this; const IC = getIC(model.collection.conn); return nextCount(IC, settings, groupingFieldValue); }); // Add resetCount as both a method on documents and a static on the schema for convenience. schema.method('resetCount', function (groupingFieldValue) { const doc = this; const IC = getIC(doc.collection.conn); return resetCount(IC, settings, groupingFieldValue); }); // $FlowFixMe schema.static('resetCount', function (groupingFieldValue) { const model = this; const IC = getIC(model.collection.conn); return resetCount(IC, settings, groupingFieldValue); }); // Every time documents in this schema are saved, run this logic. schema.pre('validate', function (next) { // Get reference to the document being saved. const doc = this; // $FlowFixMe const alreadyGetId = doc.__maiRanOnce === true; // Only do this if it is a new document & the field doesn't have // a value set (see http://mongoosejs.com/docs/api.html#document_Document-isNew) if (doc.isNew && !alreadyGetId || settings.migrate) { const IC = getIC(doc.collection.conn); preSave(IC, settings, doc, next); } else { // If the document does not have the field we're interested in or that field isn't a number AND the user did // not specify that we should increment on updates, then just continue the save without any increment logic. next(); } }); } function plugin(schema, options) { return autoIncrement(schema, options); } exports.default = autoIncrement;