mock平台

mongoose-auto-increment.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // Module Scope
  2. var mongoose = require('mongoose'),
  3. extend = require('extend'),
  4. counterSchema,
  5. IdentityCounter;
  6. // Initialize plugin by creating counter collection in database.
  7. exports.initialize = function (connection) {
  8. try {
  9. IdentityCounter = mongoose.model('IdentityCounter');
  10. } catch (ex) {
  11. if (ex.name === 'MissingSchemaError') {
  12. // Create new counter schema.
  13. counterSchema = new mongoose.Schema({
  14. model: { type: String, require: true },
  15. field: { type: String, require: true },
  16. count: { type: Number, default: 0 }
  17. });
  18. // Create a unique index using the "field" and "model" fields.
  19. counterSchema.index({ field: 1, model: 1 }, { unique: true, required: true, index: -1 });
  20. // Create model using new schema.
  21. IdentityCounter = mongoose.model('IdentityCounter', counterSchema);
  22. }
  23. else
  24. throw ex;
  25. }
  26. };
  27. // The function to use when invoking the plugin on a custom schema.
  28. exports.plugin = function (schema, options) {
  29. // If we don't have reference to the counterSchema or the IdentityCounter model then the plugin was most likely not
  30. // initialized properly so throw an error.
  31. if (!counterSchema || !IdentityCounter) throw new Error("mongoose-auto-increment has not been initialized");
  32. // Default settings and plugin scope variables.
  33. var settings = {
  34. model: null, // The model to configure the plugin for.
  35. field: '_id', // The field the plugin should track.
  36. startAt: 0, // The number the count should start at.
  37. incrementBy: 1, // The number by which to increment the count each time.
  38. unique: true // Should we create a unique index for the field
  39. },
  40. fields = {}, // A hash of fields to add properties to in Mongoose.
  41. ready = false; // True if the counter collection has been updated and the document is ready to be saved.
  42. switch (typeof(options)) {
  43. // If string, the user chose to pass in just the model name.
  44. case 'string':
  45. settings.model = options;
  46. break;
  47. // If object, the user passed in a hash of options.
  48. case 'object':
  49. extend(settings, options);
  50. break;
  51. }
  52. if (settings.model == null)
  53. throw new Error("model must be set");
  54. // Add properties for field in schema.
  55. fields[settings.field] = {
  56. type: Number,
  57. require: true
  58. };
  59. if (settings.field !== '_id')
  60. fields[settings.field].unique = settings.unique
  61. schema.add(fields);
  62. // Find the counter for this model and the relevant field.
  63. IdentityCounter.findOne(
  64. { model: settings.model, field: settings.field },
  65. function (err, counter) {
  66. if (!counter) {
  67. // If no counter exists then create one and save it.
  68. counter = new IdentityCounter({ model: settings.model, field: settings.field, count: settings.startAt - settings.incrementBy });
  69. counter.save(function () {
  70. ready = true;
  71. });
  72. }
  73. else {
  74. ready = true;
  75. }
  76. }
  77. );
  78. // Declare a function to get the next counter for the model/schema.
  79. var nextCount = function (callback) {
  80. IdentityCounter.findOne({
  81. model: settings.model,
  82. field: settings.field
  83. }, function (err, counter) {
  84. if (err) return callback(err);
  85. callback(null, counter === null ? settings.startAt : counter.count + settings.incrementBy);
  86. });
  87. };
  88. // Add nextCount as both a method on documents and a static on the schema for convenience.
  89. schema.method('nextCount', nextCount);
  90. schema.static('nextCount', nextCount);
  91. // Declare a function to reset counter at the start value - increment value.
  92. var resetCount = function (callback) {
  93. IdentityCounter.findOneAndUpdate(
  94. { model: settings.model, field: settings.field },
  95. { count: settings.startAt - settings.incrementBy },
  96. { new: true }, // new: true specifies that the callback should get the updated counter.
  97. function (err) {
  98. if (err) return callback(err);
  99. callback(null, settings.startAt);
  100. }
  101. );
  102. };
  103. // Add resetCount as both a method on documents and a static on the schema for convenience.
  104. schema.method('resetCount', resetCount);
  105. schema.static('resetCount', resetCount);
  106. // Every time documents in this schema are saved, run this logic.
  107. schema.pre('save', function (next) {
  108. // Get reference to the document being saved.
  109. var doc = this;
  110. // Only do this if it is a new document (see http://mongoosejs.com/docs/api.html#document_Document-isNew)
  111. if (doc.isNew) {
  112. // Declare self-invoking save function.
  113. (function save() {
  114. // If ready, run increment logic.
  115. // Note: ready is true when an existing counter collection is found or after it is created for the
  116. // first time.
  117. if (ready) {
  118. // check that a number has already been provided, and update the counter to that number if it is
  119. // greater than the current count
  120. if (typeof doc[settings.field] === 'number') {
  121. IdentityCounter.findOneAndUpdate(
  122. // IdentityCounter documents are identified by the model and field that the plugin was invoked for.
  123. // Check also that count is less than field value.
  124. { model: settings.model, field: settings.field, count: { $lt: doc[settings.field] } },
  125. // Change the count of the value found to the new field value.
  126. { count: doc[settings.field] },
  127. function (err) {
  128. if (err) return next(err);
  129. // Continue with default document save functionality.
  130. next();
  131. }
  132. );
  133. } else {
  134. // Find the counter collection entry for this model and field and update it.
  135. IdentityCounter.findOneAndUpdate(
  136. // IdentityCounter documents are identified by the model and field that the plugin was invoked for.
  137. { model: settings.model, field: settings.field },
  138. // Increment the count by `incrementBy`.
  139. { $inc: { count: settings.incrementBy } },
  140. // new:true specifies that the callback should get the counter AFTER it is updated (incremented).
  141. { new: true },
  142. // Receive the updated counter.
  143. function (err, updatedIdentityCounter) {
  144. if (err) return next(err);
  145. // If there are no errors then go ahead and set the document's field to the current count.
  146. doc[settings.field] = updatedIdentityCounter.count;
  147. // Continue with default document save functionality.
  148. next();
  149. }
  150. );
  151. }
  152. }
  153. // If not ready then set a 5 millisecond timer and try to save again. It will keep doing this until
  154. // the counter collection is ready.
  155. else
  156. setTimeout(save, 5);
  157. })();
  158. }
  159. // If the document does not have the field we're interested in or that field isn't a number AND the user did
  160. // not specify that we should increment on updates, then just continue the save without any increment logic.
  161. else
  162. next();
  163. });
  164. };