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
JavaScript
;
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;