monastery 1.28.4 → 1.30.1
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/.eslintrc.json +30 -4
- package/docs/errors.md +0 -0
- package/docs/image-plugin.md +0 -0
- package/docs/manager/model.md +0 -0
- package/docs/manager/models.md +0 -0
- package/docs/model/findOne.md +0 -0
- package/docs/model/index.md +0 -0
- package/docs/model/remove.md +0 -0
- package/docs/readme.md +1 -1
- package/docs/rules.md +0 -0
- package/lib/index.js +0 -1
- package/lib/model-crud.js +33 -17
- package/lib/model-validate.js +25 -24
- package/lib/model.js +23 -12
- package/lib/rules.js +38 -38
- package/lib/util.js +13 -7
- package/package.json +16 -15
- package/plugins/images/index.js +97 -90
- package/test/assets/bad.svg +0 -0
- package/test/assets/image.ico +0 -0
- package/test/assets/image.webp +0 -0
- package/test/assets/lion1.png +0 -0
- package/test/assets/lion2.jpg +0 -0
- package/test/assets/logo.png +0 -0
- package/test/assets/logo2.png +0 -0
- package/test/blacklisting.js +4 -8
- package/test/crud.js +28 -31
- package/test/model.js +114 -28
- package/test/monk.js +4 -4
- package/test/plugin-images.js +486 -346
- package/test/populate.js +15 -18
- package/test/util.js +8 -8
- package/test/validate.js +227 -108
- package/test/virtuals.js +2 -4
package/.eslintrc.json
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": true,
|
|
4
|
+
"es2021": true,
|
|
5
|
+
"node": true
|
|
6
|
+
},
|
|
7
|
+
"extends": [
|
|
8
|
+
"eslint:recommended"
|
|
9
|
+
],
|
|
10
|
+
"globals": {
|
|
11
|
+
"test": true,
|
|
12
|
+
"expect": true
|
|
13
|
+
},
|
|
2
14
|
"parserOptions": {
|
|
3
|
-
"ecmaVersion": 2018,
|
|
4
|
-
"sourceType": "module",
|
|
5
15
|
"ecmaFeatures": {
|
|
6
16
|
"jsx": true
|
|
7
|
-
}
|
|
17
|
+
},
|
|
18
|
+
"ecmaVersion": "latest",
|
|
19
|
+
"sourceType": "module"
|
|
8
20
|
},
|
|
21
|
+
"plugins": [],
|
|
9
22
|
"rules": {
|
|
10
|
-
|
|
23
|
+
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
|
24
|
+
"max-len": ["error", { "code": 120, "ignorePattern": "^\\s*<(rect|path|line)\\s" }],
|
|
25
|
+
"no-prototype-builtins": "off",
|
|
26
|
+
"no-unused-vars": ["error", { "args": "none" }],
|
|
27
|
+
"object-shorthand": ["error", "consistent"],
|
|
28
|
+
// "no-restricted-syntax": [
|
|
29
|
+
// "error",
|
|
30
|
+
// {
|
|
31
|
+
// "selector": "ObjectPattern > Property[shorthand=false]",
|
|
32
|
+
// "message": "Renaming properties within object deconstructions is not allowed."
|
|
33
|
+
// }
|
|
34
|
+
// ],
|
|
35
|
+
"quotes": ["error", "single"],
|
|
36
|
+
"semi": ["error", "never"]
|
|
11
37
|
}
|
|
12
38
|
}
|
package/docs/errors.md
CHANGED
|
File without changes
|
package/docs/image-plugin.md
CHANGED
|
File without changes
|
package/docs/manager/model.md
CHANGED
|
File without changes
|
package/docs/manager/models.md
CHANGED
|
File without changes
|
package/docs/model/findOne.md
CHANGED
|
File without changes
|
package/docs/model/index.md
CHANGED
|
File without changes
|
package/docs/model/remove.md
CHANGED
|
File without changes
|
package/docs/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/monastery) [](https://travis-ci.com/boycce/monastery)
|
|
3
|
+
[](https://www.npmjs.com/package/monastery) [](https://app.travis-ci.com/github/boycce/monastery)
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
package/docs/rules.md
CHANGED
|
File without changes
|
package/lib/index.js
CHANGED
package/lib/model-crud.js
CHANGED
|
@@ -9,7 +9,8 @@ module.exports = {
|
|
|
9
9
|
* @param {object|array} <opts.data> - documents to insert
|
|
10
10
|
* @param {array|string|false} <opts.blacklist> - augment schema.insertBL, `false` will remove all blacklisting
|
|
11
11
|
* @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
|
|
12
|
-
* @param {array|string|
|
|
12
|
+
* @param {array|string|false} validateUndefined - ignore all required fields during insert, or
|
|
13
|
+
* undefined subdocument required fields that have a defined parent/grandparent during update
|
|
13
14
|
* @param {array|string|true} <opts.skipValidation> - skip validation for this field name(s)
|
|
14
15
|
* @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are automatically inserted
|
|
15
16
|
* @param {any} <opts.any> - any mongodb option
|
|
@@ -21,7 +22,9 @@ module.exports = {
|
|
|
21
22
|
opts.insert = true
|
|
22
23
|
opts.model = this
|
|
23
24
|
let data = opts.data = opts.data || (opts.req? opts.req.body : {})
|
|
24
|
-
let options = util.omit(opts, [
|
|
25
|
+
let options = util.omit(opts, [
|
|
26
|
+
'data', 'insert', 'model', 'respond', 'validateUndefined', 'skipValidation', 'blacklist'
|
|
27
|
+
])
|
|
25
28
|
if (cb && !util.isFunction(cb)) {
|
|
26
29
|
throw new Error(`The callback passed to ${this.name}.insert() is not a function`)
|
|
27
30
|
}
|
|
@@ -75,7 +78,7 @@ module.exports = {
|
|
|
75
78
|
opts.one = one || opts.one
|
|
76
79
|
// Operation options
|
|
77
80
|
options = util.omit(opts, ['blacklist', 'one', 'populate', 'project', 'query', 'respond'])
|
|
78
|
-
options.sort = options.sort || {
|
|
81
|
+
options.sort = options.sort || { 'createdAt': -1 }
|
|
79
82
|
options.skip = Math.max(0, options.skip || 0)
|
|
80
83
|
options.limit = opts.one? 1 : parseInt(options.limit || this.manager.limit || 0)
|
|
81
84
|
options.addFields = options.addFields || {}
|
|
@@ -99,8 +102,8 @@ module.exports = {
|
|
|
99
102
|
}
|
|
100
103
|
// Has text search?
|
|
101
104
|
// if (opts.query.$text) {
|
|
102
|
-
// options.projection.score = { $meta:
|
|
103
|
-
// options.sort = { score: { $meta:
|
|
105
|
+
// options.projection.score = { $meta: 'textScore' }
|
|
106
|
+
// options.sort = { score: { $meta: 'textScore' }}
|
|
104
107
|
// }
|
|
105
108
|
// Sort string passed
|
|
106
109
|
if (util.isString(options.sort)) {
|
|
@@ -130,7 +133,7 @@ module.exports = {
|
|
|
130
133
|
continue
|
|
131
134
|
}
|
|
132
135
|
// Populate model (convert array into document & create lookup)
|
|
133
|
-
options.addFields[path] = {
|
|
136
|
+
options.addFields[path] = { '$arrayElemAt': [ '$' + path, 0 ] }
|
|
134
137
|
lookups.push({ $lookup: {
|
|
135
138
|
from: modelName,
|
|
136
139
|
localField: path,
|
|
@@ -191,7 +194,8 @@ module.exports = {
|
|
|
191
194
|
* @param {object} <opts.query> - mongodb query object
|
|
192
195
|
* @param {object|array} <opts.data> - mongodb document update object(s)
|
|
193
196
|
* @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
|
|
194
|
-
* @param {array|string|
|
|
197
|
+
* @param {array|string|false} validateUndefined - ignore all required fields during insert, or
|
|
198
|
+
* undefined subdocument required fields that have a defined parent/grandparent during update
|
|
195
199
|
* @param {array|string|true} <opts.skipValidation> - skip validation for this field name(s)
|
|
196
200
|
* @param {boolean} <opts.timestamps> - whether `updatedAt` is automatically updated
|
|
197
201
|
* @param {array|string|false} <opts.blacklist> - augment schema.updateBL, `false` will remove all blacklisting
|
|
@@ -211,8 +215,8 @@ module.exports = {
|
|
|
211
215
|
data = opts.data = opts.data || (opts.req? opts.req.body : null)
|
|
212
216
|
operators = util.pluck(opts, [/^\$/])
|
|
213
217
|
// Operation options
|
|
214
|
-
options = util.omit(opts, ['data', 'query', 'respond', '
|
|
215
|
-
options.sort = options.sort || {
|
|
218
|
+
options = util.omit(opts, ['data', 'query', 'respond', 'validateUndefined', 'skipValidation', 'blacklist'])
|
|
219
|
+
options.sort = options.sort || { 'createdAt': -1 }
|
|
216
220
|
options.limit = parseInt(options.limit || 0)
|
|
217
221
|
// Sort string passed
|
|
218
222
|
if (util.isString(options.sort)) {
|
|
@@ -283,7 +287,7 @@ module.exports = {
|
|
|
283
287
|
if (util.isEmpty(opts.query)) throw new Error('Please specify opts.query')
|
|
284
288
|
// Operation options
|
|
285
289
|
options = util.omit(opts, ['query', 'respond'])
|
|
286
|
-
options.sort = options.sort || {
|
|
290
|
+
options.sort = options.sort || { 'createdAt': -1 }
|
|
287
291
|
options.limit = parseInt(options.limit || 1)
|
|
288
292
|
// Sort string passed
|
|
289
293
|
if (util.isString(options.sort)) {
|
|
@@ -397,7 +401,7 @@ module.exports = {
|
|
|
397
401
|
let paths = (populate||[]).map(o => o && o.as? o.as : o)
|
|
398
402
|
|
|
399
403
|
if (!paths.length) return blacklistProjection
|
|
400
|
-
this._recurseFields(model.fields,
|
|
404
|
+
this._recurseFields(model.fields, '', function(path, field) {
|
|
401
405
|
// Remove array indexes from the path e.g. '0.'
|
|
402
406
|
path = path.replace(/(\.[0-9]+)(\.|$)/, '$2')
|
|
403
407
|
if (!field.model || !paths.includes(path)) return
|
|
@@ -420,7 +424,8 @@ module.exports = {
|
|
|
420
424
|
/**
|
|
421
425
|
* Merge blacklist in
|
|
422
426
|
* @param {object} blacklistProjection
|
|
423
|
-
* @param {array} paths - e.g. ['password', '-email'] - email will be whitelisted / removed from
|
|
427
|
+
* @param {array} paths - e.g. ['password', '-email'] - email will be whitelisted / removed from
|
|
428
|
+
* exlcusion projection
|
|
424
429
|
* @return {object} exclusion blacklist
|
|
425
430
|
* @this model
|
|
426
431
|
*/
|
|
@@ -461,7 +466,7 @@ module.exports = {
|
|
|
461
466
|
let findWL = [ '_id', ...this.findWL ]
|
|
462
467
|
let model = this.manager.model
|
|
463
468
|
|
|
464
|
-
this._recurseFields(this.fields,
|
|
469
|
+
this._recurseFields(this.fields, '', function(path, field) {
|
|
465
470
|
// Remove array indexes from the path e.g. '0.'
|
|
466
471
|
path = path.replace(/(\.[0-9]+)(\.|$)/, '$2')
|
|
467
472
|
//if (field.type == 'any') findWL.push(path) //exclude.push(path)
|
|
@@ -529,7 +534,7 @@ module.exports = {
|
|
|
529
534
|
}
|
|
530
535
|
|
|
531
536
|
for (let doc of util.toArray(data)) {
|
|
532
|
-
recurseAndDeleteData(doc,
|
|
537
|
+
recurseAndDeleteData(doc, '')
|
|
533
538
|
}
|
|
534
539
|
return data
|
|
535
540
|
},
|
|
@@ -586,7 +591,11 @@ module.exports = {
|
|
|
586
591
|
if (!data) return
|
|
587
592
|
|
|
588
593
|
// Valid model object field.
|
|
589
|
-
if (
|
|
594
|
+
if (
|
|
595
|
+
((util.isArray(field) && field[0].model) || field.model) &&
|
|
596
|
+
data[fieldName] &&
|
|
597
|
+
util.isObjectAndNotID(data[fieldName])
|
|
598
|
+
) {
|
|
590
599
|
// Note that sometimes a single model is passed instead of an array of models via a custom populate $lookup
|
|
591
600
|
out.push({
|
|
592
601
|
dataRef: data[fieldName],
|
|
@@ -595,11 +604,18 @@ module.exports = {
|
|
|
595
604
|
})
|
|
596
605
|
|
|
597
606
|
// Recurse through fields that are sub-documents
|
|
598
|
-
} else if (
|
|
607
|
+
} else if (
|
|
608
|
+
util.isSubdocument(field) &&
|
|
609
|
+
util.isObjectAndNotID(data[fieldName])
|
|
610
|
+
) {
|
|
599
611
|
out = [...out, ...this._recurseAndFindModels(field, data[fieldName])]
|
|
600
612
|
|
|
601
613
|
// Array of sub-documents or models
|
|
602
|
-
} else if (
|
|
614
|
+
} else if (
|
|
615
|
+
(util.isArray(field) || field.model) &&
|
|
616
|
+
data[fieldName] &&
|
|
617
|
+
util.isObjectAndNotID(data[fieldName][0])
|
|
618
|
+
) {
|
|
603
619
|
// Valid model object found in array
|
|
604
620
|
// Note that sometimes an array of models are passed instead of single object via a custom populate $lookup
|
|
605
621
|
if (field.model || field[0].model) {
|
package/lib/model-validate.js
CHANGED
|
@@ -12,9 +12,11 @@ module.exports = {
|
|
|
12
12
|
* @param {boolean(false)} update - are we validating for insert or update?
|
|
13
13
|
* @param {array|string|false} blacklist - augment schema blacklist, `false` will remove all blacklisting
|
|
14
14
|
* @param {array|string} projection - only return these fields, ignores blacklist
|
|
15
|
-
* @param {array|string|
|
|
15
|
+
* @param {array|string|false} validateUndefined - ignore all required fields during insert, or undefined
|
|
16
|
+
* subdocument required fields that have a defined parent/grandparent during update
|
|
16
17
|
* @param {array|string|true} skipValidation - skip validation on these fields
|
|
17
|
-
* @param {boolean} timestamps - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
|
|
18
|
+
* @param {boolean} timestamps - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
|
|
19
|
+
* updated, depending on the `options.update` value
|
|
18
20
|
* @param {function} <cb> - instead of returning a promise
|
|
19
21
|
* @this model
|
|
20
22
|
|
|
@@ -56,7 +58,7 @@ module.exports = {
|
|
|
56
58
|
else continue
|
|
57
59
|
if (whitelist.includes(split.join())) {
|
|
58
60
|
blacklist.splice(i, 1)
|
|
59
|
-
break
|
|
61
|
+
break
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
}
|
|
@@ -175,7 +177,11 @@ module.exports = {
|
|
|
175
177
|
// Object schema errors
|
|
176
178
|
errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
|
|
177
179
|
// Recurse if data value is a subdocument, or when inserting, or when updating deep properties (non-root)
|
|
178
|
-
if (
|
|
180
|
+
if (
|
|
181
|
+
util.isObject(value) ||
|
|
182
|
+
opts.insert ||
|
|
183
|
+
((path2||'').match(/\./) && (util.isDefined(opts.validateUndefined) ? opts.validateUndefined : true))
|
|
184
|
+
) {
|
|
179
185
|
var res = this._validateFields(dataRoot, field, value, opts, path2)
|
|
180
186
|
errors.push(...res[0])
|
|
181
187
|
}
|
|
@@ -189,11 +195,11 @@ module.exports = {
|
|
|
189
195
|
errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
|
|
190
196
|
// Data value is array too
|
|
191
197
|
if (util.isArray(value)) {
|
|
192
|
-
var
|
|
193
|
-
errors.push(...
|
|
198
|
+
var res2 = this._validateFields(dataRoot, field, value, opts, path2)
|
|
199
|
+
errors.push(...res2[0])
|
|
194
200
|
}
|
|
195
201
|
if (util.isDefined(value) && !verrors.length) {
|
|
196
|
-
data2[indexOrFieldName] =
|
|
202
|
+
data2[indexOrFieldName] = res2? res2[1] : value
|
|
197
203
|
}
|
|
198
204
|
}
|
|
199
205
|
}, this)
|
|
@@ -211,6 +217,7 @@ module.exports = {
|
|
|
211
217
|
* @param {object} dataRoot - data
|
|
212
218
|
* @param {object} field - field schema
|
|
213
219
|
* @param {string} path - full field path
|
|
220
|
+
* @param {object} opts - original validate() options
|
|
214
221
|
* @this model
|
|
215
222
|
* @return {array} errors
|
|
216
223
|
*/
|
|
@@ -234,44 +241,38 @@ module.exports = {
|
|
|
234
241
|
for (let i=0, l=skippedFieldChunks.length; i<l; i++) {
|
|
235
242
|
if (skippedFieldChunks[i] == '$') skippedFieldChunks[i] = '[0-9]+'
|
|
236
243
|
}
|
|
237
|
-
if (path.match(new RegExp('^' + skippedFieldChunks.join('.') + '(
|
|
244
|
+
if (path.match(new RegExp('^' + skippedFieldChunks.join('.') + '(.|$)'))) return []
|
|
238
245
|
}
|
|
239
246
|
}
|
|
240
247
|
|
|
241
248
|
for (let ruleName in field) {
|
|
242
249
|
if (this._ignoredRules.indexOf(ruleName) > -1) continue
|
|
243
|
-
|
|
244
|
-
if (util.isUndefined(value) && ((opts.update && !path.match(/\./)) || opts.ignoreUndefined)) continue
|
|
245
|
-
let error = this._validateRule(dataRoot, ruleName, field, field[ruleName], value, path)
|
|
250
|
+
let error = this._validateRule(dataRoot, ruleName, field, field[ruleName], value, opts, path)
|
|
246
251
|
if (error && ruleName == 'required') return [error] // only show the required error
|
|
247
252
|
if (error) errors.push(error)
|
|
248
253
|
}
|
|
249
254
|
return errors
|
|
250
255
|
},
|
|
251
256
|
|
|
252
|
-
_validateRule: function(dataRoot, ruleName, field, ruleArg, value, path) {
|
|
253
|
-
//this.debug(path, field, ruleName, ruleArg, value)
|
|
257
|
+
_validateRule: function(dataRoot, ruleName, field, ruleArg, value, opts, path) {
|
|
258
|
+
// this.debug(path, field, ruleName, ruleArg, value)
|
|
254
259
|
// Remove [] from the message path, and simply ignore non-numeric children to test for all array items
|
|
255
260
|
ruleArg = ruleArg === true? undefined : ruleArg
|
|
256
261
|
let rule = this.rules[ruleName] || rules[ruleName]
|
|
257
|
-
let fieldName = path.match(/[
|
|
262
|
+
let fieldName = path.match(/[^.]+$/)[0]
|
|
258
263
|
let ruleMessageKey = this._getMostSpecificKeyMatchingPath(this.messages, path)
|
|
259
264
|
let ruleMessage = ruleMessageKey && this.messages[ruleMessageKey][ruleName]
|
|
265
|
+
let validateUndefined = util.isDefined(opts.validateUndefined) ? opts.validateUndefined : rule.validateUndefined
|
|
260
266
|
if (!ruleMessage) ruleMessage = rule.message
|
|
261
267
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (typeof value === 'undefined') return
|
|
268
|
+
// Ignore undefined (if updated root property, or ignoring)
|
|
269
|
+
if ((!validateUndefined || (opts.update && !path.match(/\./))) && typeof value === 'undefined') return
|
|
265
270
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
// Ignore null if nullObject is set on objects or arrays
|
|
270
|
-
if (value === null && field.nullObject) return
|
|
271
|
-
}
|
|
271
|
+
// Ignore null (if nullObject is set on objects or arrays) (todo: change to ignoreNull)
|
|
272
|
+
if (field.nullObject && (field.isObject || field.isArray) && value === null) return
|
|
272
273
|
|
|
273
274
|
// Ignore empty strings
|
|
274
|
-
if (value === ''
|
|
275
|
+
if (!rule.validateEmptyString && value === '') return
|
|
275
276
|
|
|
276
277
|
// Rule failed
|
|
277
278
|
if (!rule.fn.call(dataRoot, value, ruleArg, path, this)) return {
|
package/lib/model.js
CHANGED
|
@@ -61,6 +61,7 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
61
61
|
// Update with formatted rule
|
|
62
62
|
let formattedRule = util.isObject(rule)? rule : { fn: rule }
|
|
63
63
|
if (!formattedRule.message) formattedRule.message = `Invalid data property for rule "${ruleName}".`
|
|
64
|
+
if (typeof formattedRule.validateEmptyString == 'undefined') formattedRule.validateEmptyString = true
|
|
64
65
|
this.rules[ruleName] = formattedRule
|
|
65
66
|
}
|
|
66
67
|
}, this)
|
|
@@ -203,7 +204,12 @@ Model.prototype._setupFields = function(fields) {
|
|
|
203
204
|
let nullObject = this.manager.nullObjects
|
|
204
205
|
let virtual = field.length == 1 && (field[0]||{}).virtual ? true : undefined
|
|
205
206
|
field.schema = {
|
|
206
|
-
type: 'array',
|
|
207
|
+
type: 'array',
|
|
208
|
+
isArray: true,
|
|
209
|
+
default: arrayDefault,
|
|
210
|
+
nullObject: nullObject,
|
|
211
|
+
virtual: virtual,
|
|
212
|
+
...(field.schema || {})
|
|
207
213
|
}
|
|
208
214
|
this._setupFields(field)
|
|
209
215
|
|
|
@@ -236,11 +242,10 @@ Model.prototype._setupFieldsAndWhitelists = function(fields, path) {
|
|
|
236
242
|
this.findBLProject = this.findBL.reduce((o, v) => { (o[v] = 0); return o }, {})
|
|
237
243
|
},
|
|
238
244
|
|
|
239
|
-
Model.prototype._setupIndexes = function(fields) {
|
|
245
|
+
Model.prototype._setupIndexes = function(fields, opts={}) {
|
|
240
246
|
/**
|
|
241
|
-
* Creates indexes for the model
|
|
247
|
+
* Creates indexes for the model (multikey, and sub-document supported)
|
|
242
248
|
* Note: only one text index per model(collection) is allowed due to mongodb limitations
|
|
243
|
-
* Note: we and currently don't support indexes on sub-collections, but sub-documents yes!
|
|
244
249
|
* @link https://docs.mongodb.com/manual/reference/command/createIndexes/
|
|
245
250
|
* @link https://mongodb.github.io/node-mongodb-native/2.1/api/Collection.html#createIndexes
|
|
246
251
|
* @param {object} <fields>
|
|
@@ -262,7 +267,10 @@ Model.prototype._setupIndexes = function(fields) {
|
|
|
262
267
|
|
|
263
268
|
// No db defined
|
|
264
269
|
if (!(model.manager._state || '').match(/^open/)) {
|
|
265
|
-
let error = {
|
|
270
|
+
let error = {
|
|
271
|
+
type: 'info',
|
|
272
|
+
detail: `Skipping createIndex on the '${model.name}' model, no mongodb connection found.`
|
|
273
|
+
}
|
|
266
274
|
return Promise.reject(error)
|
|
267
275
|
}
|
|
268
276
|
|
|
@@ -270,6 +278,7 @@ Model.prototype._setupIndexes = function(fields) {
|
|
|
270
278
|
recurseFields(fields || model.fields, '')
|
|
271
279
|
// console.log(2, indexes, fields)
|
|
272
280
|
if (hasTextIndex) indexes.push(textIndex)
|
|
281
|
+
if (opts.dryRun) return Promise.resolve(indexes || [])
|
|
273
282
|
if (!indexes.length) return Promise.resolve([]) // No indexes defined
|
|
274
283
|
|
|
275
284
|
// Create indexes
|
|
@@ -322,22 +331,24 @@ Model.prototype._setupIndexes = function(fields) {
|
|
|
322
331
|
util.forEach(fields, (field, name) => {
|
|
323
332
|
let index = field.index
|
|
324
333
|
if (index) {
|
|
325
|
-
let path = name == 'schema'? parentPath.slice(0, -1) : parentPath + name
|
|
326
334
|
let options = util.isObject(index)? util.omit(index, ['type']) : {}
|
|
327
335
|
let type = util.isObject(index)? index.type : index
|
|
336
|
+
let path = name == 'schema'? parentPath.slice(0, -1) : parentPath + name
|
|
337
|
+
let path2 = path.replace(/(^|\.)[0-9]+(\.|$)/g, '$2') // no numirical keys, e.g. pets.1.name
|
|
328
338
|
if (type === true) type = 1
|
|
329
|
-
|
|
330
339
|
if (type == 'text') {
|
|
331
|
-
hasTextIndex = textIndex.key[
|
|
340
|
+
hasTextIndex = textIndex.key[path2] = 'text'
|
|
332
341
|
Object.assign(textIndex, options)
|
|
333
342
|
} else if (type == '1' || type == '-1' || type == '2dsphere') {
|
|
334
|
-
indexes.push({ name: `${
|
|
343
|
+
indexes.push({ name: `${path2}_${type}`, key: { [path2]: type }, ...options })
|
|
335
344
|
} else if (type == 'unique') {
|
|
336
|
-
indexes.push({ name: `${
|
|
345
|
+
indexes.push({ name: `${path2}_1`, key: { [path2]: 1 }, unique: true, ...options })
|
|
337
346
|
}
|
|
338
347
|
}
|
|
339
348
|
if (util.isSubdocument(field)) {
|
|
340
349
|
recurseFields(field, parentPath + name + '.')
|
|
350
|
+
} else if (util.isArray(field)) {
|
|
351
|
+
recurseFields(field, parentPath + name + '.')
|
|
341
352
|
}
|
|
342
353
|
})
|
|
343
354
|
}
|
|
@@ -361,10 +372,10 @@ Model.prototype._timestampFields = {
|
|
|
361
372
|
}
|
|
362
373
|
}
|
|
363
374
|
|
|
364
|
-
for (
|
|
375
|
+
for (let key in crud) {
|
|
365
376
|
Model.prototype[key] = crud[key]
|
|
366
377
|
}
|
|
367
378
|
|
|
368
|
-
for (
|
|
379
|
+
for (let key in validate) {
|
|
369
380
|
Model.prototype[key] = validate[key]
|
|
370
381
|
}
|