forked from nassor/mongoose-auto-increment
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
221 lines (207 loc) · 7.69 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// 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 = connection.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.
// Bug fixed:
// 1. Unhandled rejection MongoError: The field 'required' is not valid for an index specification.
// 2. Unhandled rejection MongoError: The field 'index' is not valid for an index specification.
// remove the "required" and "index" fields from options
counterSchema.index({
field: 1,
model: 1
}, {
unique: true
});
// Create model using new schema.
IdentityCounter = connection.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-ai 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();
});
};