@electerm/nedb 1.10.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -717
- package/lib/async.js +237 -0
- package/lib/cursor.js +1 -1
- package/lib/datastore.js +10 -8
- package/lib/executor.js +1 -1
- package/lib/indexes.js +12 -10
- package/lib/model.js +37 -34
- package/lib/persistence.js +2 -2
- package/lib/storage.js +1 -1
- package/lib/underscore.js +177 -0
- package/lib/util.js +44 -0
- package/package.json +25 -20
package/lib/async.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom async utilities to replace the async library
|
|
3
|
+
* Uses modern async/await internally while maintaining callback compatibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Promisify a callback-based function
|
|
8
|
+
*/
|
|
9
|
+
function promisify(fn) {
|
|
10
|
+
return function(...args) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
fn(...args, (err, ...results) => {
|
|
13
|
+
if (err) reject(err);
|
|
14
|
+
else resolve(results.length <= 1 ? results[0] : results);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Execute functions in series (waterfall pattern)
|
|
22
|
+
* Each function receives the result of the previous function as its parameters (except the last which is always the callback)
|
|
23
|
+
*/
|
|
24
|
+
function waterfall(tasks, finalCallback) {
|
|
25
|
+
finalCallback = finalCallback || function() {};
|
|
26
|
+
|
|
27
|
+
if (!Array.isArray(tasks) || tasks.length === 0) {
|
|
28
|
+
return finalCallback(null);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let taskIndex = 0;
|
|
32
|
+
|
|
33
|
+
function runNextTask(err, ...results) {
|
|
34
|
+
if (err) {
|
|
35
|
+
return finalCallback(err);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (taskIndex >= tasks.length) {
|
|
39
|
+
return finalCallback(null, ...results);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const task = tasks[taskIndex++];
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (typeof task === 'function') {
|
|
46
|
+
// Regular function
|
|
47
|
+
if (taskIndex === 1) {
|
|
48
|
+
// First task gets only the callback
|
|
49
|
+
task(runNextTask);
|
|
50
|
+
} else {
|
|
51
|
+
// Subsequent tasks get results from previous task, then callback
|
|
52
|
+
task(...results, runNextTask);
|
|
53
|
+
}
|
|
54
|
+
} else if (task && task.__async_apply) {
|
|
55
|
+
// Handle async.apply case
|
|
56
|
+
const args = [...task.args];
|
|
57
|
+
if (taskIndex > 1) {
|
|
58
|
+
// If not the first task, add results from previous task
|
|
59
|
+
args.push(...results);
|
|
60
|
+
}
|
|
61
|
+
args.push(runNextTask);
|
|
62
|
+
task.fn.apply(null, args);
|
|
63
|
+
} else {
|
|
64
|
+
return finalCallback(new Error('Invalid task in waterfall'));
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return finalCallback(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
runNextTask();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Apply partial application to a function (like async.apply)
|
|
76
|
+
*/
|
|
77
|
+
function apply(fn, ...args) {
|
|
78
|
+
return {
|
|
79
|
+
__async_apply: true,
|
|
80
|
+
fn: fn,
|
|
81
|
+
args: args
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Execute a function for each item in an array, in series
|
|
87
|
+
*/
|
|
88
|
+
function eachSeries(array, iterator, callback) {
|
|
89
|
+
callback = callback || function() {};
|
|
90
|
+
|
|
91
|
+
let index = 0;
|
|
92
|
+
|
|
93
|
+
function processNext(err) {
|
|
94
|
+
if (err) {
|
|
95
|
+
return callback(err);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (index >= array.length) {
|
|
99
|
+
return callback(null);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const item = array[index++];
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
iterator(item, processNext);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return callback(error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
processNext();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Execute a function for each item in an array, in parallel
|
|
116
|
+
*/
|
|
117
|
+
function each(array, iterator, callback) {
|
|
118
|
+
callback = callback || function() {};
|
|
119
|
+
|
|
120
|
+
if (array.length === 0) {
|
|
121
|
+
return callback(null);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let completed = 0;
|
|
125
|
+
let hasError = false;
|
|
126
|
+
|
|
127
|
+
array.forEach(function(item) {
|
|
128
|
+
iterator(item, function(err) {
|
|
129
|
+
if (hasError) return;
|
|
130
|
+
|
|
131
|
+
if (err) {
|
|
132
|
+
hasError = true;
|
|
133
|
+
return callback(err);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
completed++;
|
|
137
|
+
if (completed === array.length) {
|
|
138
|
+
callback(null);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Execute a function repeatedly while a condition is true
|
|
146
|
+
*/
|
|
147
|
+
function whilst(test, iterator, callback) {
|
|
148
|
+
callback = callback || function() {};
|
|
149
|
+
|
|
150
|
+
function iterate(err) {
|
|
151
|
+
if (err) {
|
|
152
|
+
return callback(err);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!test()) {
|
|
156
|
+
return callback(null);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
iterator(iterate);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return callback(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
iterate();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Task queue implementation
|
|
171
|
+
*/
|
|
172
|
+
function queue(worker, concurrency = 1) {
|
|
173
|
+
const tasks = [];
|
|
174
|
+
let running = 0;
|
|
175
|
+
let paused = false;
|
|
176
|
+
|
|
177
|
+
function processTasks() {
|
|
178
|
+
while (tasks.length > 0 && running < concurrency && !paused) {
|
|
179
|
+
const task = tasks.shift();
|
|
180
|
+
running++;
|
|
181
|
+
|
|
182
|
+
worker(task.data, function(err) {
|
|
183
|
+
running--;
|
|
184
|
+
task.callback(err);
|
|
185
|
+
|
|
186
|
+
// Process next tasks
|
|
187
|
+
if (tasks.length > 0) {
|
|
188
|
+
setImmediate(processTasks);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
push: function(data, callback) {
|
|
196
|
+
callback = callback || function() {};
|
|
197
|
+
tasks.push({ data, callback });
|
|
198
|
+
setImmediate(processTasks);
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
unshift: function(data, callback) {
|
|
202
|
+
callback = callback || function() {};
|
|
203
|
+
tasks.unshift({ data, callback });
|
|
204
|
+
setImmediate(processTasks);
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
pause: function() {
|
|
208
|
+
paused = true;
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
resume: function() {
|
|
212
|
+
paused = false;
|
|
213
|
+
setImmediate(processTasks);
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
length: function() {
|
|
217
|
+
return tasks.length;
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
running: function() {
|
|
221
|
+
return running;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
idle: function() {
|
|
225
|
+
return tasks.length === 0 && running === 0;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
module.exports = {
|
|
231
|
+
waterfall,
|
|
232
|
+
apply,
|
|
233
|
+
eachSeries,
|
|
234
|
+
each,
|
|
235
|
+
whilst,
|
|
236
|
+
queue
|
|
237
|
+
};
|
package/lib/cursor.js
CHANGED
package/lib/datastore.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
var customUtils = require('./customUtils')
|
|
2
2
|
, model = require('./model')
|
|
3
|
-
, async = require('async')
|
|
3
|
+
, async = require('./async')
|
|
4
4
|
, Executor = require('./executor')
|
|
5
5
|
, Index = require('./indexes')
|
|
6
|
-
, util = require('util')
|
|
7
|
-
, _ = require('
|
|
6
|
+
, util = require('./util')
|
|
7
|
+
, _ = require('./underscore')
|
|
8
8
|
, Persistence = require('./persistence')
|
|
9
9
|
, Cursor = require('./cursor')
|
|
10
10
|
;
|
|
11
11
|
|
|
12
|
+
var isDate = util.isDate;
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Create a new collection
|
|
@@ -269,7 +271,7 @@ Datastore.prototype.getCandidates = function (query, dontExpireStaleDocs, callba
|
|
|
269
271
|
// For a basic match
|
|
270
272
|
usableQueryKeys = [];
|
|
271
273
|
Object.keys(query).forEach(function (k) {
|
|
272
|
-
if (typeof query[k] === 'string' || typeof query[k] === 'number' || typeof query[k] === 'boolean' ||
|
|
274
|
+
if (typeof query[k] === 'string' || typeof query[k] === 'number' || typeof query[k] === 'boolean' || isDate(query[k]) || query[k] === null) {
|
|
273
275
|
usableQueryKeys.push(k);
|
|
274
276
|
}
|
|
275
277
|
});
|
|
@@ -314,7 +316,7 @@ Datastore.prototype.getCandidates = function (query, dontExpireStaleDocs, callba
|
|
|
314
316
|
docs.forEach(function (doc) {
|
|
315
317
|
var valid = true;
|
|
316
318
|
ttlIndexesFieldNames.forEach(function (i) {
|
|
317
|
-
if (doc[i] !== undefined &&
|
|
319
|
+
if (doc[i] !== undefined && isDate(doc[i]) && Date.now() > doc[i].getTime() + self.ttlIndexes[i] * 1000) {
|
|
318
320
|
valid = false;
|
|
319
321
|
}
|
|
320
322
|
});
|
|
@@ -351,7 +353,7 @@ Datastore.prototype._insert = function (newDoc, cb) {
|
|
|
351
353
|
return callback(e);
|
|
352
354
|
}
|
|
353
355
|
|
|
354
|
-
this.persistence.persistNewState(
|
|
356
|
+
this.persistence.persistNewState(Array.isArray(preparedDoc) ? preparedDoc : [preparedDoc], function (err) {
|
|
355
357
|
if (err) { return callback(err); }
|
|
356
358
|
return callback(null, model.deepCopy(preparedDoc));
|
|
357
359
|
});
|
|
@@ -377,7 +379,7 @@ Datastore.prototype.createNewId = function () {
|
|
|
377
379
|
Datastore.prototype.prepareDocumentForInsertion = function (newDoc) {
|
|
378
380
|
var preparedDoc, self = this;
|
|
379
381
|
|
|
380
|
-
if (
|
|
382
|
+
if (Array.isArray(newDoc)) {
|
|
381
383
|
preparedDoc = [];
|
|
382
384
|
newDoc.forEach(function (doc) { preparedDoc.push(self.prepareDocumentForInsertion(doc)); });
|
|
383
385
|
} else {
|
|
@@ -397,7 +399,7 @@ Datastore.prototype.prepareDocumentForInsertion = function (newDoc) {
|
|
|
397
399
|
* @api private
|
|
398
400
|
*/
|
|
399
401
|
Datastore.prototype._insertInCache = function (preparedDoc) {
|
|
400
|
-
if (
|
|
402
|
+
if (Array.isArray(preparedDoc)) {
|
|
401
403
|
this._insertMultipleDocsInCache(preparedDoc);
|
|
402
404
|
} else {
|
|
403
405
|
this.addToIndexes(preparedDoc);
|
package/lib/executor.js
CHANGED
package/lib/indexes.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
var BinarySearchTree = require('@yetzt/binary-search-tree').AVLTree
|
|
2
2
|
, model = require('./model')
|
|
3
|
-
, _ = require('
|
|
4
|
-
, util = require('util')
|
|
3
|
+
, _ = require('./underscore')
|
|
4
|
+
, util = require('./util')
|
|
5
5
|
;
|
|
6
6
|
|
|
7
|
+
var isDate = util.isDate;
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Two indexed pointers are equal iif they point to the same place
|
|
9
11
|
*/
|
|
@@ -19,7 +21,7 @@ function projectForUnique (elt) {
|
|
|
19
21
|
if (typeof elt === 'string') { return '$string' + elt; }
|
|
20
22
|
if (typeof elt === 'boolean') { return '$boolean' + elt; }
|
|
21
23
|
if (typeof elt === 'number') { return '$number' + elt; }
|
|
22
|
-
if (
|
|
24
|
+
if (isDate(elt)) { return '$date' + elt.getTime(); }
|
|
23
25
|
|
|
24
26
|
return elt; // Arrays and objects, will check for pointer equality
|
|
25
27
|
}
|
|
@@ -66,14 +68,14 @@ Index.prototype.insert = function (doc) {
|
|
|
66
68
|
, keys, i, failingI, error
|
|
67
69
|
;
|
|
68
70
|
|
|
69
|
-
if (
|
|
71
|
+
if (Array.isArray(doc)) { this.insertMultipleDocs(doc); return; }
|
|
70
72
|
|
|
71
73
|
key = model.getDotValue(doc, this.fieldName);
|
|
72
74
|
|
|
73
75
|
// We don't index documents that don't contain the field if the index is sparse
|
|
74
76
|
if (key === undefined && this.sparse) { return; }
|
|
75
77
|
|
|
76
|
-
if (!
|
|
78
|
+
if (!Array.isArray(key)) {
|
|
77
79
|
this.tree.insert(key, doc);
|
|
78
80
|
} else {
|
|
79
81
|
// If an insert fails due to a unique constraint, roll back all inserts before it
|
|
@@ -138,13 +140,13 @@ Index.prototype.insertMultipleDocs = function (docs) {
|
|
|
138
140
|
Index.prototype.remove = function (doc) {
|
|
139
141
|
var key, self = this;
|
|
140
142
|
|
|
141
|
-
if (
|
|
143
|
+
if (Array.isArray(doc)) { doc.forEach(function (d) { self.remove(d); }); return; }
|
|
142
144
|
|
|
143
145
|
key = model.getDotValue(doc, this.fieldName);
|
|
144
146
|
|
|
145
147
|
if (key === undefined && this.sparse) { return; }
|
|
146
148
|
|
|
147
|
-
if (!
|
|
149
|
+
if (!Array.isArray(key)) {
|
|
148
150
|
this.tree.delete(key, doc);
|
|
149
151
|
} else {
|
|
150
152
|
_.uniq(key, projectForUnique).forEach(function (_key) {
|
|
@@ -160,7 +162,7 @@ Index.prototype.remove = function (doc) {
|
|
|
160
162
|
* Naive implementation, still in O(log(n))
|
|
161
163
|
*/
|
|
162
164
|
Index.prototype.update = function (oldDoc, newDoc) {
|
|
163
|
-
if (
|
|
165
|
+
if (Array.isArray(oldDoc)) { this.updateMultipleDocs(oldDoc); return; }
|
|
164
166
|
|
|
165
167
|
this.remove(oldDoc);
|
|
166
168
|
|
|
@@ -219,7 +221,7 @@ Index.prototype.updateMultipleDocs = function (pairs) {
|
|
|
219
221
|
Index.prototype.revertUpdate = function (oldDoc, newDoc) {
|
|
220
222
|
var revert = [];
|
|
221
223
|
|
|
222
|
-
if (!
|
|
224
|
+
if (!Array.isArray(oldDoc)) {
|
|
223
225
|
this.update(newDoc, oldDoc);
|
|
224
226
|
} else {
|
|
225
227
|
oldDoc.forEach(function (pair) {
|
|
@@ -238,7 +240,7 @@ Index.prototype.revertUpdate = function (oldDoc, newDoc) {
|
|
|
238
240
|
Index.prototype.getMatching = function (value) {
|
|
239
241
|
var self = this;
|
|
240
242
|
|
|
241
|
-
if (!
|
|
243
|
+
if (!Array.isArray(value)) {
|
|
242
244
|
return self.tree.search(value);
|
|
243
245
|
} else {
|
|
244
246
|
var _res = {}, res = [];
|
package/lib/model.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Querying, update
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
var util = require('util')
|
|
9
|
-
, _ = require('
|
|
8
|
+
var util = require('./util')
|
|
9
|
+
, _ = require('./underscore')
|
|
10
10
|
, modifierFunctions = {}
|
|
11
11
|
, lastStepModifierFunctions = {}
|
|
12
12
|
, comparisonFunctions = {}
|
|
@@ -14,6 +14,9 @@ var util = require('util')
|
|
|
14
14
|
, arrayComparisonFunctions = {}
|
|
15
15
|
;
|
|
16
16
|
|
|
17
|
+
var isDate = util.isDate;
|
|
18
|
+
var isRegExp = util.isRegExp;
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Check a key, throw an error if the key is non valid
|
|
@@ -43,7 +46,7 @@ function checkKey (k, v) {
|
|
|
43
46
|
* Works by applying the above checkKey function to all fields recursively
|
|
44
47
|
*/
|
|
45
48
|
function checkObject (obj) {
|
|
46
|
-
if (
|
|
49
|
+
if (Array.isArray(obj)) {
|
|
47
50
|
obj.forEach(function (o) {
|
|
48
51
|
checkObject(o);
|
|
49
52
|
});
|
|
@@ -113,11 +116,11 @@ function deepCopy (obj, strictKeys) {
|
|
|
113
116
|
typeof obj === 'number' ||
|
|
114
117
|
typeof obj === 'string' ||
|
|
115
118
|
obj === null ||
|
|
116
|
-
(
|
|
119
|
+
(isDate(obj)) ) {
|
|
117
120
|
return obj;
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
if (
|
|
123
|
+
if (Array.isArray(obj)) {
|
|
121
124
|
res = [];
|
|
122
125
|
obj.forEach(function (o) { res.push(deepCopy(o, strictKeys)); });
|
|
123
126
|
return res;
|
|
@@ -146,8 +149,8 @@ function isPrimitiveType (obj) {
|
|
|
146
149
|
typeof obj === 'number' ||
|
|
147
150
|
typeof obj === 'string' ||
|
|
148
151
|
obj === null ||
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
isDate(obj) ||
|
|
153
|
+
Array.isArray(obj));
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
|
|
@@ -211,12 +214,12 @@ function compareThings (a, b, _compareStrings) {
|
|
|
211
214
|
if (typeof b === 'boolean') { return typeof a === 'boolean' ? compareNSB(a, b) : 1; }
|
|
212
215
|
|
|
213
216
|
// Dates
|
|
214
|
-
if (
|
|
215
|
-
if (
|
|
217
|
+
if (isDate(a)) { return isDate(b) ? compareNSB(a.getTime(), b.getTime()) : -1; }
|
|
218
|
+
if (isDate(b)) { return isDate(a) ? compareNSB(a.getTime(), b.getTime()) : 1; }
|
|
216
219
|
|
|
217
220
|
// Arrays (first element is most significant and so on)
|
|
218
|
-
if (
|
|
219
|
-
if (
|
|
221
|
+
if (Array.isArray(a)) { return Array.isArray(b) ? compareArrays(a, b) : -1; }
|
|
222
|
+
if (Array.isArray(b)) { return Array.isArray(a) ? compareArrays(a, b) : 1; }
|
|
220
223
|
|
|
221
224
|
// Objects
|
|
222
225
|
aKeys = Object.keys(a).sort();
|
|
@@ -272,7 +275,7 @@ lastStepModifierFunctions.$push = function (obj, field, value) {
|
|
|
272
275
|
// Create the array if it doesn't exist
|
|
273
276
|
if (!obj.hasOwnProperty(field)) { obj[field] = []; }
|
|
274
277
|
|
|
275
|
-
if (!
|
|
278
|
+
if (!Array.isArray(obj[field])) { throw new Error("Can't $push an element on non-array values"); }
|
|
276
279
|
|
|
277
280
|
if (value !== null && typeof value === 'object' && value.$slice && value.$each === undefined) {
|
|
278
281
|
value.$each = [];
|
|
@@ -280,7 +283,7 @@ lastStepModifierFunctions.$push = function (obj, field, value) {
|
|
|
280
283
|
|
|
281
284
|
if (value !== null && typeof value === 'object' && value.$each) {
|
|
282
285
|
if (Object.keys(value).length >= 3 || (Object.keys(value).length === 2 && value.$slice === undefined)) { throw new Error("Can only use $slice in cunjunction with $each when $push to array"); }
|
|
283
|
-
if (!
|
|
286
|
+
if (!Array.isArray(value.$each)) { throw new Error("$each requires an array value"); }
|
|
284
287
|
|
|
285
288
|
value.$each.forEach(function (v) {
|
|
286
289
|
obj[field].push(v);
|
|
@@ -318,11 +321,11 @@ lastStepModifierFunctions.$addToSet = function (obj, field, value) {
|
|
|
318
321
|
// Create the array if it doesn't exist
|
|
319
322
|
if (!obj.hasOwnProperty(field)) { obj[field] = []; }
|
|
320
323
|
|
|
321
|
-
if (!
|
|
324
|
+
if (!Array.isArray(obj[field])) { throw new Error("Can't $addToSet an element on non-array values"); }
|
|
322
325
|
|
|
323
326
|
if (value !== null && typeof value === 'object' && value.$each) {
|
|
324
327
|
if (Object.keys(value).length > 1) { throw new Error("Can't use another field in conjunction with $each"); }
|
|
325
|
-
if (!
|
|
328
|
+
if (!Array.isArray(value.$each)) { throw new Error("$each requires an array value"); }
|
|
326
329
|
|
|
327
330
|
value.$each.forEach(function (v) {
|
|
328
331
|
lastStepModifierFunctions.$addToSet(obj, field, v);
|
|
@@ -340,7 +343,7 @@ lastStepModifierFunctions.$addToSet = function (obj, field, value) {
|
|
|
340
343
|
* Remove the first or last element of an array
|
|
341
344
|
*/
|
|
342
345
|
lastStepModifierFunctions.$pop = function (obj, field, value) {
|
|
343
|
-
if (!
|
|
346
|
+
if (!Array.isArray(obj[field])) { throw new Error("Can't $pop an element from non-array values"); }
|
|
344
347
|
if (typeof value !== 'number') { throw new Error(value + " isn't an integer, can't use it with $pop"); }
|
|
345
348
|
if (value === 0) { return; }
|
|
346
349
|
|
|
@@ -358,7 +361,7 @@ lastStepModifierFunctions.$pop = function (obj, field, value) {
|
|
|
358
361
|
lastStepModifierFunctions.$pull = function (obj, field, value) {
|
|
359
362
|
var arr, i;
|
|
360
363
|
|
|
361
|
-
if (!
|
|
364
|
+
if (!Array.isArray(obj[field])) { throw new Error("Can't $pull an element from non-array values"); }
|
|
362
365
|
|
|
363
366
|
arr = obj[field];
|
|
364
367
|
for (i = arr.length - 1; i >= 0; i -= 1) {
|
|
@@ -500,7 +503,7 @@ function getDotValue (obj, field) {
|
|
|
500
503
|
|
|
501
504
|
if (fieldParts.length === 1) { return obj[fieldParts[0]]; }
|
|
502
505
|
|
|
503
|
-
if (
|
|
506
|
+
if (Array.isArray(obj[fieldParts[0]])) {
|
|
504
507
|
// If the next field is an integer, return only this item of the array
|
|
505
508
|
i = parseInt(fieldParts[1], 10);
|
|
506
509
|
if (typeof i === 'number' && !isNaN(i)) {
|
|
@@ -533,11 +536,11 @@ function areThingsEqual (a, b) {
|
|
|
533
536
|
b === null || typeof b === 'string' || typeof b === 'boolean' || typeof b === 'number') { return a === b; }
|
|
534
537
|
|
|
535
538
|
// Dates
|
|
536
|
-
if (
|
|
539
|
+
if (isDate(a) || isDate(b)) { return isDate(a) && isDate(b) && a.getTime() === b.getTime(); }
|
|
537
540
|
|
|
538
541
|
// Arrays (no match since arrays are used as a $in)
|
|
539
542
|
// undefined (no match since they mean field doesn't exist and can't be serialized)
|
|
540
|
-
if ((!(
|
|
543
|
+
if ((!(Array.isArray(a) && Array.isArray(b)) && (Array.isArray(a) || Array.isArray(b))) || a === undefined || b === undefined) { return false; }
|
|
541
544
|
|
|
542
545
|
// General objects (check for deep equality)
|
|
543
546
|
// a and b should be objects at this point
|
|
@@ -561,8 +564,8 @@ function areThingsEqual (a, b) {
|
|
|
561
564
|
* Check that two values are comparable
|
|
562
565
|
*/
|
|
563
566
|
function areComparable (a, b) {
|
|
564
|
-
if (typeof a !== 'string' && typeof a !== 'number' && !
|
|
565
|
-
typeof b !== 'string' && typeof b !== 'number' && !
|
|
567
|
+
if (typeof a !== 'string' && typeof a !== 'number' && !isDate(a) &&
|
|
568
|
+
typeof b !== 'string' && typeof b !== 'number' && !isDate(b)) {
|
|
566
569
|
return false;
|
|
567
570
|
}
|
|
568
571
|
|
|
@@ -601,7 +604,7 @@ comparisonFunctions.$ne = function (a, b) {
|
|
|
601
604
|
comparisonFunctions.$in = function (a, b) {
|
|
602
605
|
var i;
|
|
603
606
|
|
|
604
|
-
if (!
|
|
607
|
+
if (!Array.isArray(b)) { throw new Error("$in operator called with a non-array"); }
|
|
605
608
|
|
|
606
609
|
for (i = 0; i < b.length; i += 1) {
|
|
607
610
|
if (areThingsEqual(a, b[i])) { return true; }
|
|
@@ -611,13 +614,13 @@ comparisonFunctions.$in = function (a, b) {
|
|
|
611
614
|
};
|
|
612
615
|
|
|
613
616
|
comparisonFunctions.$nin = function (a, b) {
|
|
614
|
-
if (!
|
|
617
|
+
if (!Array.isArray(b)) { throw new Error("$nin operator called with a non-array"); }
|
|
615
618
|
|
|
616
619
|
return !comparisonFunctions.$in(a, b);
|
|
617
620
|
};
|
|
618
621
|
|
|
619
622
|
comparisonFunctions.$regex = function (a, b) {
|
|
620
|
-
if (!
|
|
623
|
+
if (!isRegExp(b)) { throw new Error("$regex operator called with non regular expression"); }
|
|
621
624
|
|
|
622
625
|
if (typeof a !== 'string') {
|
|
623
626
|
return false
|
|
@@ -642,13 +645,13 @@ comparisonFunctions.$exists = function (value, exists) {
|
|
|
642
645
|
|
|
643
646
|
// Specific to arrays
|
|
644
647
|
comparisonFunctions.$size = function (obj, value) {
|
|
645
|
-
if (!
|
|
648
|
+
if (!Array.isArray(obj)) { return false; }
|
|
646
649
|
if (value % 1 !== 0) { throw new Error("$size operator called without an integer"); }
|
|
647
650
|
|
|
648
651
|
return (obj.length == value);
|
|
649
652
|
};
|
|
650
653
|
comparisonFunctions.$elemMatch = function (obj, value) {
|
|
651
|
-
if (!
|
|
654
|
+
if (!Array.isArray(obj)) { return false; }
|
|
652
655
|
var i = obj.length;
|
|
653
656
|
var result = false; // Initialize result
|
|
654
657
|
while (i--) {
|
|
@@ -671,7 +674,7 @@ arrayComparisonFunctions.$elemMatch = true;
|
|
|
671
674
|
logicalOperators.$or = function (obj, query) {
|
|
672
675
|
var i;
|
|
673
676
|
|
|
674
|
-
if (!
|
|
677
|
+
if (!Array.isArray(query)) { throw new Error("$or operator used without an array"); }
|
|
675
678
|
|
|
676
679
|
for (i = 0; i < query.length; i += 1) {
|
|
677
680
|
if (match(obj, query[i])) { return true; }
|
|
@@ -689,7 +692,7 @@ logicalOperators.$or = function (obj, query) {
|
|
|
689
692
|
logicalOperators.$and = function (obj, query) {
|
|
690
693
|
var i;
|
|
691
694
|
|
|
692
|
-
if (!
|
|
695
|
+
if (!Array.isArray(query)) { throw new Error("$and operator used without an array"); }
|
|
693
696
|
|
|
694
697
|
for (i = 0; i < query.length; i += 1) {
|
|
695
698
|
if (!match(obj, query[i])) { return false; }
|
|
@@ -768,14 +771,14 @@ function matchQueryPart (obj, queryKey, queryValue, treatObjAsValue) {
|
|
|
768
771
|
, i, keys, firstChars, dollarFirstChars;
|
|
769
772
|
|
|
770
773
|
// Check if the value is an array if we don't force a treatment as value
|
|
771
|
-
if (
|
|
774
|
+
if (Array.isArray(objValue) && !treatObjAsValue) {
|
|
772
775
|
// If the queryValue is an array, try to perform an exact match
|
|
773
|
-
if (
|
|
776
|
+
if (Array.isArray(queryValue)) {
|
|
774
777
|
return matchQueryPart(obj, queryKey, queryValue, true);
|
|
775
778
|
}
|
|
776
779
|
|
|
777
780
|
// Check if we are using an array-specific comparison function
|
|
778
|
-
if (queryValue !== null && typeof queryValue === 'object' && !
|
|
781
|
+
if (queryValue !== null && typeof queryValue === 'object' && !isRegExp(queryValue)) {
|
|
779
782
|
keys = Object.keys(queryValue);
|
|
780
783
|
for (i = 0; i < keys.length; i += 1) {
|
|
781
784
|
if (arrayComparisonFunctions[keys[i]]) { return matchQueryPart(obj, queryKey, queryValue, true); }
|
|
@@ -791,7 +794,7 @@ function matchQueryPart (obj, queryKey, queryValue, treatObjAsValue) {
|
|
|
791
794
|
|
|
792
795
|
// queryValue is an actual object. Determine whether it contains comparison operators
|
|
793
796
|
// or only normal fields. Mixed objects are not allowed
|
|
794
|
-
if (queryValue !== null && typeof queryValue === 'object' && !
|
|
797
|
+
if (queryValue !== null && typeof queryValue === 'object' && !isRegExp(queryValue) && !Array.isArray(queryValue)) {
|
|
795
798
|
keys = Object.keys(queryValue);
|
|
796
799
|
firstChars = _.map(keys, function (item) { return item[0]; });
|
|
797
800
|
dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; });
|
|
@@ -812,7 +815,7 @@ function matchQueryPart (obj, queryKey, queryValue, treatObjAsValue) {
|
|
|
812
815
|
}
|
|
813
816
|
|
|
814
817
|
// Using regular expressions with basic querying
|
|
815
|
-
if (
|
|
818
|
+
if (isRegExp(queryValue)) { return comparisonFunctions.$regex(objValue, queryValue); }
|
|
816
819
|
|
|
817
820
|
// queryValue is either a native value or a normal object
|
|
818
821
|
// Basic matching is possible
|
package/lib/persistence.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
var storage = require('./storage')
|
|
9
9
|
, path = require('path')
|
|
10
10
|
, model = require('./model')
|
|
11
|
-
, async = require('async')
|
|
11
|
+
, async = require('./async')
|
|
12
12
|
, customUtils = require('./customUtils')
|
|
13
13
|
, Index = require('./indexes')
|
|
14
14
|
;
|
|
@@ -72,7 +72,7 @@ Persistence.ensureDirectoryExists = function (dir, cb) {
|
|
|
72
72
|
var callback = cb || function () {}
|
|
73
73
|
;
|
|
74
74
|
|
|
75
|
-
storage.mkdirp(dir
|
|
75
|
+
storage.mkdirp(dir).then(function() { return callback(); }).catch(callback);
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
|