monastery 3.0.22 → 3.1.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/changelog.md +4 -0
- package/lib/model-crud.js +39 -36
- package/lib/model-validate.js +100 -65
- package/lib/model.js +126 -61
- package/lib/util.js +12 -2
- package/package.json +1 -1
- package/plugins/images/index.js +9 -9
- package/test/blacklisting.js +2 -1
- package/test/crud.js +62 -0
- package/test/manager.js +1 -1
- package/test/model.js +158 -76
- package/test/plugin-images.js +38 -16
- package/test/validate.js +81 -0
- package/test/virtuals.js +2 -2
package/lib/model.js
CHANGED
|
@@ -21,6 +21,11 @@ function Model(name, opts, manager) {
|
|
|
21
21
|
} else if (!opts.fields) {
|
|
22
22
|
throw `We couldn't find ${name}.fields in the model definition, the model maybe setup `
|
|
23
23
|
+ `or exported incorrectly:\n${JSON.stringify(opts, null, 2)}`
|
|
24
|
+
} else if (!util.isSubdocument(opts.fields) && opts.fields.type == 'any') {
|
|
25
|
+
throw `Instead of using { type: 'any' } for ${name}.fields, please use the new 'strict' definition rule` +
|
|
26
|
+
', e.g. { schema: { strict: false }}'
|
|
27
|
+
} else if (!util.isSubdocument(opts.fields) && !util.isEmpty(opts.fields)) {
|
|
28
|
+
throw `The ${name}.fields object should be a valid document, e.g. { name: { type: 'string' }}`
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
// Add schema options
|
|
@@ -107,8 +112,11 @@ function Model(name, opts, manager) {
|
|
|
107
112
|
}, this)
|
|
108
113
|
|
|
109
114
|
// Extend default fields with passed in fields and check for invalid fields
|
|
110
|
-
this._setupFields(
|
|
115
|
+
this._setupFields(
|
|
116
|
+
this.fields = util.isSchema(this.fields) ? this.fields : Object.assign({}, this._defaultFields, this.fields)
|
|
117
|
+
)
|
|
111
118
|
this.fieldsFlattened = this._getFieldsFlattened(this.fields, '') // test output?
|
|
119
|
+
this.modelFieldsArray = this._getModelFieldsArray()
|
|
112
120
|
|
|
113
121
|
// Get collection, and extend model with collection methods
|
|
114
122
|
this.collection = this.manager.get(name, { castIds: false })
|
|
@@ -138,115 +146,158 @@ function Model(name, opts, manager) {
|
|
|
138
146
|
|
|
139
147
|
Model.prototype._getFieldsFlattened = function(fields, path) {
|
|
140
148
|
/**
|
|
141
|
-
*
|
|
149
|
+
* Get flattened fields
|
|
142
150
|
* @param {object|array} fields - can be a nested subdocument or array
|
|
143
151
|
* @param {string} path
|
|
144
152
|
* @return {object} e.g. {'name': Schema, 'pets.dog': Schema}
|
|
145
153
|
*/
|
|
146
154
|
let obj = {}
|
|
147
155
|
util.forEach(fields, function(field, fieldName) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
156
|
+
const schema = field.schema
|
|
157
|
+
const newPath = /*util.isArray(fields)? path : */path + fieldName + '.'
|
|
158
|
+
if (fieldName == 'schema') return
|
|
159
|
+
if (schema.isArray) {
|
|
152
160
|
Object.assign(obj, this._getFieldsFlattened(field, newPath))
|
|
153
|
-
} else if (
|
|
161
|
+
} else if (schema.isObject) {
|
|
154
162
|
Object.assign(obj, this._getFieldsFlattened(field, newPath))
|
|
155
163
|
} else {
|
|
156
|
-
obj[newPath.replace(/\.$/, '')] =
|
|
164
|
+
obj[newPath.replace(/\.$/, '')] = schema
|
|
157
165
|
}
|
|
158
166
|
}, this)
|
|
159
167
|
return obj
|
|
160
168
|
}
|
|
161
169
|
|
|
162
|
-
Model.prototype.
|
|
170
|
+
Model.prototype._getModelFieldsArray = function() {
|
|
171
|
+
/**
|
|
172
|
+
* Get all the model fields in an array
|
|
173
|
+
* @return {array} e.g. [{ path: 'pets.0.dog', 'path2': 'pets.dog'}, ...]
|
|
174
|
+
*/
|
|
175
|
+
return Object.keys(this.fieldsFlattened).reduce((acc, path) => {
|
|
176
|
+
if (this.fieldsFlattened[path].model) {
|
|
177
|
+
acc.push({ path: path, path2: path.replace(/\.[0-9]+(\.|$)/g, '$1') })
|
|
178
|
+
}
|
|
179
|
+
return acc
|
|
180
|
+
}, [])
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
Model.prototype._setupFields = function(fields, isSub) {
|
|
163
184
|
/**
|
|
164
185
|
* Check for invalid rules on a field object, and set field.isType
|
|
165
186
|
* @param {object|array} fields - subsdocument or array
|
|
166
187
|
*/
|
|
188
|
+
// We need to allow the processing of the root schema object
|
|
189
|
+
if (!isSub) fields = { fields }
|
|
190
|
+
|
|
167
191
|
util.forEach(fields, function(field, fieldName) {
|
|
168
192
|
// Schema field
|
|
193
|
+
if (fieldName == 'schema') return
|
|
169
194
|
if (util.isSchema(field)) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (field.model) field.type = 'id'
|
|
173
|
-
let isType = 'is' + util.ucFirst(field.type)
|
|
195
|
+
const schema = field
|
|
196
|
+
fields[fieldName] = field = { schema }
|
|
174
197
|
|
|
175
|
-
//
|
|
176
|
-
if (
|
|
198
|
+
// Type 'model'
|
|
199
|
+
if (schema.model) {
|
|
200
|
+
schema.type = 'id'
|
|
201
|
+
|
|
202
|
+
// Type 'image', but no image plugin schema processing done, e.g. image plugin not setup
|
|
203
|
+
} else if (schema.type == 'image' && !schema.image) {
|
|
204
|
+
schema.image = true
|
|
205
|
+
schema.type = 'any'
|
|
206
|
+
|
|
207
|
+
// No type
|
|
208
|
+
} else if (!schema.type) {
|
|
177
209
|
this.error(`No type defined on "${this.name}" for field "${fieldName}". Defaulting to string.`)
|
|
178
|
-
|
|
210
|
+
schema.type = 'string'
|
|
211
|
+
}
|
|
179
212
|
|
|
180
|
-
// Type
|
|
181
|
-
|
|
182
|
-
|
|
213
|
+
// Type isn't a rule
|
|
214
|
+
const isType = schema.isType = 'is' + util.ucFirst(schema.type)
|
|
215
|
+
if (!this.rules[isType] && !rules[isType]) {
|
|
216
|
+
this.error(`Not a valid type "${schema.type}" defined on "${this.name}" for field "${fieldName}".
|
|
183
217
|
Defaulting to string.`)
|
|
184
|
-
|
|
218
|
+
schema.type = 'string'
|
|
185
219
|
}
|
|
186
220
|
|
|
187
|
-
|
|
188
|
-
|
|
221
|
+
field.schema = {
|
|
222
|
+
...field.schema,
|
|
223
|
+
[isType]: true, // e.g. isString rule
|
|
224
|
+
isSchema: true,
|
|
225
|
+
}
|
|
189
226
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
if ((this.rules[ruleName] || rules[ruleName]) && this._ignoredRules.indexOf(ruleName) != -1) {
|
|
193
|
-
this.error(`The rule name "${ruleName}" for the model "${this.name}" is a reserved keyword, ignoring rule.`)
|
|
194
|
-
}
|
|
195
|
-
if (!this.rules[ruleName] && !rules[ruleName] && this._ignoredRules.indexOf(ruleName) == -1) {
|
|
196
|
-
// console.log(field)
|
|
197
|
-
this.error(`No rule "${ruleName}" exists for model "${this.name}", ignoring rule.`)
|
|
198
|
-
delete field[ruleName]
|
|
199
|
-
}
|
|
200
|
-
}, this)
|
|
227
|
+
// Remove invalid rules
|
|
228
|
+
this._removeInvalidRules(field)
|
|
201
229
|
|
|
202
230
|
// Misused schema property
|
|
203
|
-
} else if (fieldName == 'schema') {
|
|
204
|
-
this.error(`Invalid
|
|
231
|
+
} else if (fieldName == 'schema' || fieldName == 'isSchema') {
|
|
232
|
+
this.error(`Invalid field '${fieldName}' on model '${this.name}', this is a reserved property, ignoring field.`)
|
|
205
233
|
delete fields[fieldName]
|
|
206
234
|
|
|
207
235
|
// Fields be an array
|
|
208
236
|
} else if (util.isArray(field)) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
let virtual = field.length == 1 && (field[0]||{}).virtual ? true : undefined
|
|
212
|
-
field.schema = {
|
|
237
|
+
this._removeInvalidRules(field)
|
|
238
|
+
field.schema = util.removeUndefined({
|
|
213
239
|
type: 'array',
|
|
214
240
|
isArray: true,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
isSchema: true,
|
|
242
|
+
isType: 'isArray',
|
|
243
|
+
default: this.manager.opts.defaultObjects? () => [] : undefined,
|
|
244
|
+
nullObject: this.manager.opts.nullObjects,
|
|
245
|
+
virtual: field.length == 1 && (field[0] || {}).virtual ? true : undefined,
|
|
218
246
|
...(field.schema || {}),
|
|
219
|
-
}
|
|
220
|
-
this._setupFields(field)
|
|
247
|
+
})
|
|
248
|
+
this._setupFields(field, true)
|
|
221
249
|
|
|
222
250
|
// Fields can be a subdocument, e.g. user.pet = { name: {}, ..}
|
|
223
251
|
} else if (util.isSubdocument(field)) {
|
|
224
|
-
let objectDefault = this.manager.opts.defaultObjects? () => ({}) : undefined
|
|
225
|
-
let nullObject = this.manager.opts.nullObjects
|
|
226
252
|
let index2dsphere = util.isSubdocument2dsphere(field)
|
|
227
253
|
field.schema = field.schema || {}
|
|
228
254
|
if (index2dsphere) {
|
|
229
255
|
field.schema.index = index2dsphere
|
|
230
256
|
delete field.index
|
|
231
257
|
}
|
|
232
|
-
field
|
|
258
|
+
this._removeInvalidRules(field)
|
|
259
|
+
field.schema = util.removeUndefined({
|
|
233
260
|
type: 'object',
|
|
234
261
|
isObject: true,
|
|
235
|
-
|
|
236
|
-
|
|
262
|
+
isSchema: true,
|
|
263
|
+
isType: 'isObject',
|
|
264
|
+
default: this.manager.opts.defaultObjects? () => ({}) : undefined,
|
|
265
|
+
nullObject: this.manager.opts.nullObjects,
|
|
237
266
|
...field.schema,
|
|
238
|
-
}
|
|
239
|
-
this._setupFields(field)
|
|
267
|
+
})
|
|
268
|
+
this._setupFields(field, true)
|
|
240
269
|
}
|
|
241
270
|
}, this)
|
|
242
271
|
},
|
|
243
272
|
|
|
273
|
+
Model.prototype._removeInvalidRules = function(field) {
|
|
274
|
+
/**
|
|
275
|
+
* Remove invalid rules on a field object
|
|
276
|
+
* @param {object} field
|
|
277
|
+
* @return {object} field
|
|
278
|
+
**/
|
|
279
|
+
for (let ruleName in (field||{}).schema) {
|
|
280
|
+
const ruleFn = this.rules[ruleName] || rules[ruleName]
|
|
281
|
+
// Rule doesn't exist
|
|
282
|
+
if (!ruleFn && this._ignoredRules.indexOf(ruleName) == -1) {
|
|
283
|
+
// console.log(field.schema)
|
|
284
|
+
this.error(`No rule "${ruleName}" exists for model "${this.name}", ignoring rule.`)
|
|
285
|
+
delete field.schema[ruleName]
|
|
286
|
+
}
|
|
287
|
+
// Reserved rule
|
|
288
|
+
if (this.rules[ruleName] && this._ignoredRules.indexOf(ruleName) != -1) {
|
|
289
|
+
this.error(`The rule "${ruleName}" for the model "${this.name}" is a reserved keyword, ignoring custom rule function.`)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return field
|
|
293
|
+
},
|
|
294
|
+
|
|
244
295
|
Model.prototype._setupIndexes = async function(fields, opts={}) {
|
|
245
296
|
/**
|
|
246
297
|
* Creates indexes for the model (multikey, and sub-document supported)
|
|
247
298
|
* Note: the collection be created beforehand???
|
|
248
299
|
* Note: only one text index per model(collection) is allowed due to mongodb limitations
|
|
249
|
-
* @param {object} <fields>
|
|
300
|
+
* @param {object} <fields> - processed or unprocessed fields, e.g. {schema: {name: {index}}} or {name: {index}}
|
|
250
301
|
* @return Promise( {array} indexes ensured ) || error
|
|
251
302
|
*
|
|
252
303
|
* MongoDB index structures = [
|
|
@@ -267,8 +318,16 @@ Model.prototype._setupIndexes = async function(fields, opts={}) {
|
|
|
267
318
|
throw new Error(`Skipping createIndex on the '${model.name||''}' model, no mongodb connection found.`)
|
|
268
319
|
}
|
|
269
320
|
|
|
321
|
+
// Process custom 'unprocessed' fields
|
|
322
|
+
if (fields && !(fields[Object.keys(fields)[0]].schema||{}).isSchema) {
|
|
323
|
+
fields = util.deepCopy(fields)
|
|
324
|
+
this._setupFields(fields)
|
|
325
|
+
// console.dir(fields, { depth: null })
|
|
326
|
+
}
|
|
327
|
+
|
|
270
328
|
// Find all indexes
|
|
271
329
|
recurseFields(fields || model.fields, '')
|
|
330
|
+
|
|
272
331
|
// console.log(2, indexes, fields)
|
|
273
332
|
if (hasTextIndex) indexes.push(textIndex)
|
|
274
333
|
if (opts.dryRun) return indexes || []
|
|
@@ -313,14 +372,19 @@ Model.prototype._setupIndexes = async function(fields, opts={}) {
|
|
|
313
372
|
model.info('db index(s) created for ' + model.name)
|
|
314
373
|
return indexes
|
|
315
374
|
|
|
316
|
-
function recurseFields(
|
|
317
|
-
|
|
318
|
-
|
|
375
|
+
function recurseFields(schemaFields, parentPath) {
|
|
376
|
+
/**
|
|
377
|
+
* Recursively find fields with an index property
|
|
378
|
+
* @param {object} schemaFields
|
|
379
|
+
*/
|
|
380
|
+
util.forEach(schemaFields, (field, fieldName) => {
|
|
381
|
+
const index = (field.schema||{}).index
|
|
382
|
+
if (fieldName == 'schema') return
|
|
319
383
|
if (index) {
|
|
320
384
|
let options = util.isObject(index)? util.omit(index, ['type']) : {}
|
|
321
385
|
let type = util.isObject(index)? index.type : index
|
|
322
|
-
let path =
|
|
323
|
-
let path2 = path.replace(/(^|\.)[0-9]+(\.|$)/g, '$2') // no numirical keys, e.g. pets.1.
|
|
386
|
+
let path = parentPath + fieldName
|
|
387
|
+
let path2 = path.replace(/(^|\.)[0-9]+(\.|$)/g, '$2') // no numirical keys, e.g. pets.1.fieldName
|
|
324
388
|
if (type === true) type = 1
|
|
325
389
|
if (type == 'text') {
|
|
326
390
|
hasTextIndex = textIndex.key[path2] = 'text'
|
|
@@ -331,10 +395,11 @@ Model.prototype._setupIndexes = async function(fields, opts={}) {
|
|
|
331
395
|
indexes.push({ name: `${path2}_1`, key: { [path2]: 1 }, unique: true, ...options })
|
|
332
396
|
}
|
|
333
397
|
}
|
|
334
|
-
if (
|
|
335
|
-
recurseFields(field, parentPath +
|
|
336
|
-
|
|
337
|
-
|
|
398
|
+
if (field.schema.isObject) {
|
|
399
|
+
recurseFields(field, parentPath + fieldName + '.')
|
|
400
|
+
|
|
401
|
+
} else if (field.schema.isArray) {
|
|
402
|
+
recurseFields(field, parentPath + fieldName + '.')
|
|
338
403
|
}
|
|
339
404
|
})
|
|
340
405
|
}
|
package/lib/util.js
CHANGED
|
@@ -35,8 +35,7 @@ module.exports = {
|
|
|
35
35
|
let obj2 = Array.isArray(obj)? [] : {}
|
|
36
36
|
for (let key in obj) {
|
|
37
37
|
let v = obj[key]
|
|
38
|
-
|
|
39
|
-
else obj2[key] = (typeof v === 'object')? this.deepCopy(v) : v
|
|
38
|
+
obj2[key] = (typeof v === 'object' && !this.isIdFast(v))? this.deepCopy(v) : v
|
|
40
39
|
}
|
|
41
40
|
return obj2
|
|
42
41
|
},
|
|
@@ -114,6 +113,17 @@ module.exports = {
|
|
|
114
113
|
else return false
|
|
115
114
|
},
|
|
116
115
|
|
|
116
|
+
isIdFast: function(value) {
|
|
117
|
+
// Check quickly if the value is an ObjectId. We can use db.isId() but this may be slower
|
|
118
|
+
// console.log(isId('66333b1b3343d7e3b200005b')) = true
|
|
119
|
+
// console.log(isId(db.id())) = true
|
|
120
|
+
// console.log(isId(null)) = undefined
|
|
121
|
+
// console.log(isId('qwefqwefqwef')) = undefined
|
|
122
|
+
// console.log(isId({})) = undefined
|
|
123
|
+
// console.log(isId(['66333b1b3343d7e3b200005b'])) = undefined
|
|
124
|
+
if ((value||'').toString()?.match(/^[0-9a-fA-F]{24}$/) && !this.isArray(value)) return true
|
|
125
|
+
},
|
|
126
|
+
|
|
117
127
|
isNumber: function(value) {
|
|
118
128
|
return !isNaN(parseFloat(value)) && isFinite(value)
|
|
119
129
|
},
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "monastery",
|
|
3
3
|
"description": "⛪ A simple, straightforward MongoDB ODM",
|
|
4
4
|
"author": "Ricky Boyce",
|
|
5
|
-
"version": "3.0
|
|
5
|
+
"version": "3.1.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/plugins/images/index.js
CHANGED
|
@@ -482,27 +482,27 @@ let plugin = module.exports = {
|
|
|
482
482
|
})).then(() => validFiles)
|
|
483
483
|
},
|
|
484
484
|
|
|
485
|
-
_findAndTransformImageFields: function(
|
|
485
|
+
_findAndTransformImageFields: function(unprocessedFields, path) {
|
|
486
486
|
/**
|
|
487
|
-
* Returns a list of valid image
|
|
488
|
-
* @param {object|array} fields
|
|
487
|
+
* Returns a list of valid image field schemas
|
|
488
|
+
* @param {object|array} unprocessedFields - fields not yet setup
|
|
489
489
|
* @param {string} path
|
|
490
490
|
* @return [{}, ...]
|
|
491
491
|
* @this plugin
|
|
492
492
|
*/
|
|
493
493
|
let list = []
|
|
494
494
|
let that = this
|
|
495
|
-
util.forEach(
|
|
495
|
+
util.forEach(unprocessedFields, (field, fieldName) => {
|
|
496
496
|
let path2 = `${path}.${fieldName}`.replace(/^\./, '')
|
|
497
|
-
|
|
497
|
+
if (fieldName == 'schema') return
|
|
498
498
|
|
|
499
499
|
// Subdocument field
|
|
500
|
-
if (util.isSubdocument(field)) {
|
|
500
|
+
if (util.isSubdocument(field)) {
|
|
501
501
|
// log(`Recurse 1: ${path2}`)
|
|
502
502
|
list = list.concat(plugin._findAndTransformImageFields(field, path2))
|
|
503
503
|
|
|
504
504
|
// Array field
|
|
505
|
-
} else if (util.isArray(field)) {
|
|
505
|
+
} else if (util.isArray(field)) {
|
|
506
506
|
// log(`Recurse 2: ${path2}`)
|
|
507
507
|
list = list.concat(plugin._findAndTransformImageFields(field, path2))
|
|
508
508
|
|
|
@@ -532,15 +532,15 @@ let plugin = module.exports = {
|
|
|
532
532
|
params: field.params ? util.deepCopy(field.params) : undefined,
|
|
533
533
|
})
|
|
534
534
|
// Convert image field to subdocument
|
|
535
|
-
|
|
535
|
+
unprocessedFields[fieldName] = {
|
|
536
536
|
bucket: { type: 'string' },
|
|
537
537
|
date: { type: 'number' },
|
|
538
538
|
filename: { type: 'string' },
|
|
539
539
|
filesize: { type: 'number' },
|
|
540
540
|
metadata: { type: 'any' },
|
|
541
541
|
path: { type: 'string' },
|
|
542
|
-
schema: { image: true, nullObject: true, isImageObject: true },
|
|
543
542
|
uid: { type: 'string' },
|
|
543
|
+
schema: { image: true, isImageObject: true, nullObject: true },
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
})
|
package/test/blacklisting.js
CHANGED
|
@@ -328,7 +328,8 @@ test('find project population', async () => {
|
|
|
328
328
|
expect(find3).toEqual(customProject)
|
|
329
329
|
})
|
|
330
330
|
|
|
331
|
-
test('insert blacklisting
|
|
331
|
+
test('insert blacklisting validate', async () => {
|
|
332
|
+
// todo: isolated model._pathBlacklisted test
|
|
332
333
|
let user = db.model('user', {
|
|
333
334
|
fields: {
|
|
334
335
|
list: [{ type: 'number' }],
|
package/test/crud.js
CHANGED
|
@@ -571,6 +571,68 @@ test('update mixing formData', async() => {
|
|
|
571
571
|
})
|
|
572
572
|
})
|
|
573
573
|
|
|
574
|
+
test('update large document', async () => {
|
|
575
|
+
// todo: sereach util.deepCopy
|
|
576
|
+
// todo: check castIds and any other recursive functions
|
|
577
|
+
// todo: move default fields to before validate
|
|
578
|
+
db.model('a', { fields: {} })
|
|
579
|
+
db.model('b', { fields: {} })
|
|
580
|
+
db.model('c', { fields: {} })
|
|
581
|
+
db.model('d', { fields: {} })
|
|
582
|
+
db.model('e', { fields: {} })
|
|
583
|
+
try {
|
|
584
|
+
var large = db.model('large', require('../resources/fixtures/large-definition.js'))
|
|
585
|
+
var largePayload = require('../resources/fixtures/large-payload.json')
|
|
586
|
+
} catch (e) {
|
|
587
|
+
// ignore publicly for now
|
|
588
|
+
return
|
|
589
|
+
}
|
|
590
|
+
// Insert
|
|
591
|
+
let inserted = await large._insert({})
|
|
592
|
+
// Update
|
|
593
|
+
// console.time('update large document')
|
|
594
|
+
let update = await large.update({
|
|
595
|
+
query: inserted._id,
|
|
596
|
+
data: largePayload,
|
|
597
|
+
})
|
|
598
|
+
// console.timeEnd('update large document')
|
|
599
|
+
// Check
|
|
600
|
+
await expect(update).toEqual(removePrunedProperties(largePayload))
|
|
601
|
+
// Find
|
|
602
|
+
// console.time('find large document')
|
|
603
|
+
// await large.findOne({
|
|
604
|
+
// query: inserted._id,
|
|
605
|
+
// })
|
|
606
|
+
// console.timeEnd('find large document')
|
|
607
|
+
|
|
608
|
+
function removePrunedProperties(entity) {
|
|
609
|
+
for (let entitiesKey of [
|
|
610
|
+
'components', 'connections', 'bridges', 'openings', 'spaces', 'elements', 'elementTypes',
|
|
611
|
+
'categories', 'typologies',
|
|
612
|
+
]) {
|
|
613
|
+
if (entity[entitiesKey]) {
|
|
614
|
+
for (let i=0, l=entity[entitiesKey].length; i<l; i++) {
|
|
615
|
+
entity[entitiesKey][i] = removePrunedProperties(entity[entitiesKey][i])
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// remove actually keys
|
|
620
|
+
if (entity.metrics) {
|
|
621
|
+
for (let key in entity.metrics) {
|
|
622
|
+
delete entity.metrics[key].actually
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (entity.code?.match(/^(ELEM|CAT)/)) {
|
|
626
|
+
delete entity.name
|
|
627
|
+
}
|
|
628
|
+
// // convert _id to ObjectId
|
|
629
|
+
// if (entity._id) {
|
|
630
|
+
// entity._id = db.id(entity._id)
|
|
631
|
+
// }
|
|
632
|
+
return entity
|
|
633
|
+
}
|
|
634
|
+
})
|
|
635
|
+
|
|
574
636
|
test('findOneAndUpdate general', async () => {
|
|
575
637
|
// todo: test all findOneAndUpdate options (e.g. array population)
|
|
576
638
|
// todo: test find & update hooks
|
package/test/manager.js
CHANGED
|
@@ -8,7 +8,7 @@ test('manager > basics', async () => {
|
|
|
8
8
|
// Basic find command
|
|
9
9
|
expect(await manager.db.collection('non-collection').findOne({})).toEqual(null)
|
|
10
10
|
// Raw MongoDB ping command
|
|
11
|
-
expect(await manager.command({ ping: 1 })).
|
|
11
|
+
expect(await manager.command({ ping: 1 })).toMatchObject({ ok: 1 }) // cluster connections return extra fields
|
|
12
12
|
manager.close()
|
|
13
13
|
})
|
|
14
14
|
|