| 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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 |
3×
3×
3×
3×
3×
3×
3×
3×
3×
115×
115×
5×
5×
115×
5×
110×
110×
110×
109×
109×
109×
108×
468×
108×
468×
26×
26×
1×
1×
26×
26×
1×
25×
25×
25×
25×
24×
23×
22×
22×
21×
28×
21×
21×
21×
21×
28×
28×
28×
21×
21×
21×
23×
23×
21×
21×
21×
20×
20×
25×
25×
20×
20×
25×
20×
20×
20×
25×
20×
2×
131×
131×
500×
2×
131×
2×
28×
28×
217×
21×
21×
28×
28×
28×
28×
28×
28×
28×
28×
28×
28×
| /**
* Aggregate Queries
*/
var async = require('async');
var _ = require('lodash');
var usageError = require('../utils/usageError');
var utils = require('../utils/helpers');
var normalize = require('../utils/normalize');
var callbacks = require('../utils/callbacksRunner');
var Deferred = require('./deferred');
var hasOwnProperty = utils.object.hasOwnProperty;
module.exports = {
/**
* Create an Array of records
*
* @param {Array} array of values to create
* @param {Function} callback
* @return Deferred object if no callback
*/
createEach: function(valuesList, cb, metaContainer) {
var self = this;
// Handle Deferred where it passes criteria first
if(_.isPlainObject(arguments[0]) && _.isArray(arguments[1])) {
valuesList = arguments[1];
cb = arguments[2];
}
// Return Deferred or pass to adapter
if (typeof cb !== 'function') {
return new Deferred(this, this.createEach, {}, valuesList);
}
// Validate Params
var usage = utils.capitalize(this.identity) + '.createEach(valuesList, callback)';
Iif (!valuesList) return usageError('No valuesList specified!', usage, cb);
if (!Array.isArray(valuesList)) return usageError('Invalid valuesList specified (should be an array!)', usage, cb);
Iif (typeof cb !== 'function') return usageError('Invalid callback specified!', usage, cb);
var errStr = _validateValues(_.cloneDeep(valuesList));
if (errStr) return usageError(errStr, usage, cb);
// Handle undefined values
var filteredValues = _.filter(valuesList, function(value) {
return value !== undefined;
});
// Create will take care of cloning values so original isn't mutated
async.map(filteredValues, function(data, next) {
self.create(data, next, metaContainer);
}, cb);
},
/**
* Iterate through a list of objects, trying to find each one
* For any that don't exist, create them
*
* @param {Object} criteria
* @param {Array} valuesList
* @param {Function} callback
* @return Deferred object if no callback
*/
findOrCreateEach: function(criteria, valuesList, cb, metaContainer) {
var self = this;
if (typeof valuesList === 'function') {
cb = valuesList;
valuesList = null;
}
// Normalize criteria
criteria = normalize.criteria(criteria);
// Return Deferred or pass to adapter
if (typeof cb !== 'function') {
return new Deferred(this, this.findOrCreateEach, criteria, valuesList);
}
// Validate Params
var usage = utils.capitalize(this.identity) + '.findOrCreateEach(criteria, valuesList, callback)';
Iif (typeof cb !== 'function') return usageError('Invalid callback specified!', usage, cb);
Iif (!criteria) return usageError('No criteria specified!', usage, cb);
if (!Array.isArray(criteria)) return usageError('No criteria specified!', usage, cb);
if (!valuesList) return usageError('No valuesList specified!', usage, cb);
if (!Array.isArray(valuesList)) return usageError('Invalid valuesList specified (should be an array!)', usage, cb);
var errStr = _validateValues(valuesList);
if (errStr) return usageError(errStr, usage, cb);
// Validate each record in the array and if all are valid
// pass the array to the adapter's findOrCreateEach method
var validateItem = function(item, next) {
_validate.call(self, item, next);
};
async.each(valuesList, validateItem, function(err) {
Iif (err) return cb(err);
// Transform Values
var transformedValues = [];
valuesList.forEach(function(value) {
// Transform values
value = self._transformer.serialize(value);
// Clean attributes
value = self._schema.cleanValues(value);
transformedValues.push(value);
});
// Set values array to the transformed array
valuesList = transformedValues;
// Transform Search Criteria
var transformedCriteria = [];
criteria.forEach(function(value) {
value = self._transformer.serialize(value);
transformedCriteria.push(value);
});
// Set criteria array to the transformed array
criteria = transformedCriteria;
// Pass criteria and attributes to adapter definition
self.adapter.findOrCreateEach(criteria, valuesList, function(err, values) {
if (err) return cb(err);
// Unserialize Values
var unserializedValues = [];
values.forEach(function(value) {
value = self._transformer.unserialize(value);
unserializedValues.push(value);
});
// Set values array to the transformed array
values = unserializedValues;
// Run AfterCreate Callbacks
async.each(values, function(item, next) {
callbacks.afterCreate(self, item, next);
}, function(err) {
Iif (err) return cb(err);
var models = [];
// Make each result an instance of model
values.forEach(function(value) {
models.push(new self._model(value));
});
cb(null, models);
});
}, metaContainer);
});
}
};
/**
* Validate valuesList
*
* @param {Array} valuesList
* @return {String}
* @api private
*/
function _validateValues(valuesList) {
var err;
for (var i = 0; i < valuesList.length; i++) {
if (valuesList[i] !== Object(valuesList[i])) {
err = 'Invalid valuesList specified (should be an array of valid values objects!)';
}
}
return err;
}
/**
* Validate values and add in default values
*
* @param {Object} record
* @param {Function} cb
* @api private
*/
function _validate(record, cb) {
var self = this;
// Set Default Values if available
for (var key in self.attributes) {
if (!record[key] && record[key] !== false && hasOwnProperty(self.attributes[key], 'defaultsTo')) {
var defaultsTo = self.attributes[key].defaultsTo;
record[key] = typeof defaultsTo === 'function' ? defaultsTo.call(record) : _.clone(defaultsTo);
}
}
// Cast values to proper types (handle numbers as strings)
record = self._cast.run(record);
async.series([
// Run Validation with Validation LifeCycle Callbacks
function(next) {
callbacks.validate(self, record, true, next);
},
// Before Create Lifecycle Callback
function(next) {
callbacks.beforeCreate(self, record, next);
}
], function(err) {
Iif (err) return cb(err);
// Automatically add updatedAt and createdAt (if enabled)
Eif (self.autoCreatedAt) {
record[self.autoCreatedAt] = new Date();
}
Eif (self.autoUpdatedAt) {
record[self.autoUpdatedAt] = new Date();
}
cb();
});
}
|