| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // Module Scope
- var mongoose = require('mongoose'),
- extend = require('extend'),
- counterSchema,
- IdentityCounter;
- // Initialize plugin by creating counter collection in database.
- exports.initialize = function (connection) {
- try {
- IdentityCounter = mongoose.model('IdentityCounter');
- } catch (ex) {
- if (ex.name === 'MissingSchemaError') {
- // Create new counter schema.
- counterSchema = new mongoose.Schema({
- model: { type: String, require: true },
- field: { type: String, require: true },
- count: { type: Number, default: 0 }
- });
- // Create a unique index using the "field" and "model" fields.
- counterSchema.index({ field: 1, model: 1 }, { unique: true, required: true, index: -1 });
- // Create model using new schema.
- IdentityCounter = mongoose.model('IdentityCounter', counterSchema);
- }
- else
- throw ex;
- }
- };
- // The function to use when invoking the plugin on a custom schema.
- exports.plugin = function (schema, options) {
- // If we don't have reference to the counterSchema or the IdentityCounter model then the plugin was most likely not
- // initialized properly so throw an error.
- if (!counterSchema || !IdentityCounter) throw new Error("mongoose-auto-increment has not been initialized");
- // Default settings and plugin scope variables.
- var settings = {
- model: null, // The model to configure the plugin for.
- field: '_id', // The field the plugin should track.
- startAt: 0, // The number the count should start at.
- incrementBy: 1, // The number by which to increment the count each time.
- unique: true // Should we create a unique index for the field
- },
- fields = {}, // A hash of fields to add properties to in Mongoose.
- ready = false; // True if the counter collection has been updated and the document is ready to be saved.
- 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':
- extend(settings, options);
- break;
- }
- if (settings.model == null)
- throw new Error("model must be set");
- // Add properties for field in schema.
- fields[settings.field] = {
- type: Number,
- require: true
- };
- if (settings.field !== '_id')
- fields[settings.field].unique = settings.unique
- schema.add(fields);
- // Find the counter for this model and the relevant field.
- IdentityCounter.findOne(
- { model: settings.model, field: settings.field },
- function (err, counter) {
- if (!counter) {
- // If no counter exists then create one and save it.
- counter = new IdentityCounter({ model: settings.model, field: settings.field, count: settings.startAt - settings.incrementBy });
- counter.save(function () {
- ready = true;
- });
- }
- else {
- ready = true;
- }
- }
- );
- // Declare a function to get the next counter for the model/schema.
- var nextCount = function (callback) {
- IdentityCounter.findOne({
- model: settings.model,
- field: settings.field
- }, function (err, counter) {
- if (err) return callback(err);
- callback(null, counter === null ? settings.startAt : counter.count + settings.incrementBy);
- });
- };
- // Add nextCount as both a method on documents and a static on the schema for convenience.
- schema.method('nextCount', nextCount);
- schema.static('nextCount', nextCount);
- // Declare a function to reset counter at the start value - increment value.
- var resetCount = function (callback) {
- IdentityCounter.findOneAndUpdate(
- { model: settings.model, field: settings.field },
- { count: settings.startAt - settings.incrementBy },
- { new: true }, // new: true specifies that the callback should get the updated counter.
- function (err) {
- if (err) return callback(err);
- callback(null, settings.startAt);
- }
- );
- };
- // Add resetCount as both a method on documents and a static on the schema for convenience.
- schema.method('resetCount', resetCount);
- schema.static('resetCount', resetCount);
- // Every time documents in this schema are saved, run this logic.
- schema.pre('save', function (next) {
- // Get reference to the document being saved.
- var doc = this;
- // Only do this if it is a new document (see http://mongoosejs.com/docs/api.html#document_Document-isNew)
- if (doc.isNew) {
- // Declare self-invoking save function.
- (function save() {
- // If ready, run increment logic.
- // Note: ready is true when an existing counter collection is found or after it is created for the
- // first time.
- if (ready) {
- // check that a number has already been provided, and update the counter to that number if it is
- // greater than the current count
- if (typeof doc[settings.field] === 'number') {
- IdentityCounter.findOneAndUpdate(
- // IdentityCounter documents are identified by the model and field that the plugin was invoked for.
- // Check also that count is less than field value.
- { model: settings.model, field: settings.field, count: { $lt: doc[settings.field] } },
- // Change the count of the value found to the new field value.
- { count: doc[settings.field] },
- function (err) {
- if (err) return next(err);
- // Continue with default document save functionality.
- next();
- }
- );
- } else {
- // Find the counter collection entry for this model and field and update it.
- IdentityCounter.findOneAndUpdate(
- // IdentityCounter documents are identified by the model and field that the plugin was invoked for.
- { model: settings.model, field: settings.field },
- // 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 },
- // Receive the updated counter.
- function (err, updatedIdentityCounter) {
- if (err) return next(err);
- // If there are no errors then go ahead and set the document's field to the current count.
- doc[settings.field] = updatedIdentityCounter.count;
- // Continue with default document save functionality.
- next();
- }
- );
- }
- }
- // If not ready then set a 5 millisecond timer and try to save again. It will keep doing this until
- // the counter collection is ready.
- else
- setTimeout(save, 5);
- })();
- }
- // 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.
- else
- next();
- });
- };
|